From 714fca679d4360d0e7fc530b11315df80997c3ba Mon Sep 17 00:00:00 2001 From: alexander-sei Date: Fri, 12 Jun 2026 01:13:54 +0200 Subject: [PATCH 1/2] docs: fix factual errors, broken examples, and migrate to Hardhat 3 Full-docs review with every finding verified against the sei-chain source or upstream tool docs before fixing: - Funds-affecting example bugs: staking undelegate/redelegate amounts are 6-decimal usei (verified in staking.go), bank sendNative takes wei (verified via HandlePaymentUseiWei), distribution uses withdrawDelegationRewards per abi.json - P256: correct half-curve-order malleability constant; switch all examples to low-level staticcall since the precompile returns empty data on invalid signatures per RIP-7212, which reverts high-level calls - addr: associate examples now pass the full EIP-191 prefixed message (recovery hashes the raw message string, verified in addr.go) - Contradiction fixes: SIP-03 date (June 15, 2026), instant finality on Twin Turbo page, gas fee formula, node min-gas-price guidance, Ledger app guidance aligned with the EVM signing path - Hardhat 3 everywhere: deploy-verify, migrate-from-solana, migrate-from-other-evms, and precompile pages now use defineConfig, configVariable, encrypted keystore, and network.getOrCreate() (layerzero.mdx intentionally stays on LayerZero's HH2-based toolkit) - Broken commands/links: hardhat verify invocation per HH version, web3auth install, The Graph subgraph ID, statesync glob, validator chain IDs, tab-indented YAML, dead RPC link, jq field names, destructive "compact" command relabeled, fabricated sei_getStorageStats removed - Runtime errors in examples: wagmi setState-in-render, governance demo imports and bigint math, Pyth ABI signature, Pimlico dotenv, USDC explorer URL, ledger getAddresses return type, undefined client in association examples Co-Authored-By: Claude Fable 5 --- ai/mcp-server.mdx | 2 +- ai/x402.mdx | 50 ++++----- evm/ai-tooling/mcp-server.mdx | 2 +- evm/bridging/layerzero.mdx | 2 +- evm/building-a-frontend.mdx | 18 ++-- evm/evm-hardhat.mdx | 17 ++- evm/evm-parity/examples/deploy-verify.mdx | 53 +++++---- evm/indexer-providers/the-graph.mdx | 10 +- evm/migrate-from-other-evms.mdx | 25 ++--- evm/migrate-from-solana.mdx | 35 +++--- evm/oracles/api3.mdx | 11 +- evm/oracles/chainlink.mdx | 4 +- evm/oracles/pyth-network.mdx | 9 +- evm/precompiles/cosmwasm-precompiles/addr.mdx | 63 +++++------ evm/precompiles/cosmwasm-precompiles/bank.mdx | 102 +++++++++--------- .../cosmwasm-precompiles/cosmwasm.mdx | 8 +- .../cosmwasm-precompiles/example-usage.mdx | 6 +- evm/precompiles/example-usage.mdx | 14 +-- evm/precompiles/governance.mdx | 4 +- evm/precompiles/json.mdx | 31 +++--- evm/precompiles/p256-precompile.mdx | 93 ++++++++++------ evm/precompiles/staking.mdx | 10 +- evm/python-quickstart.mdx | 2 - evm/sei-global-wallet.mdx | 2 +- evm/sei-js/create-sei.mdx | 12 ++- evm/sei-js/ledger.mdx | 4 +- evm/tracing/index.mdx | 10 +- evm/transactions-with-seid.mdx | 2 +- evm/usdc-on-sei.mdx | 2 +- evm/wallet-integrations/particle.mdx | 8 +- evm/wallet-integrations/pimlico.mdx | 2 - evm/wallet-integrations/thirdweb.mdx | 33 ++---- learn/accounts.mdx | 11 +- learn/dev-gas.mdx | 8 +- learn/hardware-wallets.mdx | 26 +++-- learn/seidb.mdx | 22 ---- learn/sip-03-migration.mdx | 6 +- learn/twin-turbo-consensus.mdx | 2 +- node/advanced-config-monitoring.mdx | 94 ++++++++-------- node/index.mdx | 7 +- node/node-operators.mdx | 4 +- node/seictl.mdx | 4 +- node/statesync.mdx | 2 +- node/troubleshooting.mdx | 4 +- node/validators.mdx | 10 +- 45 files changed, 459 insertions(+), 387 deletions(-) diff --git a/ai/mcp-server.mdx b/ai/mcp-server.mdx index 3556388..6e5a45a 100644 --- a/ai/mcp-server.mdx +++ b/ai/mcp-server.mdx @@ -273,7 +273,7 @@ Pre-configured prompts for common tasks:

Send Transaction

-

"Send 1 SEI to 0x742d35Cc6634C0532925a3b8D4C1C4e3153DC"

+

"Send 1 SEI to 0x742d35Cc6634C0532925a3b844De3e9Fe0b5BaDa"

→ Executes transfer and returns transaction hash

diff --git a/ai/x402.mdx b/ai/x402.mdx index 7c4efce..353ea49 100644 --- a/ai/x402.mdx +++ b/ai/x402.mdx @@ -52,15 +52,15 @@ These packages help streamline both the client-side (paying) and server-side (ch --- -# Axiom Kit Integration +## Axiom Kit Integration While you can build x402 integrations using standard tools and the libraries mentioned above, you can also optionally use Axiom Kit for a more agent-centric approach. This guide demonstrates an end-to-end x402 (HTTP 402 Payment Required) micropayment flow on the Sei testnet using AxiomKit. -## What is x402? +### What is x402? x402 is an open standard protocol for internet-native payments that enables users to send and receive payments globally in a simple, secure, and interoperable manner. The protocol leverages the HTTP 402 status code ("Payment Required") to facilitate blockchain-based micropayments directly through HTTP requests. -### Key Features of x402: +#### Key Features of x402: - **HTTP-Native**: Uses standard HTTP status codes and headers - **Blockchain Integration**: Supports multiple blockchain networks @@ -68,7 +68,7 @@ x402 is an open standard protocol for internet-native payments that enables user - **Interoperable**: Works across different payment schemes and networks - **Micropayment Support**: Designed for small, frequent transactions -## Protocol Overview +### Protocol Overview The x402 protocol follows a specific flow: @@ -78,22 +78,22 @@ The x402 protocol follows a specific flow: 4. **Payment Proof**: Client includes payment proof in subsequent request 5. **Resource Access**: Server verifies payment and grants access -## Axiom Integration +### Axiom Integration Axiom is a blockchain interaction framework that provides tools and libraries for building decentralized applications. In this implementation, Axiom integrates with x402 to enable seamless blockchain payments within Sei network applications. -### Axiom Components Used: +#### Axiom Components Used: - **@axiomkit/core**: Core framework for building blockchain agents - **@axiomkit/sei**: Sei blockchain integration - **AxiomSeiWallet**: Wallet management for Sei transactions - **Context and Actions**: Framework for building interactive blockchain agents -## Sei Blockchain Implementation +### Sei Blockchain Implementation This implementation uses the Sei testnet for x402 payments with the following configuration: -### Network Configuration +#### Network Configuration ```typescript export const X402_CONFIG = { @@ -107,11 +107,11 @@ export const X402_CONFIG = { }; ``` -## Technical Architecture +### Technical Architecture The x402 implementation consists of several key components: -### 1. Payment Challenge Generation +#### 1. Payment Challenge Generation ```typescript function generatePaymentChallenge() { @@ -142,7 +142,7 @@ function generatePaymentChallenge() { } ``` -### 2. Payment Verification +#### 2. Payment Verification ```typescript async function verifyPayment(paymentHeader: string) { @@ -163,7 +163,7 @@ async function verifyPayment(paymentHeader: string) { } ``` -### 3. Axiom Agent Integration +#### 3. Axiom Agent Integration The Axiom agent handles the complete x402 flow: @@ -201,16 +201,16 @@ action({ }); ``` -## Payment Flow +### Payment Flow -### 1. Initial Request +#### 1. Initial Request ``` Client → GET /api/weather Server → 402 Payment Required + Payment Challenge ``` -### 2. Payment Challenge Response +#### 2. Payment Challenge Response ```json { @@ -236,7 +236,7 @@ Server → 402 Payment Required + Payment Challenge } ``` -### 3. Payment Execution +#### 3. Payment Execution The Axiom agent executes a USDC transfer on Sei testnet: @@ -264,16 +264,16 @@ const hash = await seiWallet.walletClient.sendTransaction({ }); ``` -### 4. Payment Proof Submission +#### 4. Payment Proof Submission ``` Client → GET /api/weather + X-Payment Header (base64 encoded payment proof) Server → Verifies payment + Returns weather data ``` -## Code Examples +### Code Examples -### Complete Weather API Implementation +#### Complete Weather API Implementation ```typescript export async function GET(req: NextRequest) { @@ -309,7 +309,7 @@ export async function GET(req: NextRequest) { } ``` -### Axiom Agent Weather Action +#### Axiom Agent Weather Action ```typescript action({ @@ -392,29 +392,29 @@ Please try again or check your wallet balance for USDC tokens needed for the pay }); ``` -## Security Considerations +### Security Considerations -### Payment Verification +#### Payment Verification - **On-chain Verification**: All payments are verified against the Sei blockchain. - **Transaction Receipt Validation**: Ensures transaction success and proper recipient. - **Payment Caching**: Prevents double-spending by caching verified payments. - **Reference Validation**: Unique payment references prevent replay attacks. -### Network Security +#### Network Security - **HTTPS Required**: All API communications use secure connections. - **Base64 Encoding**: Payment proofs are base64 encoded for safe transmission. - **Timeout Handling**: Payment challenges include timeout mechanisms. - **Error Handling**: Comprehensive error handling prevents information leakage. -### Wallet Security +#### Wallet Security - **Private Key Management**: Private keys are stored securely in environment variables. - **Transaction Signing**: All transactions are properly signed before submission. - **Balance Validation**: Sufficient balance checks before payment execution. -## Tutorials & Resources +### Tutorials & Resources - **[Sei-js X402 Repository](https://github.com/sei-protocol/sei-x402)**: Comprehensive guide on using the x402 protocol with the sei-js library, including package details and examples. - **[AxiomKit X402 Demo Repository](https://github.com/AxiomKit/axiomkit-showcase)**: The complete source code for the Axiom integration demo used in this guide, including installation and configuration instructions. diff --git a/evm/ai-tooling/mcp-server.mdx b/evm/ai-tooling/mcp-server.mdx index 3556388..6e5a45a 100644 --- a/evm/ai-tooling/mcp-server.mdx +++ b/evm/ai-tooling/mcp-server.mdx @@ -273,7 +273,7 @@ Pre-configured prompts for common tasks:

Send Transaction

-

"Send 1 SEI to 0x742d35Cc6634C0532925a3b8D4C1C4e3153DC"

+

"Send 1 SEI to 0x742d35Cc6634C0532925a3b844De3e9Fe0b5BaDa"

→ Executes transfer and returns transaction hash

diff --git a/evm/bridging/layerzero.mdx b/evm/bridging/layerzero.mdx index cf02843..659a125 100644 --- a/evm/bridging/layerzero.mdx +++ b/evm/bridging/layerzero.mdx @@ -369,7 +369,7 @@ pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { OFT } from "@layerzerolabs/oft-evm/contracts/OFT.sol"; -contract MyOFT is OFT, Ownable { +contract MyOFT is OFT { constructor(string memory name, string memory symbol, address endpoint, address owner) OFT(name, symbol, endpoint, owner) Ownable(owner) {} diff --git a/evm/building-a-frontend.mdx b/evm/building-a-frontend.mdx index 6886ee4..24ada90 100644 --- a/evm/building-a-frontend.mdx +++ b/evm/building-a-frontend.mdx @@ -581,7 +581,7 @@ export const config = createConfig({ Now implement the Wagmi interface: ```tsx title="src/components/WagmiInterface.tsx" -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { useAccount, useConnect, useReadContract, useWriteContract, useWaitForTransactionReceipt } from 'wagmi'; import { injected } from 'wagmi/connectors'; import { parseEther, formatEther } from 'viem'; @@ -654,11 +654,13 @@ export function WagmiInterface() { }; // Handle successful transfer - if (isConfirmed) { - refetchBalance(); - setRecipientAddress(''); - setAmount(''); - } + useEffect(() => { + if (isConfirmed) { + refetchBalance(); + setRecipientAddress(''); + setAmount(''); + } + }, [isConfirmed, refetchBalance]); return (
@@ -792,10 +794,10 @@ To build your application, run: npm run build ``` -Then deploy via: +Then preview the production build locally: ```bash npm run preview ``` -This will start a local production server, at `http://localhost:4173`. You can toggle between the different library implementations to see how each one works. +This serves the built app at `http://localhost:4173` so you can verify it before deploying. To go live, deploy the contents of the `dist` directory to any static hosting provider. diff --git a/evm/evm-hardhat.mdx b/evm/evm-hardhat.mdx index b82694a..6021dd5 100644 --- a/evm/evm-hardhat.mdx +++ b/evm/evm-hardhat.mdx @@ -125,11 +125,7 @@ You'll be prompted to paste the private key of the account you deploy from; Hard ## Using OpenZeppelin Contracts -OpenZeppelin provides a library of secure, tested smart contract components that you can use to build your applications. First, let's install the OpenZeppelin Contracts package: - -```bash -npm install @openzeppelin/contracts -``` +OpenZeppelin provides a library of secure, tested smart contract components that you can use to build your applications. The `@openzeppelin/contracts` package was already installed during setup, so you're ready to import its contracts directly. ## Creating and Deploying an ERC20 Token, ERC721 NFT or an Upgradeable UUPS Token @@ -303,9 +299,12 @@ console.log('SeiNFT deployed to:', await seiNFT.getAddress()); // Mint an example NFT console.log('Minting an example NFT...'); -const mintTx = await seiNFT.safeMint(deployer.address, '1.json'); -await mintTx.wait(); -console.log('NFT minted with ID: 1'); +const mintTx = await seiNFT.safeMint(deployer.address, '0.json'); +const receipt = await mintTx.wait(); +// _nextTokenId starts at 0, so the first minted token has ID 0. +// Read the actual ID from the Transfer event: +const transferEvent = receipt.logs.map((log) => seiNFT.interface.parseLog(log)).find((parsed) => parsed?.name === 'Transfer'); +console.log('NFT minted with ID:', transferEvent.args.tokenId.toString()); ``` Deploy the NFT contract to the Sei testnet: @@ -582,7 +581,7 @@ npx hardhat test Once you've tested your contracts, you can deploy them to the Sei testnet or mainnet. To deploy, you'll need: 1. SEI tokens in your wallet for gas fees -2. Your private key in the `.env` file +2. Your private key stored in the encrypted keystore (`npx hardhat keystore set SEI_PRIVATE_KEY`) — or exported as a `SEI_PRIVATE_KEY` environment variable in CI Deploy to the testnet: diff --git a/evm/evm-parity/examples/deploy-verify.mdx b/evm/evm-parity/examples/deploy-verify.mdx index 4b8e5d6..728484a 100644 --- a/evm/evm-parity/examples/deploy-verify.mdx +++ b/evm/evm-parity/examples/deploy-verify.mdx @@ -68,33 +68,39 @@ forge create src/MyContract.sol:MyContract \ ## Deploying with Hardhat -Add Sei networks to your `hardhat.config.ts`: +Add Sei networks to your `hardhat.config.ts` (Hardhat 3): ```ts -import { HardhatUserConfig } from 'hardhat/config'; +import { defineConfig, configVariable } from 'hardhat/config'; -const config: HardhatUserConfig = { +export default defineConfig({ networks: { sei: { - url: 'https://evm-rpc.sei-apis.com', + type: 'http', chainId: 1329, - accounts: [process.env.PRIVATE_KEY!], + url: 'https://evm-rpc.sei-apis.com', + accounts: [configVariable('SEI_PRIVATE_KEY')], }, seiTestnet: { - url: 'https://evm-rpc-testnet.sei-apis.com', + type: 'http', chainId: 1328, - accounts: [process.env.PRIVATE_KEY!], + url: 'https://evm-rpc-testnet.sei-apis.com', + accounts: [configVariable('SEI_PRIVATE_KEY')], }, }, -}; +}); +``` -export default config; +Store your private key in Hardhat's encrypted keystore so it never lives in a plaintext file: + +```bash +npx hardhat keystore set SEI_PRIVATE_KEY ``` -Then deploy: +Then deploy with Hardhat Ignition: ```bash -npx hardhat run scripts/deploy.ts --network sei +npx hardhat ignition deploy ignition/modules/MyContract.ts --network sei ``` ## Verifying Contracts @@ -134,28 +140,33 @@ Install the verify plugin: npm install --save-dev @nomicfoundation/hardhat-verify ``` -Add it to `hardhat.config.ts` — no additional Sourcify config block is needed: +Add it to the `plugins` array in `hardhat.config.ts` — Sourcify verification is enabled by default in `hardhat-verify` and needs no API key: ```ts -import '@nomicfoundation/hardhat-verify'; +import { defineConfig, configVariable } from 'hardhat/config'; +import hardhatVerify from '@nomicfoundation/hardhat-verify'; -const config: HardhatUserConfig = { +export default defineConfig({ networks: { sei: { - url: 'https://evm-rpc.sei-apis.com', + type: 'http', chainId: 1329, - accounts: [process.env.PRIVATE_KEY!], + url: 'https://evm-rpc.sei-apis.com', + accounts: [configVariable('SEI_PRIVATE_KEY')], }, seiTestnet: { - url: 'https://evm-rpc-testnet.sei-apis.com', + type: 'http', chainId: 1328, - accounts: [process.env.PRIVATE_KEY!], + url: 'https://evm-rpc-testnet.sei-apis.com', + accounts: [configVariable('SEI_PRIVATE_KEY')], }, }, -}; + + plugins: [hardhatVerify], +}); ``` -Verify a deployed contract: +Verify a deployed contract on Sourcify: ```bash npx hardhat verify sourcify --network sei 0xDeployedContractAddress @@ -167,6 +178,8 @@ If your contract has constructor arguments: npx hardhat verify sourcify --network sei 0xDeployedContractAddress "arg1" "arg2" ``` +Running `npx hardhat verify` without the `sourcify` subtask attempts verification on all enabled providers (Etherscan, Blockscout, and Sourcify) simultaneously. + ## Network Reference | Network | Chain ID | RPC | Explorer | diff --git a/evm/indexer-providers/the-graph.mdx b/evm/indexer-providers/the-graph.mdx index 13f997b..72f8102 100644 --- a/evm/indexer-providers/the-graph.mdx +++ b/evm/indexer-providers/the-graph.mdx @@ -113,9 +113,11 @@ Before you can query your subgraph, Indexers need to begin serving queries on it. In order to streamline this process, you can curate your own subgraph using GRT. -When publishing, you'll see the option to curate your subgraph. As of May 2024, -it is recommended that you curate your own subgraph with at least 3,000 GRT to -ensure that it is indexed and available for querying as soon as possible. +When publishing, you'll see the option to curate your subgraph. Signaling GRT +on your own subgraph helps ensure that it is indexed and available for querying +as soon as possible. See The Graph's +[curating documentation](https://thegraph.com/docs/en/resources/roles/curating/) +for current guidance on how much GRT to signal. ![Publish screen](/assets/ecosystem/resources/the-graph/publish_screen.png) *Publish Screen* @@ -138,7 +140,7 @@ by Messari: *Query URL* The query URL for this subgraph is: -`https://gateway-arbitrum.network.thegraph.com/api/`**[api-key]**`/subgraphs/id/HdVdERFUe8h61vm2fDyycgxjsde5PbB832NHgJfZNqK` +`https://gateway-arbitrum.network.thegraph.com/api/`**[api-key]**`/subgraphs/id/HdVdERFUe8h61vm2fDyycHgxjsde5PbB832NHgJfZNqK` Now, you simply need to fill in your own API Key to start sending GraphQL queries to this endpoint. diff --git a/evm/migrate-from-other-evms.mdx b/evm/migrate-from-other-evms.mdx index 58e687b..d9fec35 100644 --- a/evm/migrate-from-other-evms.mdx +++ b/evm/migrate-from-other-evms.mdx @@ -301,29 +301,30 @@ Revisit the [Divergence from Ethereum](/evm/differences-with-ethereum) doc and c **Hardhat Configuration:** ```ts title="hardhat.config.ts" -import '@nomicfoundation/hardhat-verify'; +import { defineConfig, configVariable } from 'hardhat/config'; +import hardhatToolboxMochaEthers from '@nomicfoundation/hardhat-toolbox-mocha-ethers'; -const config = { +export default defineConfig({ networks: { seiMainnet: { - url: process.env.SEI_MAINNET_RPC ?? 'https://evm-rpc.sei-apis.com', + type: 'http', chainId: 1329, - accounts: process.env.DEPLOYER_KEYS?.split(',') ?? [] + url: 'https://evm-rpc.sei-apis.com', + accounts: [configVariable('SEI_PRIVATE_KEY')] }, seiTestnet: { - url: process.env.SEI_TESTNET_RPC ?? 'https://evm-rpc-testnet.sei-apis.com', + type: 'http', chainId: 1328, - accounts: process.env.DEPLOYER_KEYS?.split(',') ?? [] + url: 'https://evm-rpc-testnet.sei-apis.com', + accounts: [configVariable('SEI_PRIVATE_KEY')] } }, - sourcify: { - enabled: true - } -}; - -export default config; + plugins: [hardhatToolboxMochaEthers] +}); ``` +Store your deployer key in Hardhat's encrypted keystore with `npx hardhat keystore set SEI_PRIVATE_KEY`. Contract verification via Sourcify is enabled by default in Hardhat 3's `hardhat-verify` (bundled with the toolbox). + **Foundry Configuration:** ```toml title="foundry.toml" diff --git a/evm/migrate-from-solana.mdx b/evm/migrate-from-solana.mdx index ead0b80..6584bb8 100644 --- a/evm/migrate-from-solana.mdx +++ b/evm/migrate-from-solana.mdx @@ -346,7 +346,7 @@ While Sei handles parallelization automatically, you can still optimize your con # https://nodejs.org/ # Install Hardhat (recommended for Solana devs transitioning) -npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox +npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox-mocha-ethers # Or install Foundry (Rust-based, may feel more familiar) curl -L https://foundry.paradigm.xyz | bash @@ -359,29 +359,33 @@ foundryup ```ts title="hardhat.config.ts" -import { HardhatUserConfig } from 'hardhat/config'; -import '@nomicfoundation/hardhat-toolbox'; +import { defineConfig, configVariable } from 'hardhat/config'; +import hardhatToolboxMochaEthers from '@nomicfoundation/hardhat-toolbox-mocha-ethers'; -const config: HardhatUserConfig = { +export default defineConfig({ solidity: '0.8.22', networks: { seiMainnet: { - url: 'https://evm-rpc.sei-apis.com', + type: 'http', chainId: 1329, - accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + url: 'https://evm-rpc.sei-apis.com', + accounts: [configVariable('SEI_PRIVATE_KEY')] }, seiTestnet: { - url: 'https://evm-rpc-testnet.sei-apis.com', + type: 'http', chainId: 1328, - accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + url: 'https://evm-rpc-testnet.sei-apis.com', + accounts: [configVariable('SEI_PRIVATE_KEY')] } }, - sourcify: { - enabled: true - } -}; + plugins: [hardhatToolboxMochaEthers] +}); +``` + +Store your deployer key in Hardhat's encrypted keystore (no plaintext `.env` file needed): -export default config; +```bash +npx hardhat keystore set SEI_PRIVATE_KEY ``` @@ -692,7 +696,10 @@ describe('counter', () => { ```ts import { expect } from 'chai'; -import { ethers } from 'hardhat'; +import { network } from 'hardhat'; + +// Hardhat 3 exposes ethers through a network connection rather than a global import +const { ethers } = await network.getOrCreate(); describe('Counter', function () { it('Initializes', async function () { diff --git a/evm/oracles/api3.mdx b/evm/oracles/api3.mdx index 72d84f7..72de170 100644 --- a/evm/oracles/api3.mdx +++ b/evm/oracles/api3.mdx @@ -82,7 +82,7 @@ contract DataFeedReaderExample is Ownable { } function readDataFeed() - external + public view returns (int224 value, uint256 timestamp) { @@ -163,7 +163,7 @@ console.log('Last Updated:', new Date(result.timestamp * 1000)); **Expected Output Format:** -- `value`: SEI price in USD (with 18 decimals, e.g., `3000000000000000` = $0.3) +- `value`: SEI price in USD (with 18 decimals, e.g., `300000000000000000` = $0.3) - `timestamp`: Unix timestamp of the last price update ### 3. Integration Examples @@ -171,8 +171,9 @@ console.log('Last Updated:', new Date(result.timestamp * 1000)); **Basic Price Display:** ```solidity -function getSEIPriceFormatted() external view returns (string memory) { - (int224 value, uint256 timestamp) = readDataFeed(); - return string(abi.encodePacked("$", uint224(value) / 1e18)); +function getSEIPrice() external view returns (int224 value) { + (value, ) = readDataFeed(); + // `value` has 18 decimals, e.g. 300000000000000000 = $0.30. + // Divide by 1e18 off-chain (or scale on-chain) to get the dollar price. } ``` diff --git a/evm/oracles/chainlink.mdx b/evm/oracles/chainlink.mdx index 8eebf2c..ecab335 100644 --- a/evm/oracles/chainlink.mdx +++ b/evm/oracles/chainlink.mdx @@ -50,7 +50,7 @@ Before starting this tutorial, ensure you have: - **API credentials**: Contact Chainlink to get access to Data Streams on Sei Mainnet/testnet - **API Key** and **User Secret** for testnet access - **Feed IDs**: We'll use [SEI/USDT](https://docs.chain.link/data-streams/crypto-streams?page=1&testnetPage=1&testnetSearch=SEI#testnet-crypto-streams) feed ID `0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117` -- **VerifierProxy contract address** on [Sei mainnet/testnet](https://docs.chain.link/data-streams/crypto-streams?page=1&testnetPage=1#overview): [`0x60fAa7faC949aF392DFc858F5d97E3EEfa07E9EB`](https://seiscan.io/address/0x60fAa7faC949aF392DFc858F5d97E3EEfa07E9EB) +- **VerifierProxy contract address**: `0x60fAa7faC949aF392DFc858F5d97E3EEfa07E9EB` — the same address is used on [Sei mainnet](https://seiscan.io/address/0x60fAa7faC949aF392DFc858F5d97E3EEfa07E9EB) and [Sei testnet](https://testnet.seiscan.io/address/0x60fAa7faC949aF392DFc858F5d97E3EEfa07E9EB). See Chainlink's [Stream Addresses](https://docs.chain.link/data-streams/crypto-streams) page for the current list. ## Step-by-Step Tutorial @@ -409,7 +409,7 @@ npx tsx verifyOnChain.ts **Expected Output:** ```bash -npx tsx singleStream.ts +npx tsx verifyOnChain.ts [DataStreams] Data Streams client initialized Fetching latest report... [DataStreams] Request successful: GET https://api.testnet-dataengine.chain.link/api/v1/reports/latest?feedID=0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117 - 200 diff --git a/evm/oracles/pyth-network.mdx b/evm/oracles/pyth-network.mdx index 39d1d56..62b25d3 100644 --- a/evm/oracles/pyth-network.mdx +++ b/evm/oracles/pyth-network.mdx @@ -213,7 +213,7 @@ async function updateAndQuerySeiPrice(contractAddress, privateKey, rpcUrl = 'htt const wallet = new ethers.Wallet(privateKey, provider); // Contract ABI - const contractAbi = ['function getSeiPrice(bytes[] calldata priceUpdateData) external payable returns (tuple(int64 price, uint64 conf, int32 expo, uint256 publishTime))', 'function getLatestSeiPrice(uint256 time) external view returns (tuple(int64 price, uint64 conf, int32 expo, uint256 publishTime))', 'function isPriceFresh(uint256 maxAge) external view returns (bool)', 'function getUpdateFee(bytes[] calldata priceUpdateData) external view returns (uint256)', 'receive() external payable']; + const contractAbi = ['function getSeiPrice(bytes[] calldata priceUpdateData) external payable returns (tuple(int64 price, uint64 conf, int32 expo, uint256 publishTime))', 'function getLatestSeiPrice() external view returns (tuple(int64 price, uint64 conf, int32 expo, uint256 publishTime))', 'function isPriceFresh(uint256 maxAge) external view returns (bool)', 'function getUpdateFee(bytes[] calldata priceUpdateData) external view returns (uint256)', 'receive() external payable']; const contract = new ethers.Contract(contractAddress, contractAbi, wallet); const seiPriceId = '0x53614f1cb0c031d4af66c04cb9c756234adad0e1cee85303795091499a4084eb'; @@ -241,9 +241,8 @@ async function updateAndQuerySeiPrice(contractAddress, privateKey, rpcUrl = 'htt const receipt = await tx.wait(); console.log(`✅ Transaction confirmed in block: ${receipt.blockNumber}`); - const time = 120; // 120 seconds freshness - - const onChainPrice = await contract.getLatestSeiPrice(time); + // Reads the last known price (the contract enforces a 60-second freshness window) + const onChainPrice = await contract.getLatestSeiPrice(); console.log('Onchain Query Raw', onChainPrice); @@ -421,6 +420,6 @@ Pyth price data includes several important fields: ## Resources - [Pyth Network Documentation](https://docs.pyth.network/) -- [Pyth Price Feed IDs](https://docs.pyth.network/price-feeds/core/contract-addresses/evm) +- [Pyth Price Feed IDs](https://docs.pyth.network/price-feeds/core/price-feeds/price-feed-ids?search=sei&pageSize=50) - [Pyth EVM SDK GitHub](https://github.com/pyth-network/pyth-crosschain) - [Hermes Price Service API](https://api-reference.pyth.network/price-feeds/evm) diff --git a/evm/precompiles/cosmwasm-precompiles/addr.mdx b/evm/precompiles/cosmwasm-precompiles/addr.mdx index 1b296ff..f93d52f 100644 --- a/evm/precompiles/cosmwasm-precompiles/addr.mdx +++ b/evm/precompiles/cosmwasm-precompiles/addr.mdx @@ -102,11 +102,11 @@ Before getting started, ensure you have: Install the required packages for interacting with Sei precompiles: ```bash -# Intitate a hardhat project -npx hardhat init +# Initiate a Hardhat 3 project (choose "Hardhat 3" and the Mocha + Ethers.js TypeScript setup) +npx hardhat --init # Install ethers.js for smart contract interactions -npm install ethers dotenv +npm install ethers # Install Sei EVM bindings for precompile addresses and ABIs npm install @sei-js/precompiles@2.1.2 @@ -114,30 +114,30 @@ npm install @sei-js/precompiles@2.1.2 #### Setup Hardhat Environment -Create a `hardhat.config.js` file with the following content: +Create a `hardhat.config.ts` file with the following content: -```javascript -require('@nomicfoundation/hardhat-toolbox'); -require('dotenv').config(); +```typescript +import { defineConfig, configVariable } from 'hardhat/config'; +import hardhatToolboxMochaEthers from '@nomicfoundation/hardhat-toolbox-mocha-ethers'; -/** @type import('hardhat/config').HardhatUserConfig */ -module.exports = { +export default defineConfig({ solidity: '0.8.28', networks: { sei: { - url: 'https://evm-rpc.sei-apis.com', + type: 'http', chainId: 1329, - accounts: [process.env.PRIVATE_KEY] + url: 'https://evm-rpc.sei-apis.com', + accounts: [configVariable('PRIVATE_KEY')] } - } -}; + }, + plugins: [hardhatToolboxMochaEthers] +}); ``` -Create a `.env` file in the root directory with your private key and RPC URL: +Store your private key in Hardhat's encrypted keystore (no plaintext `.env` file needed): -```plaintext -PRIVATE_KEY=your_private_key_here -RPC_URL=https://evm-rpc.sei-apis.com +```bash +npx hardhat keystore set PRIVATE_KEY ``` #### Import Precompile Components @@ -262,9 +262,9 @@ try { const signature = await signer.signMessage(message); const sig = ethers.Signature.from(signature); - // Prepare the prefixed message (signMessage already applies this prefix) - const messageBytes = new TextEncoder().encode(message); - const customMessage = `\x19Ethereum Signed Message:\n${message}`; + // Reconstruct the EIP-191 prefixed message that signMessage() actually signed: + // "\x19Ethereum Signed Message:\n" + message length + message + const customMessage = `\x19Ethereum Signed Message:\n${message.length}${message}`; // Convert signature components to strings const v = (sig.v - 27).toString(); @@ -611,14 +611,14 @@ contract CrossChainAddressManager { Deploy the contract: ```javascript -const { ethers } = require('hardhat'); +import { network } from 'hardhat'; -const dotenv = require('dotenv'); -dotenv.config(); +// Hardhat 3 exposes ethers through a network connection rather than a global import +const { ethers } = await network.getOrCreate(); async function main() { - const provider = new ethers.JsonRpcProvider(process.env.RPC_URL); - const deployer = new ethers.Wallet(process.env.PRIVATE_KEY, provider); + // The signer comes from the network config (encrypted keystore) + const [deployer] = await ethers.getSigners(); console.log('Deploying contracts with the account:', deployer.address); const balance = await deployer.provider.getBalance(deployer.address); @@ -647,13 +647,16 @@ npx hardhat run scripts/deploy.js --network sei ``` ```javascript -const { ethers } = require('hardhat'); -const addressManagerAbi = require('./artifacts/contracts/CrossChainAddressManager.sol/CrossChainAddressManager.json'); +import { network } from 'hardhat'; +import addressManagerAbi from './artifacts/contracts/CrossChainAddressManager.sol/CrossChainAddressManager.json' with { type: 'json' }; + +// Hardhat 3 exposes ethers through a network connection rather than a global import +const { ethers } = await network.getOrCreate(); async function main() { - const provider = new ethers.JsonRpcProvider(process.env.RPC_URL); - const deployer = new ethers.Wallet(process.env.PRIVATE_KEY, provider); - console.log('Deploying contracts with the account:', deployer.address); + // The signer comes from the network config (encrypted keystore) + const [deployer] = await ethers.getSigners(); + console.log('Interacting with the account:', deployer.address); const balance = await deployer.provider.getBalance(deployer.address); console.log('Account balance:', ethers.formatEther(balance)); diff --git a/evm/precompiles/cosmwasm-precompiles/bank.mdx b/evm/precompiles/cosmwasm-precompiles/bank.mdx index ed885f4..9ec8bc5 100644 --- a/evm/precompiles/cosmwasm-precompiles/bank.mdx +++ b/evm/precompiles/cosmwasm-precompiles/bank.mdx @@ -131,11 +131,11 @@ Before getting started, ensure you have: Install the required packages for interacting with Sei precompiles: ```bash -# Instantiate a new hardhat project -npx hardhat init +# Instantiate a new Hardhat 3 project (choose "Hardhat 3" and the Mocha + Ethers.js TypeScript setup) +npx hardhat --init # Install ethers.js for smart contract interactions -npm install ethers dotenv +npm install ethers # Install Sei EVM bindings for precompile addresses and ABIs npm install @sei-js/precompiles@2.1.2 @@ -143,30 +143,30 @@ npm install @sei-js/precompiles@2.1.2 #### Setup Hardhat Environment -Create a `hardhat.config.js` file with the following content: +Create a `hardhat.config.ts` file with the following content: -```javascript -require('@nomicfoundation/hardhat-toolbox'); -require('dotenv').config(); +```typescript +import { defineConfig, configVariable } from 'hardhat/config'; +import hardhatToolboxMochaEthers from '@nomicfoundation/hardhat-toolbox-mocha-ethers'; -/** @type import('hardhat/config').HardhatUserConfig */ -module.exports = { +export default defineConfig({ solidity: '0.8.28', networks: { sei: { - url: 'https://evm-rpc.sei-apis.com', + type: 'http', chainId: 1329, - accounts: [process.env.PRIVATE_KEY] + url: 'https://evm-rpc.sei-apis.com', + accounts: [configVariable('PRIVATE_KEY')] } - } -}; + }, + plugins: [hardhatToolboxMochaEthers] +}); ``` -Create a `.env` file in the root directory with your private key and RPC URL: +Store your private key in Hardhat's encrypted keystore (no plaintext `.env` file needed): -```plaintext -RPC_URL=https://evm-rpc.sei-apis.com -PRIVATE_KEY=your_private_key_here +```bash +npx hardhat keystore set PRIVATE_KEY ``` #### Import Precompile Components @@ -290,12 +290,12 @@ const recipientAddress = 'sei1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6s8dpw'; const amountInSei = '0.1'; // 0.1 SEI try { - // Convert SEI to usei (1 SEI = 1,000,000 usei) - const amountInUsei = ethers.parseUnits(amountInSei, 6); + // msg.value is in wei (18 decimals), like any other EVM transaction + const amountInWei = ethers.parseUnits(amountInSei, 18); // equivalent to ethers.parseEther(amountInSei) // Send native SEI const tx = await bank.sendNative(recipientAddress, { - value: amountInUsei, + value: amountInWei, gasLimit: 300000n }); @@ -335,31 +335,30 @@ contract PaymentProcessor { +`send()` can only be called by the registered ERC20 native pointer contract for the given denom — calling it from a regular wallet will revert. The snippet below only illustrates the arguments a pointer contract passes. As a user, transfer native denoms through the denom's ERC20 pointer contract (`transfer()`), or use `sendNative()` for SEI. + ```typescript -// Send custom tokens between addresses +// Illustration: how a registered ERC20 pointer contract calls send() const fromAddress = '0x1234567890123456789012345678901234567890'; const toAddress = '0x9876543210987654321098765432109876543210'; const tokenDenom = 'usei'; const amount = '1'; -try { - // Convert addresses to proper format if needed - const tx = await bank.send( - fromAddress, - toAddress, - tokenDenom, - ethers.parseUnits(amount, 18), // Adjust decimals based on token - { - gasLimit: 300000n - } - ); +// Amounts are in the denom's base units — check decimals() for each denom. +// usei has 6 decimals (1 SEI = 1,000,000 usei) +const tx = await bank.send( + fromAddress, + toAddress, + tokenDenom, + ethers.parseUnits(amount, 6), + { + gasLimit: 300000n + } +); - const receipt = await tx.wait(); - console.log('Custom token transfer successful:', receipt.transactionHash); - console.log(`Sent ${amount} ${tokenDenom} from ${fromAddress} to ${toAddress}`); -} catch (error) { - console.error('Custom token transfer failed:', error); -} +const receipt = await tx.wait(); +console.log('Custom token transfer successful:', receipt.transactionHash); +console.log(`Sent ${amount} ${tokenDenom} from ${fromAddress} to ${toAddress}`); ``` @@ -1013,14 +1012,14 @@ npx hardhat compile First, deploy the contract using Hardhat: ```javascript -const { ethers } = require('hardhat'); +import { network } from 'hardhat'; -const dotenv = require('dotenv'); -dotenv.config(); +// Hardhat 3 exposes ethers through a network connection rather than a global import +const { ethers } = await network.getOrCreate(); async function main() { - const provider = new ethers.JsonRpcProvider(process.env.RPC_URL); - const deployer = new ethers.Wallet(process.env.PRIVATE_KEY, provider); + // The signer comes from the network config (encrypted keystore) + const [deployer] = await ethers.getSigners(); console.log('Deploying contracts with the account:', deployer.address); const balance = await deployer.provider.getBalance(deployer.address); @@ -1051,13 +1050,16 @@ npx hardhat run scripts/deploy.js --network sei Next look through the integration example: ```javascript -const { ethers } = require('hardhat'); -const TokenManager = require('./artifacts/contracts/ComprehensiveTokenManager.sol/ComprehensiveTokenManager.json'); +import { network } from 'hardhat'; +import TokenManager from './artifacts/contracts/ComprehensiveTokenManager.sol/ComprehensiveTokenManager.json' with { type: 'json' }; + +// Hardhat 3 exposes ethers through a network connection rather than a global import +const { ethers } = await network.getOrCreate(); async function main() { - const provider = new ethers.JsonRpcProvider(process.env.RPC_URL); - const deployer = new ethers.Wallet(process.env.PRIVATE_KEY, provider); - console.log('Deploying contracts with the account:', deployer.address); + // The signer comes from the network config (encrypted keystore) + const [deployer] = await ethers.getSigners(); + console.log('Interacting with the account:', deployer.address); const address = '0xYourDeployedContractAddress'; // Replace with your contract address const tokenManager = new ethers.Contract(address, TokenManager.abi, deployer); @@ -1092,10 +1094,10 @@ main() }); ``` -The run the integration example: +Then run the integration example: ```bash -node integrationExample.js +npx hardhat run scripts/integrationExample.js --network sei ``` diff --git a/evm/precompiles/cosmwasm-precompiles/cosmwasm.mdx b/evm/precompiles/cosmwasm-precompiles/cosmwasm.mdx index ff6c7ca..a24b16d 100644 --- a/evm/precompiles/cosmwasm-precompiles/cosmwasm.mdx +++ b/evm/precompiles/cosmwasm-precompiles/cosmwasm.mdx @@ -676,10 +676,10 @@ contract CosmWasmBridge { **Client Integration Example** ```javascript -const abi = require('./artifacts/contracts/CosmWasmBridge.sol/CosmWasmBridge.json'); -const { ethers } = require('hardhat'); -const dotenv = require('dotenv'); -dotenv.config(); +import { network } from 'hardhat'; + +// Hardhat 3 exposes ethers through a network connection rather than a global import +const { ethers } = await network.getOrCreate(); async function main() { const [deployer] = await ethers.getSigners(); diff --git a/evm/precompiles/cosmwasm-precompiles/example-usage.mdx b/evm/precompiles/cosmwasm-precompiles/example-usage.mdx index e957179..a2db606 100644 --- a/evm/precompiles/cosmwasm-precompiles/example-usage.mdx +++ b/evm/precompiles/cosmwasm-precompiles/example-usage.mdx @@ -91,13 +91,13 @@ const overrides = { }; const executeResponse = await contract.execute( COUNTER_CONTRACT_ADDRESS, - toUtf8Bytes(JSON.stringify(executeJSON)), - toUtf8Bytes(JSON.stringify({ denom: 'usdc', amount: '100' })), // Also send 100 usdc + toUtf8Bytes(JSON.stringify(executeMsg)), + toUtf8Bytes(JSON.stringify([{ denom: 'uusdc', amount: '100' }])), // Also send 100 uusdc overrides ); await executeResponse.wait(); -const receipt = await provider.getTransactionReceipt(executionResponse.hash); +const receipt = await provider.getTransactionReceipt(executeResponse.hash); ``` For payable contracts, Sei amounts have to be sent directly to the contract diff --git a/evm/precompiles/example-usage.mdx b/evm/precompiles/example-usage.mdx index 8acca96..4698939 100644 --- a/evm/precompiles/example-usage.mdx +++ b/evm/precompiles/example-usage.mdx @@ -119,12 +119,12 @@ const delegation = await client.readContract({ args: [account, 'seivaloper1...'], }); -// Undelegate +// Undelegate 5 SEI — amount is in 6-decimal usei (1 SEI = 1_000_000 usei), not wei const undelegateHash = await walletClient.writeContract({ address: STAKING_PRECOMPILE_ADDRESS, abi: STAKING_PRECOMPILE_ABI, functionName: 'undelegate', - args: ['seivaloper1...', parseEther('5')], + args: ['seivaloper1...', 5_000_000n], }); ``` @@ -142,8 +142,8 @@ await tx.wait(); const delegation = await readStaking.delegation(await signer.getAddress(), 'seivaloper1...'); console.log('Delegated:', delegation.balance.amount.toString()); -// Undelegate -const undelegateTx = await staking.undelegate('seivaloper1...', ethers.parseEther('5')); +// Undelegate 5 SEI — amount is in 6-decimal usei (1 SEI = 1_000_000 usei), not wei +const undelegateTx = await staking.undelegate('seivaloper1...', ethers.parseUnits('5', 6)); await undelegateTx.wait(); ``` @@ -191,8 +191,8 @@ import { DISTRIBUTION_PRECOMPILE_ABI, DISTRIBUTION_PRECOMPILE_ADDRESS } from '@s const hash = await walletClient.writeContract({ address: DISTRIBUTION_PRECOMPILE_ADDRESS, abi: DISTRIBUTION_PRECOMPILE_ABI, - functionName: 'withdrawDelegatorReward', - args: [account, 'seivaloper1...'], + functionName: 'withdrawDelegationRewards', + args: ['seivaloper1...'], }); ``` @@ -205,7 +205,7 @@ const distribution = new ethers.Contract( signer, ); -const tx = await distribution.withdrawDelegatorReward(await signer.getAddress(), 'seivaloper1...'); +const tx = await distribution.withdrawDelegationRewards('seivaloper1...'); await tx.wait(); ``` diff --git a/evm/precompiles/governance.mdx b/evm/precompiles/governance.mdx index 584295d..f77f0a0 100644 --- a/evm/precompiles/governance.mdx +++ b/evm/precompiles/governance.mdx @@ -623,7 +623,7 @@ Create a comprehensive governance interaction script: ```javascript title="governance-mainnet-demo.js" const { ethers } = require('ethers'); -const { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } = require('@sei-js/evm'); +const { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } = require('@sei-js/precompiles'); require('dotenv').config(); // Validation functions @@ -845,7 +845,7 @@ This is a test proposal. Vote according to your preference to test the system.`, // Summary console.log('\n🎉 === Governance Demo Completed Successfully! ==='); console.log(`📊 Proposal ID: ${proposalID.toString()}`); - console.log(`💰 Total deposited: ${ethers.formatEther(depositAmount.add(additionalDeposit))} SEI`); + console.log(`💰 Total deposited: ${ethers.formatEther(depositAmount + additionalDeposit)} SEI`); console.log('🔗 View all governance proposals: https://sei.explorers.guru/proposals'); } catch (error) { if (error.message.includes('insufficient funds')) { diff --git a/evm/precompiles/json.mdx b/evm/precompiles/json.mdx index f719a9e..566f8b7 100644 --- a/evm/precompiles/json.mdx +++ b/evm/precompiles/json.mdx @@ -565,7 +565,8 @@ node json-precompile-mainnet.js 📊 Source: MainnetPriceOracle 🔢 Version: 2.1.0 ⏰ Timestamp: 2024-12-27T12:00:00Z - ⚠️ Key "nonexistent.key" not found, using default: "N/A" + 🟢 Is Live: Yes + ⚠️ Key "nonexistent" not found, using default: "N/A" ❓ Missing Key: N/A 2️⃣ Extracting Numeric Data... @@ -592,11 +593,11 @@ node json-precompile-mainnet.js Complex processing: 120,000 gas 6️⃣ Performance & Error Testing... - ✅ metadata.source: "MainnetPriceOracle" (15ms) - ✅ market.prices.SEI: "275" (12ms) - ✅ assets: "[\"SEI\",\"BTC\",\"ETH\",\"USDC\"]" (18ms) - ⚠️ Key "nonexistent.deeply.nested.key" not found, using default: "" - ✅ nonexistent.deeply.nested.key: "" (8ms) + ✅ metadata object: "{"timestamp":"2024-12-27T12:00:00Z","source":"..." (15ms) + ✅ market data: "{"prices":{"SEI":275,"BTC":5000000,"ETH":30000..." (12ms) + ✅ assets array: "["SEI","BTC","ETH","USDC"]" (18ms) + ⚠️ Key "nonexistent" not found, using default: "" + ✅ missing key: "" (8ms) 🎉 === JSON Precompile Demo Completed Successfully! === ``` @@ -620,21 +621,23 @@ class SeiPriceOracle { isValid: boolean; }> { try { - // Extract timestamp - const timestamp = await safeExtractBytes(this.jsonPrecompile, oracleResponse, 'metadata.timestamp'); + // The precompile does not support dot notation for nested keys. + // Extract the parent "metadata" object as bytes, then parse it manually. + const metadataJson = await safeExtractBytes(this.jsonPrecompile, oracleResponse, 'metadata', '{}'); + const metadata = JSON.parse(metadataJson); + const timestamp = metadata.timestamp ?? ''; + const isValid = metadata.isValid === 1; - // Extract validation flag - const isValidRaw = await safeExtractUint256(this.jsonPrecompile, oracleResponse, 'metadata.isValid'); - const isValid = isValidRaw === 1n; + // Extract the parent "prices" object the same way + const pricesJson = await safeExtractBytes(this.jsonPrecompile, oracleResponse, 'prices', '{}'); + const rawPrices = JSON.parse(pricesJson); - // Extract price data const prices = new Map(); const assets = await safeExtractBytesList(this.jsonPrecompile, oracleResponse, 'assets'); for (const asset of assets) { - const priceRaw = await safeExtractUint256(this.jsonPrecompile, oracleResponse, `prices.${asset}`); // Convert from integer with 6 decimal places - prices.set(asset, Number(priceRaw) / 1000000); + prices.set(asset, Number(rawPrices[asset] ?? 0) / 1000000); } return { prices, timestamp, isValid }; diff --git a/evm/precompiles/p256-precompile.mdx b/evm/precompiles/p256-precompile.mdx index 02c8c11..5f468aa 100644 --- a/evm/precompiles/p256-precompile.mdx +++ b/evm/precompiles/p256-precompile.mdx @@ -66,10 +66,15 @@ library P256 { returns (bool) { bytes memory input = abi.encode(digest, signature.r, signature.s, publicKey.x, publicKey.y); - bytes memory output = P256VERIFY_CONTRACT.verify(input); - bool success = output.length == 32 && output[31] == 0x01; + // On invalid signatures the precompile returns no data, which would + // make a high-level interface call revert when decoding the `bytes` + // return value. Use a low-level staticcall and treat empty return + // data as "invalid signature" instead. + (bool success, bytes memory output) = P256VERIFY_PRECOMPILE_ADDRESS.staticcall( + abi.encodeWithSelector(IP256VERIFY.verify.selector, input) + ); - return success; + return success && output.length > 0; } } ``` @@ -106,7 +111,7 @@ For more control, you can call the precompile directly: pragma solidity ^0.8.0; interface IP256Verify { - function verify(bytes calldata input) external view returns (bytes32); + function verify(bytes calldata input) external view returns (bytes memory response); } contract P256Verifier { @@ -127,8 +132,12 @@ contract P256Verifier { publicKeyY ); - bytes32 result = P256_PRECOMPILE.verify(input); - return result != bytes32(0); + // Success returns a non-empty (ABI-encoded 32-byte) value; invalid + // signatures return no data, which would revert a high-level call. + (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall( + abi.encodeWithSelector(IP256Verify.verify.selector, input) + ); + return ok && output.length > 0; } } ``` @@ -198,8 +207,10 @@ contract WebAuthnAuth { userKey.y ); - bytes32 result = P256_PRECOMPILE.verify(input); - return result != bytes32(0); + (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall( + abi.encodeWithSelector(IP256Verify.verify.selector, input) + ); + return ok && output.length > 0; } } ``` @@ -208,6 +219,8 @@ contract WebAuthnAuth { ```solidity contract SecureEnclaveAuth { + IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011); + event SecureOperation(address indexed user, bytes32 indexed operationHash); function executeSecureOperation( @@ -233,8 +246,10 @@ contract SecureEnclaveAuth { bytes32 y ) internal view returns (bool) { bytes memory input = abi.encodePacked(hash, r, s, x, y); - bytes32 result = P256_PRECOMPILE.verify(input); - return result != bytes32(0); + (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall( + abi.encodeWithSelector(IP256Verify.verify.selector, input) + ); + return ok && output.length > 0; } } ``` @@ -275,8 +290,10 @@ contract HardwareMultiSig { publicKeys[i].y ); - bytes32 result = P256_PRECOMPILE.verify(input); - if (result != bytes32(0)) { + (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall( + abi.encodeWithSelector(IP256Verify.verify.selector, input) + ); + if (ok && output.length > 0) { validSignatures++; } } @@ -309,9 +326,9 @@ P256 signatures can be malleable. If your application requires unique signatures ```solidity function isLowS(bytes32 s) internal pure returns (bool) { - // Check if s is in the lower half of the curve order - // This prevents signature malleability - return uint256(s) <= 0x7FFFFFFF80000000FFFFFFFFFFFFFFFF; + // Check if s is in the lower half of the curve order (n / 2, where n is + // the P-256 group order). This prevents signature malleability. + return uint256(s) <= 0x7FFFFFFF800000007FFFFFFFFFFFFFFFDE737D56D38BCF4279DCE5617E3192A8; } ``` @@ -320,30 +337,41 @@ function isLowS(bytes32 s) internal pure returns (bool) { ### Preparing Input Data ```javascript -// Example: Preparing P256 verification input +// Example: Preparing P256 verification input (ethers v6) function prepareP256Input(messageHash, signature, publicKey) { // Ensure all components are 32 bytes - const hash = ethers.utils.hexZeroPad(messageHash, 32); - const r = ethers.utils.hexZeroPad(signature.r, 32); - const s = ethers.utils.hexZeroPad(signature.s, 32); - const x = ethers.utils.hexZeroPad(publicKey.x, 32); - const y = ethers.utils.hexZeroPad(publicKey.y, 32); + const hash = ethers.zeroPadValue(messageHash, 32); + const r = ethers.zeroPadValue(signature.r, 32); + const s = ethers.zeroPadValue(signature.s, 32); + const x = ethers.zeroPadValue(publicKey.x, 32); + const y = ethers.zeroPadValue(publicKey.y, 32); // Concatenate all components - return ethers.utils.concat([hash, r, s, x, y]); + return ethers.concat([hash, r, s, x, y]); } // Usage with ethers.js -async function verifyP256Signature(contract, messageHash, signature, publicKey) { +const P256_VERIFY_ADDRESS = '0x0000000000000000000000000000000000001011'; +const P256_VERIFY_ABI = ['function verify(bytes input) view returns (bytes response)']; + +async function verifyP256Signature(provider, messageHash, signature, publicKey) { + const precompile = new ethers.Contract(P256_VERIFY_ADDRESS, P256_VERIFY_ABI, provider); const input = prepareP256Input(messageHash, signature, publicKey); - const result = await contract.P256_PRECOMPILE.verify(input); - return result !== '0x0000000000000000000000000000000000000000000000000000000000000000'; + try { + const result = await precompile.verify(input); + // Success returns 32 bytes ending in 0x01 + return result === '0x0000000000000000000000000000000000000000000000000000000000000001'; + } catch (err) { + // Invalid signatures return no data, which ethers cannot decode as `bytes` + // (BAD_DATA) — treat that as a failed verification. + return false; + } } ``` ## Error Handling -The P256 precompile returns zero on failure. Common failure cases include: +The P256 precompile returns no data on failure. Because `verify` is declared as returning `bytes`, a high-level Solidity interface call reverts when it tries to decode that empty return data — always use a low-level `staticcall` (as in the examples above) so invalid signatures resolve to `false` instead of reverting. Common failure cases include: 1. **Invalid Input Length**: Input must be exactly 160 bytes 2. **Invalid Public Key**: Point not on the P256 curve @@ -372,8 +400,11 @@ function safeVerifyP256( messageHash, r, s, publicKeyX, publicKeyY ); - bytes32 result = P256_PRECOMPILE.verify(input); - return (result != bytes32(0), result == bytes32(0) ? "Verification failed" : ""); + (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall( + abi.encodeWithSelector(IP256Verify.verify.selector, input) + ); + bool verified = ok && output.length > 0; + return (verified, verified ? "" : "Verification failed"); } ``` @@ -395,9 +426,11 @@ contract P256Test { bytes32 y = 0x...; // Valid public key y bytes memory input = abi.encodePacked(messageHash, r, s, x, y); - bytes32 result = P256_PRECOMPILE.verify(input); + (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall( + abi.encodeWithSelector(IP256Verify.verify.selector, input) + ); - assert(result != bytes32(0)); + assert(ok && output.length > 0); } } ``` diff --git a/evm/precompiles/staking.mdx b/evm/precompiles/staking.mdx index ce2f961..24f48aa 100644 --- a/evm/precompiles/staking.mdx +++ b/evm/precompiles/staking.mdx @@ -475,7 +475,7 @@ npm install @sei-js/precompiles@2.1.2 ```typescript // Import Staking precompile address and ABI -// View the entire ABI here: https://github.com/sei-protocol/sei-chain/tree/evm/precompiles/staking +// View the entire ABI here: https://github.com/sei-protocol/sei-chain/tree/main/precompiles/staking import { STAKING_PRECOMPILE_ABI, STAKING_PRECOMPILE_ADDRESS } from '@sei-js/precompiles'; import { ethers } from 'ethers'; ``` @@ -1297,10 +1297,10 @@ contract StakingManager { **Client Integration Example** ```javascript -const abi = require('./artifacts/contracts/Staking.sol/StakingManager.json'); -const { ethers } = require('hardhat'); -const dotenv = require('dotenv'); -dotenv.config(); +import { network } from 'hardhat'; + +// Hardhat 3 exposes ethers through a network connection rather than a global import +const { ethers } = await network.getOrCreate(); async function main() { const [deployer] = await ethers.getSigners(); diff --git a/evm/python-quickstart.mdx b/evm/python-quickstart.mdx index 6bc06f0..aabc101 100644 --- a/evm/python-quickstart.mdx +++ b/evm/python-quickstart.mdx @@ -5,8 +5,6 @@ description: 'Connect to Sei from Python using web3.py — read chain data, then keywords: ['sei evm', 'python', 'web3.py', 'json-rpc', 'quickstart'] --- -# Python Quickstart (web3.py) - Sei is fully EVM-compatible, so any standard Ethereum client works — including Python's [web3.py](https://web3py.readthedocs.io). This guide connects to Sei from a Python script, reads chain data with no credentials, then signs and broadcasts a transaction. ## Install diff --git a/evm/sei-global-wallet.mdx b/evm/sei-global-wallet.mdx index 578a510..afaf930 100644 --- a/evm/sei-global-wallet.mdx +++ b/evm/sei-global-wallet.mdx @@ -851,7 +851,7 @@ cd my-sei-dapp **Install Dependencies:** ```bash -npm install @web3auth/modal @web3auth/modal/react \ +npm install @web3auth/modal \ wagmi viem @tanstack/react-query \ @sei-js/sei-global-wallet ``` diff --git a/evm/sei-js/create-sei.mdx b/evm/sei-js/create-sei.mdx index 1b0dbf2..f7d44f7 100644 --- a/evm/sei-js/create-sei.mdx +++ b/evm/sei-js/create-sei.mdx @@ -16,8 +16,16 @@ Every generated project includes wallet connections, contract interactions, Type You don't need to install `@sei-js/create-sei` globally. Use it directly with npx or pnpm: - ```bash npx @sei-js/create-sei app --name my-sei-app ``` - ```bash pnpm create @sei-js/create-sei app --name my-sei-app ``` + + ```bash + npx @sei-js/create-sei app --name my-sei-app + ``` + + + ```bash + pnpm create @sei-js/sei app --name my-sei-app + ``` + ## Interactive Setup diff --git a/evm/sei-js/ledger.mdx b/evm/sei-js/ledger.mdx index 65a3f68..c3bc183 100644 --- a/evm/sei-js/ledger.mdx +++ b/evm/sei-js/ledger.mdx @@ -112,7 +112,7 @@ async function delegateWithLedger() { const msgDelegate = { typeUrl: '/cosmos.staking.v1beta1.MsgDelegate', value: { - delegatorAddress: nativeAddress.address, + delegatorAddress: nativeAddress, validatorAddress: 'seivaloper1...', amount: coins(500, 'usei') } @@ -123,7 +123,7 @@ async function delegateWithLedger() { gas: '200000' }; - const result = await client.signAndBroadcast(nativeAddress.address, [msgDelegate], fee, 'Delegation via Ledger'); + const result = await client.signAndBroadcast(nativeAddress, [msgDelegate], fee, 'Delegation via Ledger'); console.log('Broadcast result:', result); } diff --git a/evm/tracing/index.mdx b/evm/tracing/index.mdx index 014009e..3473127 100644 --- a/evm/tracing/index.mdx +++ b/evm/tracing/index.mdx @@ -6,13 +6,15 @@ keywords: ['debug tracing', 'evm debugging', 'transaction analysis', 'gas optimi --- ## Requirements -Install Ethers.js v6.14.4+: +The examples in this guide call the debug JSON-RPC endpoints directly with `curl` — no SDK or additional libraries are required. Optionally install [`jq`](https://jqlang.github.io/jq/) to pretty-print the JSON responses: ```bash -npm install ethers@^6.14.4 -``` +# macOS +brew install jq -All examples use modern ES6 modules and TypeScript patterns. +# Debian/Ubuntu +sudo apt-get install jq +``` Debug tracing is your primary tool for understanding EVM transaction execution on Sei. This comprehensive system allows you to analyze transaction flow, optimize gas usage, debug smart contract interactions, and troubleshoot production issues. diff --git a/evm/transactions-with-seid.mdx b/evm/transactions-with-seid.mdx index a403a4d..4ed88b3 100644 --- a/evm/transactions-with-seid.mdx +++ b/evm/transactions-with-seid.mdx @@ -21,7 +21,7 @@ If the machine you run these commands from are not a node of the network, you'd --evm-rpc http:// ``` -Refer to the bottom of this [page](/) for the list of available RPC endpoints. +Refer to the [RPC endpoints](/evm/networks) page for the list of available RPC endpoints. Most commands support `--evm-rpc` flag to specify custom RPC endpoints. The default is `http://localhost:8545`. diff --git a/evm/usdc-on-sei.mdx b/evm/usdc-on-sei.mdx index 887d480..324248c 100644 --- a/evm/usdc-on-sei.mdx +++ b/evm/usdc-on-sei.mdx @@ -200,7 +200,7 @@ We also log key info and handle errors: console.log('Tx hash:', hash); const explorerBase = chain.blockExplorers && chain.blockExplorers.default && chain.blockExplorers.default.url ? chain.blockExplorers.default.url : 'https://seiscan.io'; - console.log('Explorer:', `${explorerBase}&tx=${hash}`); + console.log('Explorer:', `${explorerBase}/tx/${hash}`); } catch (err) { console.error('Transfer failed:', err.message || err); process.exit(1); diff --git a/evm/wallet-integrations/particle.mdx b/evm/wallet-integrations/particle.mdx index 515e81f..51eafaa 100644 --- a/evm/wallet-integrations/particle.mdx +++ b/evm/wallet-integrations/particle.mdx @@ -258,9 +258,9 @@ This transaction will be gasless because it meets two key conditions: 1. **Gasless Mode Configuration**: By setting `SendTransactionMode.Gasless` within `AAWrapProvider`, we've specified that the transaction should be gasless and sponsored. -2. **Funding Requirements**: On a Testnet like Sei, all transactions are - automatically sponsored, meaning you don't need to deposit USDT to cover - transaction fees. However, on mainnets like Sei, the Paymaster (configurable +2. **Funding Requirements**: On Sei testnet, all transactions are + automatically sponsored, meaning you don't need to deposit funds to cover + transaction fees. However, on Sei mainnet, the Paymaster (configurable in the [Particle dashboard](https://dashboard.particle.network)) would need sufficient funds to sponsor these transactions. @@ -275,7 +275,7 @@ With the setup complete, Particle Connect can now be leveraged, as demonstrated in the example application below. In this example, the application creates a smart account on Sei using a social -login or a Web3 login and sends a gasless transaction of 0.01 SEI via the +login or a Web3 login and sends a gasless transaction of 0.001 SEI via the `ethers` provider. ```tsx diff --git a/evm/wallet-integrations/pimlico.mdx b/evm/wallet-integrations/pimlico.mdx index f690829..d54391b 100644 --- a/evm/wallet-integrations/pimlico.mdx +++ b/evm/wallet-integrations/pimlico.mdx @@ -44,8 +44,6 @@ import { toSimpleSmartAccount } from 'permissionless/accounts'; import { writeFileSync } from 'fs'; import 'dotenv/config'; -// Load environment variables -dotenv.config(); const apiKey = process.env.PIMLICO_API_KEY; if (!apiKey) throw new Error('Missing PIMLICO_API_KEY'); diff --git a/evm/wallet-integrations/thirdweb.mdx b/evm/wallet-integrations/thirdweb.mdx index 05bfdb4..886c37c 100644 --- a/evm/wallet-integrations/thirdweb.mdx +++ b/evm/wallet-integrations/thirdweb.mdx @@ -128,6 +128,7 @@ import { ConnectButton, TransactionButton, useActiveAccount, + useReadContract, } from "thirdweb/react"; import { inAppWallet } from "thirdweb/wallets"; import { client } from "./client"; @@ -196,31 +197,15 @@ async function handleStore(account: any, value: string) { } } -// Retrieve value function -async function handleRetrieve(account: any) { - if (!account) return; +function App() { + const account = useActiveAccount(); - const transaction = prepareContractCall({ + // Read the stored value (free RPC call — no transaction needed) + const { data: storedValue, refetch: refetchStoredValue } = useReadContract({ contract: storageContract, method: "function retrieve() view returns (uint256)", }); - try { - const txResult = await sendAndConfirmTransaction({ - transaction, - account, - }); - console.log("Retrieved value:", txResult); - return txResult.transactionHash; - } catch (error) { - console.error("Retrieve failed:", error); - alert("Failed to retrieve value. Check console."); - } -} - -function App() { - const account = useActiveAccount(); - return (
@@ -295,11 +280,15 @@ function App() {

Retrieve Stored Number

+

+ Stored value:{" "} + {storedValue !== undefined ? storedValue.toString() : "—"} +

diff --git a/learn/accounts.mdx b/learn/accounts.mdx index e24f897..b19fdc7 100644 --- a/learn/accounts.mdx +++ b/learn/accounts.mdx @@ -124,8 +124,12 @@ This method involves signing a predefined message to prove ownership of the account. ```ts +import { createWalletClient, http, parseSignature, toHex } from 'viem'; import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts'; -import { parseSignature, toHex } from 'viem'; +import { seiTestnet } from 'viem/chains'; +import { ADDRESS_PRECOMPILE_ABI, ADDRESS_PRECOMPILE_ADDRESS } from '@sei-js/evm'; + +const client = createWalletClient({ chain: seiTestnet, transport: http() }); const associate = async () => { const account = privateKeyToAccount(''); @@ -158,7 +162,12 @@ This method compresses the public key and sends it for association. ```ts import secp256k1 from 'secp256k1'; +import { createWalletClient, http } from 'viem'; import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts'; +import { seiTestnet } from 'viem/chains'; +import { ADDRESS_PRECOMPILE_ABI, ADDRESS_PRECOMPILE_ADDRESS } from '@sei-js/evm'; + +const client = createWalletClient({ chain: seiTestnet, transport: http() }); const associateViaPubkey = async () => { const account = privateKeyToAccount(''); diff --git a/learn/dev-gas.mdx b/learn/dev-gas.mdx index 6d193cb..bdacd8e 100644 --- a/learn/dev-gas.mdx +++ b/learn/dev-gas.mdx @@ -29,7 +29,13 @@ completing the transaction. ### Fee -`Fee = Gas Price * Gas Limit`. +`Fee = Gas Price * Gas Used`. + +The gas limit caps the maximum possible charge at `Gas Price * Gas Limit`. Note +that on Sei, excess gas (the gas limit beyond the actual gas used) may not be +refunded in full or in part, so avoid setting gas limits far above what a +transaction actually needs. See +[Sei EVM vs Ethereum](/evm/differences-with-ethereum) for details. To help developers and users estimate fees on Sei, please reference [Sei Gas](https://seigas.com) and the associated diff --git a/learn/hardware-wallets.mdx b/learn/hardware-wallets.mdx index 4e7f2d8..b0c90a4 100644 --- a/learn/hardware-wallets.mdx +++ b/learn/hardware-wallets.mdx @@ -7,8 +7,17 @@ description: 'Comprehensive guide to Hardware Wallets on Sei. Learn key concepts title: Hardware Wallets --- Signing transactions manually or using hardware wallets like Ledger ensures -secure transaction approval. In order to sign Sei transactions you must have the -Cosmos app installed on your wallet and interact with a Cosmos RPC endpoint. +secure transaction approval. To sign Sei transactions, install the Ethereum app +(or the dedicated Sei app) on your Ledger device and sign through an EVM wallet +such as MetaMask. + + +The legacy flow that used the Cosmos app with a Cosmos RPC endpoint is +deprecated: per [SIP-03](/learn/sip-03-migration), Cosmos-native interfaces are +slated for deprecation on June 15, 2026. If you still hold funds on a Ledger +Cosmos-app (`sei1...`) account, see +[Migrating a hardware or mnemonic-only wallet](/learn/sip-03-migration#migrating-a-hardware-or-mnemonic-only-wallet). + ## Using MetaMask with Ledger @@ -25,9 +34,14 @@ To connect your Ledger device through MetaMask and sign transactions: 4. **Sign Transactions**: Once connected, you can sign transactions directly through MetaMask using your Ledger device. -**Installing the Cosmos App on Ledger** +**Installing the Ethereum App on Ledger** -Before you can use your Ledger device to sign transactions, ensure you have the -Cosmos app installed. You can download it from the Ledger app store: +Before you can use your Ledger device to sign transactions through MetaMask, +ensure you have the Ethereum app installed. You can install it through Ledger +Live: -• [Ledger App Store - Cosmos App](https://www.ledger.com/coin/wallet/cosmos) +• [Ledger App Store - Ethereum App](https://www.ledger.com/coin/wallet/ethereum) + +Alternatively, you can install the dedicated Sei app from the Ledger Live App +Catalog — see [Ledger Setup](/learn/ledger-setup) for step-by-step +instructions. diff --git a/learn/seidb.mdx b/learn/seidb.mdx index 84a5876..172ba02 100644 --- a/learn/seidb.mdx +++ b/learn/seidb.mdx @@ -205,28 +205,6 @@ SeiDB maintains complete compatibility with the Ethereum protocol specifications These compatibility guarantees ensure existing smart contracts, development tools, and infrastructure components work without modification. The system undergoes extensive compatibility testing against the official Ethereum test suites to verify identical results compared to reference implementations. -### Developer Tooling - -SeiDB includes analysis tools that provide insights into contract storage behavior: - -```javascript -// Storage analysis integration -async function analyzeContractStorage(contractAddress) { - const provider = new ethers.providers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); - - const stats = await provider.send('sei_getStorageStats', [contractAddress, { blockCount: 1000 }]); - - console.log('Access Hotspots:', stats.hotSlots); - console.log('Access Pattern Type:', stats.accessPattern); - console.log('Contention Points:', stats.contentionPoints); - console.log('Optimization Suggestions:', stats.optimizationSuggestions); - - return stats; -} -``` - -These tools integrate with standard development environments and provide actionable insights into storage usage patterns. The analysis examines actual on-chain behavior over specified time ranges and identifies patterns that might not be obvious from code inspection alone. - ### Deployment Configurations SeiDB's architecture supports various deployment configurations optimized for different node roles: diff --git a/learn/sip-03-migration.mdx b/learn/sip-03-migration.mdx index 17e4b83..5edaad0 100644 --- a/learn/sip-03-migration.mdx +++ b/learn/sip-03-migration.mdx @@ -45,7 +45,7 @@ If you hold any of the following IBC assets on Sei, take action before [Proposal | Asset | Token contract | Action(s) | Possible route(s) | Support | | :---- | :---- | :---- | :---- | :---- | -| **USDC.n (USDC via Noble)** | [seiscan](https://seiscan.io/address/0x3894085ef7ff0f0aedf52e2a2704928d1ec074f1) [mintscan](https://www.mintscan.io/sei/assets/ibc%2FCA6FBFAF399474A06263E10D0CE5AEBBE15189D6D4B2DD9ADE61007E68EB9DB0) | Swap to native USDC or migrate via CCTP | Sapyhre, [Symphony](https://symph.ag/), or [Stargate](https://stargate.finance/?srcChain=sei&srcToken=0x3894085Ef7Ff0f0aeDf52E2A2704928d1Ec074F1) | [Sei blog](https://blog.sei.io/announcements/holders-of-usdcn-need-to-swap-or-migrate/) [Sei Tech Chat](https://t.me/+KZdhZ1eE-G01NmZk) [Discord](https://discord.com/invite/sei?ref=blog.sei.io) | +| **USDC.n (USDC via Noble)** | [seiscan](https://seiscan.io/address/0x3894085ef7ff0f0aedf52e2a2704928d1ec074f1) [mintscan](https://www.mintscan.io/sei/assets/ibc%2FCA6FBFAF399474A06263E10D0CE5AEBBE15189D6D4B2DD9ADE61007E68EB9DB0) | Swap to native USDC or migrate via CCTP | [Saphyre](https://saphyre.xyz/swap?inputCurrency=0x3894085Ef7Ff0f0aeDf52E2A2704928d1Ec074F1&outputCurrency=0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392), [Symphony](https://symph.ag/), or [Stargate](https://stargate.finance/?srcChain=sei&srcToken=0x3894085Ef7Ff0f0aeDf52E2A2704928d1Ec074F1) | [Sei blog](https://blog.sei.io/announcements/holders-of-usdcn-need-to-swap-or-migrate/) [Sei Tech Chat](https://t.me/+KZdhZ1eE-G01NmZk) [Discord](https://discord.com/invite/sei?ref=blog.sei.io) | | **USDT.kava (USDT via Kava)** | [seiscan](https://seiscan.io/token/0xb75d0b03c06a926e488e2659df1a861f860bd3d1) [mintscan](https://www.mintscan.io/sei/assets/ibc%2F6C00E4AA0CC7618370F81F7378638AE6C48EFF8C9203CE1C2357012B440EBDB7)| Swap via Symphony or bridge out to Kava | [Symphony](https://symph.ag/), [Stargate](https://stargate.finance/?srcChain=sei&srcToken=0xB75D0B03c06A926e488e2659DF1A861F860bD3d1) | [Sei Tech Chat](https://t.me/+KZdhZ1eE-G01NmZk) [Discord](https://discord.com/invite/sei) | | **USDCso (Wormhole, Solana)** | [mintscan](https://www.mintscan.io/sei/assets/factory%2Fsei189adguawugk3e55zn63z8r9ll29xrjwca636ra7v7gxuzn98sxyqwzt47l%2F9fELvUhFo6yWL34ZaLgPbCPzdk9MD1tAzMycgH45qShH) | Bridge out to Solana via Wormhole | [Portal Bridge (legacy)](https://legacy.portalbridge.com/#/transfer) | [Sei Tech Chat](https://t.me/+KZdhZ1eE-G01NmZk) [Discord](https://discord.com/invite/sei) | | **Wormhole-bridged WETH** | [mintscan](https://www.mintscan.io/sei/assets/factory%2Fsei189adguawugk3e55zn63z8r9ll29xrjwca636ra7v7gxuzn98sxyqwzt47l%2F4tLQqCLaoKKfNFuPjA9o39YbKUwhR1F8N29Tz3hEbfP2) | Bridge out to Ethereum via Wormhole | [Portal Bridge (legacy)](https://legacy.portalbridge.com/#/transfer) | [Sei Tech Chat](https://t.me/+KZdhZ1eE-G01NmZk) [Discord](https://discord.com/invite/sei) | @@ -166,7 +166,7 @@ If you can export a raw private key, import it into an EVM wallet. This always w It depends on whether your Cosmos (`sei1...`) and EVM (`0x...`) addresses are **associated (linked)**. Staking on Sei is handled by the Cosmos staking module. After SIP-03, Cosmos-native transaction interfaces will no longer be available — but the underlying staking state is preserved and accessible via EVM for associated addresses. -**If your addresses are associated:** No action is needed. After the upgrade, your existing delegations, rewards, and unbonding state will be fully accessible via EVM. You can continue managing your stake through an EVM wallet using the [Staking Precompile](/evm/precompiles/staking), the [Sei Dashboard](https://dashboard.sei.io/stake), or the [Sei Dashboard](https://dashboard.sei.io/stake). +**If your addresses are associated:** No action is needed. After the upgrade, your existing delegations, rewards, and unbonding state will be fully accessible via EVM. You can continue managing your stake through an EVM wallet using the [Staking Precompile](/evm/precompiles/staking) or the [Sei Dashboard](https://dashboard.sei.io/stake). **If your addresses are NOT associated:** You must act before the upgrade. After SIP-03, you will not be able to associate addresses or manage delegations through a Cosmos wallet. @@ -182,4 +182,4 @@ To check whether your addresses are linked, see [Query Linked Addresses](/learn/ ### I hold USDC.n (USDC via Noble) — what should I do? -You must swap or migrate your USDC.n to native USDC before the SIP-03 upgrade (expected end of March 2026). After the upgrade, USDC.n may become inaccessible or lose its value on Sei. See the [USDC on Sei](#2-usdc-on-sei) section above for swap and migration options, or read the full announcement: [Holders of USDC.n Need to Swap or Migrate](https://blog.sei.io/announcements/holders-of-usdcn-need-to-swap-or-migrate/). +You must swap or migrate your USDC.n to native USDC before the SIP-03 deprecation of Cosmos, CosmWasm and IBC related functionality, slated for **June 15, 2026**. After the upgrade, USDC.n may become inaccessible or lose its value on Sei. See the [USDC on Sei](#2-usdc-on-sei) section above for swap and migration options, or read the full announcement: [Holders of USDC.n Need to Swap or Migrate](https://blog.sei.io/announcements/holders-of-usdcn-need-to-swap-or-migrate/). diff --git a/learn/twin-turbo-consensus.mdx b/learn/twin-turbo-consensus.mdx index 6894f43..a521c95 100644 --- a/learn/twin-turbo-consensus.mdx +++ b/learn/twin-turbo-consensus.mdx @@ -44,7 +44,7 @@ This diagram illustrates the conceptual overlap. Transaction preparation feeds i This highly optimized consensus flow translates directly into tangible benefits: -- **Near-Instant Finality:** The ~400ms target block time means transactions achieve probabilistic finality almost immediately, confirmed within a single block. Deterministic finality typically follows within two blocks (~800ms). This eliminates the long confirmation waits common on other chains. +- **Near-Instant Finality:** Sei has instant, deterministic finality — a transaction is final as soon as its block is committed, within the ~400ms target block time. There is no probabilistic confirmation window or multi-block wait. This eliminates the long confirmation waits common on other chains. - **Responsive Applications:** The low latency transforms the user experience. Interactions feel instantaneous, bridging the gap between decentralized applications and traditional web services. - **Advanced Use Cases:** The speed enables applications previously impractical on-chain, such as high-frequency trading components, real-time price oracles critical for stable DeFi, and responsive on-chain games. - **Efficient Multi-Step Operations:** Complex DeFi workflows (e.g., approve-swap-stake) that involve multiple transactions can execute in sequence within roughly a second, boosting capital efficiency and simplifying user interactions. diff --git a/node/advanced-config-monitoring.mdx b/node/advanced-config-monitoring.mdx index 03a1e37..53cab97 100644 --- a/node/advanced-config-monitoring.mdx +++ b/node/advanced-config-monitoring.mdx @@ -89,17 +89,17 @@ Example Prometheus configuration: ```yaml global: - scrape_interval: 15s - evaluation_interval: 15s + scrape_interval: 15s + evaluation_interval: 15s scrape_configs: - - job_name: 'sei_node' - static_configs: - - targets: ['node1_ip:port'] - metrics_path: /metrics - - job_name: 'node' - static_configs: - - targets: ['node2_ip:port'] + - job_name: 'sei_node' + static_configs: + - targets: ['node1_ip:port'] + metrics_path: /metrics + - job_name: 'node' + static_configs: + - targets: ['node2_ip:port'] ``` ### Grafana Integration @@ -263,44 +263,44 @@ tar xvf alertmanager-0.25.0.linux-amd64.tar.gz ```yaml groups: - - name: validator_alerts - rules: - - alert: NodeDown - expr: up == 0 - for: 5m - labels: - severity: critical - annotations: - summary: 'Node {{ $labels.instance }} down' - - - alert: BlockProductionSlow - expr: rate(tendermint_consensus_height[5m]) < 0.1 - for: 5m - labels: - severity: warning - annotations: - summary: 'Block production is slow on {{ $labels.instance }}' - - alert: ValidatorMissedBlocks - expr: increase(tendermint_consensus_validator_missed_blocks[1h]) > 0 - labels: - severity: critical - annotations: - summary: 'Validator missing blocks' - - - alert: ValidatorJailed - expr: tendermint_consensus_validator_status == 0 - labels: - severity: critical - annotations: - summary: 'Validator has been jailed' - - - alert: ConsensusStalled - expr: tendermint_consensus_height_status == 0 - for: 5m - labels: - severity: critical - annotations: - summary: 'Consensus has stalled' + - name: validator_alerts + rules: + - alert: NodeDown + expr: up == 0 + for: 5m + labels: + severity: critical + annotations: + summary: 'Node {{ $labels.instance }} down' + + - alert: BlockProductionSlow + expr: rate(tendermint_consensus_height[5m]) < 0.1 + for: 5m + labels: + severity: warning + annotations: + summary: 'Block production is slow on {{ $labels.instance }}' + - alert: ValidatorMissedBlocks + expr: increase(tendermint_consensus_validator_missed_blocks[1h]) > 0 + labels: + severity: critical + annotations: + summary: 'Validator missing blocks' + + - alert: ValidatorJailed + expr: tendermint_consensus_validator_status == 0 + labels: + severity: critical + annotations: + summary: 'Validator has been jailed' + + - alert: ConsensusStalled + expr: tendermint_consensus_height_status == 0 + for: 5m + labels: + severity: critical + annotations: + summary: 'Consensus has stalled' ``` diff --git a/node/index.mdx b/node/index.mdx index a34459c..abe6820 100644 --- a/node/index.mdx +++ b/node/index.mdx @@ -172,8 +172,9 @@ sed -i 's/persistent-peers = .*/persistent-peers = "'$PEERS'"/' ~/.sei/config/co ##### Configure App Settings ```bash -# Set minimum gas price (recommended) -sed -i -e "s/^minimum-gas-prices *=.*/minimum-gas-prices = \"0usei\"/" $HOME/.sei/config/app.toml +# Set minimum gas price (recommended; helps prevent spam transactions) +# Use "0usei" only for local development or private networks +sed -i -e "s/^minimum-gas-prices *=.*/minimum-gas-prices = \"0.01usei\"/" $HOME/.sei/config/app.toml # Tune mempool settings sed -i \ @@ -305,7 +306,7 @@ Check your node's status with these commands: ```bash # Check sync status -seid status | jq .SyncInfo +seid status | jq '.sync_info' # Monitor logs in real-time journalctl -u seid -f -o cat diff --git a/node/node-operators.mdx b/node/node-operators.mdx index b763a6d..76c38ab 100644 --- a/node/node-operators.mdx +++ b/node/node-operators.mdx @@ -1527,7 +1527,7 @@ du -sh $HOME/.sei/data/ # - `config.toml` & `app.toml`: Retains node-specific configuration. # - `genesis.json`: Required for correct chain initialization. -# Compact database to optimize storage (only if needed) +# Wipe database for a fresh resync (destructive — deletes all chain data) find $HOME/.sei/data/ -mindepth 1 ! -name 'priv_validator_state.json' -delete && rm -rf $HOME/.sei/wasm @@ -1535,6 +1535,8 @@ find $HOME/.sei/data/ -mindepth 1 ! -name 'priv_validator_state.json' -delete && cp -r $HOME/.sei/data/ $HOME/sei-backup-$(date +%Y%m%d)/ ``` +The wipe command above deletes the entire local database (everything except `priv_validator_state.json`) and the `wasm` folder. It does not compact data in place — after running it, the node must be re-synced from a [snapshot](/node/snapshot) or via [state sync](/node/statesync) before it can serve traffic again. + ## Service Management ### Systemd Commands diff --git a/node/seictl.mdx b/node/seictl.mdx index 2d2516c..043927f 100644 --- a/node/seictl.mdx +++ b/node/seictl.mdx @@ -190,7 +190,7 @@ seictl genesis patch [patch-file] seictl genesis patch patch.json # Patch from stdin -echo '{"chain_id": "sei-testnet"}' | seictl genesis patch +echo '{"chain_id": "atlantic-2"}' | seictl genesis patch # Patch and save to a new file seictl genesis patch patch.json -o genesis-modified.json @@ -331,7 +331,7 @@ seictl config --target app patch patch.toml -i ```bash -echo '{"chain_id": "sei-mainnet-1"}' | seictl genesis patch -i +echo '{"chain_id": "pacific-1"}' | seictl genesis patch -i ``` diff --git a/node/statesync.mdx b/node/statesync.mdx index 78da00b..2edde39 100644 --- a/node/statesync.mdx +++ b/node/statesync.mdx @@ -32,7 +32,7 @@ seid tendermint unsafe-reset-all --home $HOME/.sei Finally, remove the existing data and wasm folders and restore the `priv_validator_state.json`: ```bash -rm -rf $HOME/.sei/data/\* +rm -rf $HOME/.sei/data/* rm -rf $HOME/.sei/wasm cp $HOME/priv_validator_state.json $HOME/.sei/data/priv_validator_state.json ``` diff --git a/node/troubleshooting.mdx b/node/troubleshooting.mdx index 2235312..7681847 100644 --- a/node/troubleshooting.mdx +++ b/node/troubleshooting.mdx @@ -17,7 +17,7 @@ When you encounter consensus errors, quick and appropriate action is essential: ```text Error: "Consensus failure - height halted" Solution: Check for network upgrades or chain halts -Command: seid status | jq .SyncInfo +Command: seid status | jq '.sync_info' ``` ```text @@ -334,7 +334,7 @@ echo "/tmp/core.%e.%p" > /proc/sys/kernel/core_pattern `app.toml` or running: ```bash - seid unsafe-reset-all --home=$HOME/.sei --keep-addr-book + seid tendermint unsafe-reset-all --home $HOME/.sei --keep-addr-book ``` Alternatively, manually remove old state snapshots to free up space: diff --git a/node/validators.mdx b/node/validators.mdx index 60d7dd3..eb979b4 100644 --- a/node/validators.mdx +++ b/node/validators.mdx @@ -59,7 +59,7 @@ yubihsm-shell # Configure seid to use HSM tee "$HOME/.sei/config/priv_validator_config.json" << EOF { - "chain_id": "sei-chain", + "chain_id": "pacific-1", "key_type": "yubihsm", "state_file": "$HOME/.sei/data/priv_validator_state.json", "hsm_serial": "YOUR_HSM_SERIAL", @@ -72,14 +72,16 @@ EOF ### Validator Registration -Before registering your validator, ensure your node is fully synced with the network. The creation of a validator is a crucial step that requires careful consideration of commission parameters: +Before registering your validator, ensure your node is fully synced with the network. The creation of a validator is a crucial step that requires careful consideration of commission parameters. + +The examples below use `pacific-1` (mainnet); use `atlantic-2` for testnet. ```bash seid tx staking create-validator \ --amount=1000000usei \ --pubkey=$(seid tendermint show-validator) \ --moniker="choose_moniker" \ - --chain-id=sei-chain \ + --chain-id=pacific-1 \ --commission-rate="0.10" \ --commission-max-rate="0.20" \ --commission-max-change-rate="0.01" \ @@ -203,7 +205,7 @@ seid query gov proposals --status voting_period # Vote on a proposal seid tx gov vote 1 yes \ --from operator \ - --chain-id sei-chain \ + --chain-id pacific-1 \ --gas auto \ --gas-prices 0.01usei ``` From 9db4ca6214f94e740cf2f4f9f8191932cfa7facc Mon Sep 17 00:00:00 2001 From: alexander-sei Date: Fri, 12 Jun 2026 16:03:47 +0200 Subject: [PATCH 2/2] docs: address PR review fixes --- evm/migrate-from-solana.mdx | 2 +- evm/precompiles/cosmwasm-precompiles/addr.mdx | 4 ++-- evm/precompiles/cosmwasm-precompiles/bank.mdx | 4 ++-- evm/precompiles/cosmwasm-precompiles/cosmwasm.mdx | 2 +- evm/precompiles/json.mdx | 9 +++++---- evm/precompiles/staking.mdx | 2 +- learn/accounts.mdx | 3 ++- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/evm/migrate-from-solana.mdx b/evm/migrate-from-solana.mdx index 6584bb8..65c0d15 100644 --- a/evm/migrate-from-solana.mdx +++ b/evm/migrate-from-solana.mdx @@ -699,7 +699,7 @@ import { expect } from 'chai'; import { network } from 'hardhat'; // Hardhat 3 exposes ethers through a network connection rather than a global import -const { ethers } = await network.getOrCreate(); +const { ethers } = await network.create(); describe('Counter', function () { it('Initializes', async function () { diff --git a/evm/precompiles/cosmwasm-precompiles/addr.mdx b/evm/precompiles/cosmwasm-precompiles/addr.mdx index f93d52f..1df9b9f 100644 --- a/evm/precompiles/cosmwasm-precompiles/addr.mdx +++ b/evm/precompiles/cosmwasm-precompiles/addr.mdx @@ -614,7 +614,7 @@ Deploy the contract: import { network } from 'hardhat'; // Hardhat 3 exposes ethers through a network connection rather than a global import -const { ethers } = await network.getOrCreate(); +const { ethers } = await network.create(); async function main() { // The signer comes from the network config (encrypted keystore) @@ -651,7 +651,7 @@ import { network } from 'hardhat'; import addressManagerAbi from './artifacts/contracts/CrossChainAddressManager.sol/CrossChainAddressManager.json' with { type: 'json' }; // Hardhat 3 exposes ethers through a network connection rather than a global import -const { ethers } = await network.getOrCreate(); +const { ethers } = await network.create(); async function main() { // The signer comes from the network config (encrypted keystore) diff --git a/evm/precompiles/cosmwasm-precompiles/bank.mdx b/evm/precompiles/cosmwasm-precompiles/bank.mdx index 9ec8bc5..1e3ba3f 100644 --- a/evm/precompiles/cosmwasm-precompiles/bank.mdx +++ b/evm/precompiles/cosmwasm-precompiles/bank.mdx @@ -1015,7 +1015,7 @@ First, deploy the contract using Hardhat: import { network } from 'hardhat'; // Hardhat 3 exposes ethers through a network connection rather than a global import -const { ethers } = await network.getOrCreate(); +const { ethers } = await network.create(); async function main() { // The signer comes from the network config (encrypted keystore) @@ -1054,7 +1054,7 @@ import { network } from 'hardhat'; import TokenManager from './artifacts/contracts/ComprehensiveTokenManager.sol/ComprehensiveTokenManager.json' with { type: 'json' }; // Hardhat 3 exposes ethers through a network connection rather than a global import -const { ethers } = await network.getOrCreate(); +const { ethers } = await network.create(); async function main() { // The signer comes from the network config (encrypted keystore) diff --git a/evm/precompiles/cosmwasm-precompiles/cosmwasm.mdx b/evm/precompiles/cosmwasm-precompiles/cosmwasm.mdx index a24b16d..2c48f06 100644 --- a/evm/precompiles/cosmwasm-precompiles/cosmwasm.mdx +++ b/evm/precompiles/cosmwasm-precompiles/cosmwasm.mdx @@ -679,7 +679,7 @@ contract CosmWasmBridge { import { network } from 'hardhat'; // Hardhat 3 exposes ethers through a network connection rather than a global import -const { ethers } = await network.getOrCreate(); +const { ethers } = await network.create(); async function main() { const [deployer] = await ethers.getSigners(); diff --git a/evm/precompiles/json.mdx b/evm/precompiles/json.mdx index 566f8b7..ca28524 100644 --- a/evm/precompiles/json.mdx +++ b/evm/precompiles/json.mdx @@ -626,11 +626,12 @@ class SeiPriceOracle { const metadataJson = await safeExtractBytes(this.jsonPrecompile, oracleResponse, 'metadata', '{}'); const metadata = JSON.parse(metadataJson); const timestamp = metadata.timestamp ?? ''; - const isValid = metadata.isValid === 1; + const isValid = metadata.isLive === 1; - // Extract the parent "prices" object the same way - const pricesJson = await safeExtractBytes(this.jsonPrecompile, oracleResponse, 'prices', '{}'); - const rawPrices = JSON.parse(pricesJson); + // Extract the parent "market" object the same way, then read its prices. + const marketJson = await safeExtractBytes(this.jsonPrecompile, oracleResponse, 'market', '{}'); + const market = JSON.parse(marketJson); + const rawPrices = market.prices ?? {}; const prices = new Map(); const assets = await safeExtractBytesList(this.jsonPrecompile, oracleResponse, 'assets'); diff --git a/evm/precompiles/staking.mdx b/evm/precompiles/staking.mdx index 24f48aa..04504d8 100644 --- a/evm/precompiles/staking.mdx +++ b/evm/precompiles/staking.mdx @@ -1300,7 +1300,7 @@ contract StakingManager { import { network } from 'hardhat'; // Hardhat 3 exposes ethers through a network connection rather than a global import -const { ethers } = await network.getOrCreate(); +const { ethers } = await network.create(); async function main() { const [deployer] = await ethers.getSigners(); diff --git a/learn/accounts.mdx b/learn/accounts.mdx index b19fdc7..732ef94 100644 --- a/learn/accounts.mdx +++ b/learn/accounts.mdx @@ -139,13 +139,14 @@ const associate = async () => { const message = 'associate'; const signature = await newAccount.signMessage({ message }); const parsedSignature = parseSignature(signature); + const customMessage = `\x19Ethereum Signed Message:\n${message.length}${message}`; const response = await client.writeContract({ account, address: ADDRESS_PRECOMPILE_ADDRESS, abi: ADDRESS_PRECOMPILE_ABI, functionName: 'associate', - args: [toHex(Number(parsedSignature.v) - 27), parsedSignature.r, parsedSignature.s, message], + args: [toHex(Number(parsedSignature.v) - 27), parsedSignature.r, parsedSignature.s, customMessage], gasPrice: BigInt(100_000_000_000) }); console.log(response);