From 454dbf4699fdaa8c7442224285ab6dd4d4efaa4d Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 19:47:59 -0400 Subject: [PATCH 01/25] Add shared docs components --- mdx-components.js | 150 ++++++++++++++++++++++++++++- src/components/CopyableAddress.tsx | 37 +++++++ 2 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/components/CopyableAddress.tsx diff --git a/mdx-components.js b/mdx-components.js index 62c5808a..ea636068 100644 --- a/mdx-components.js +++ b/mdx-components.js @@ -2,9 +2,157 @@ import { useMDXComponents as getThemeComponents } from 'nextra-theme-docs'; const themeComponents = getThemeComponents(); +export function KeyValueTable({ rows = [] }) { + return ( +
+ + + {rows.map(([k, v], i) => ( + + + + + ))} + +
+ {k} + {v}
+
+ ); +} + +export function CardGrid({ items = [] }) { + return ( +
+ {items.map((it, i) => ( + +
+ {it.title} +
+
{it.description}
+
+ ))} +
+ ); +} + +export function FunctionList({ items = [] }) { + return ( +
+ + + + + + + + + {items.map((item, idx) => ( + + + + + ))} + +
FunctionDescription
+
{item.name}
+ {item.signature ? {item.signature} : null} +
{item.description}
+
+ ); +} + +export function TroubleshootingTable({ rows = [] }) { + return ( +
+ + + + + + + + + + {rows.map(([error, cause, fix], i) => ( + + + + + + ))} + +
ErrorCauseFix
+ {error} + {cause}{fix}
+
+ ); +} + +export function VersionTimeline({ releases = [] }) { + return ( +
+ + + + + + + + + {releases.map((release, idx) => ( + + + + + ))} + +
VersionChanges
+ + {release.version} + + +
    + {release.changes.map((change, i) => { + const parts = change.split(/(`[^`]+`)/g); + return ( +
  • + + + {parts.map((part, j) => + part.startsWith('`') && part.endsWith('`') ? ( + + {part.slice(1, -1)} + + ) : ( + {part} + ) + )} + +
  • + ); + })} +
+
+
+ ); +} + export function useMDXComponents(components) { return { ...themeComponents, - ...components + ...components, + KeyValueTable, + CardGrid, + FunctionList, + TroubleshootingTable, + VersionTimeline }; } diff --git a/src/components/CopyableAddress.tsx b/src/components/CopyableAddress.tsx new file mode 100644 index 00000000..0bf687e8 --- /dev/null +++ b/src/components/CopyableAddress.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { useState } from 'react'; + +export function CopyableAddress({ address }: { address: string }) { + const [copied, setCopied] = useState(false); + + const handleCopy = () => { + navigator.clipboard.writeText(address); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( + + ); +} From d175c95712d054cd4129dbce4762574f751c8aa0 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 19:48:09 -0400 Subject: [PATCH 02/25] Restyle callouts --- app/globals.css | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/app/globals.css b/app/globals.css index 0346d128..d29feee1 100644 --- a/app/globals.css +++ b/app/globals.css @@ -253,3 +253,134 @@ aside, .nextra-nav-container a.nx-me-auto { margin-inline-end: 0 !important; } + +/* Custom Callout Styling */ +.nextra-callout { + position: relative !important; + border-radius: 0.5rem !important; + border: 1px solid !important; + padding: 0.875rem 1rem !important; + font-size: 0.875rem !important; + line-height: 1.6 !important; + margin: 1.25rem 0 !important; +} + +.nextra-callout > *:first-child { + margin-top: 0 !important; +} + +.nextra-callout > *:last-child { + margin-bottom: 0 !important; +} + +/* Hide default Nextra icons */ +.nextra-callout svg { + display: none !important; +} + +/* Add type label badge */ +.nextra-callout::before { + content: ''; + position: absolute; + top: 0.875rem; + left: 1rem; + padding: 0.125rem 0.5rem; + font-size: 0.625rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + border-radius: 0.25rem; + margin-bottom: 0.5rem; +} + +/* Add padding to content to account for label */ +.nextra-callout > div { + padding-left: 4.5rem !important; + min-height: 1.5rem !important; +} + +/* Info callout */ +.nextra-callout.nx-border-blue-200 { + border-color: rgb(59 130 246 / 0.3) !important; + background: rgb(239 246 255 / 0.4) !important; + color: rgb(30 64 175) !important; +} + +.nextra-callout.nx-border-blue-200::before { + content: 'INFO'; + background: rgb(59 130 246) !important; + color: white !important; +} + +.dark .nextra-callout.nx-border-blue-200 { + border-color: rgb(59 130 246 / 0.3) !important; + background: rgb(30 58 138 / 0.1) !important; + color: rgb(219 234 254) !important; +} + +.dark .nextra-callout.nx-border-blue-200::before { + background: rgb(37 99 235) !important; + color: rgb(219 234 254) !important; +} + +/* Warning callout */ +.nextra-callout.nx-border-orange-200 { + border-color: rgb(245 158 11 / 0.3) !important; + background: rgb(254 243 199 / 0.4) !important; + color: rgb(146 64 14) !important; +} + +.nextra-callout.nx-border-orange-200::before { + content: 'WARNING'; + background: rgb(245 158 11) !important; + color: white !important; +} + +.dark .nextra-callout.nx-border-orange-200 { + border-color: rgb(245 158 11 / 0.3) !important; + background: rgb(120 53 15 / 0.1) !important; + color: rgb(254 243 199) !important; +} + +.dark .nextra-callout.nx-border-orange-200::before { + background: rgb(217 119 6) !important; + color: rgb(254 243 199) !important; +} + +/* Error callout */ +.nextra-callout.nx-border-red-200 { + border-color: rgb(239 68 68 / 0.3) !important; + background: rgb(254 242 242 / 0.4) !important; + color: rgb(153 27 27) !important; +} + +.nextra-callout.nx-border-red-200::before { + content: 'ERROR'; + background: rgb(239 68 68) !important; + color: white !important; +} + +.dark .nextra-callout.nx-border-red-200 { + border-color: rgb(239 68 68 / 0.3) !important; + background: rgb(127 29 29 / 0.1) !important; + color: rgb(254 226 226) !important; +} + +.dark .nextra-callout.nx-border-red-200::before { + background: rgb(220 38 38) !important; + color: rgb(254 226 226) !important; +} + +/* Style inline code within callouts */ +.nextra-callout code { + background: rgb(0 0 0 / 0.08) !important; + color: inherit !important; + padding: 0.125rem 0.375rem !important; + border-radius: 0.25rem !important; + font-size: 0.8125rem !important; + font-weight: 500 !important; +} + +.dark .nextra-callout code { + background: rgb(255 255 255 / 0.1) !important; +} From a927f672a83e8978c055aaa78baefe9d148a020c Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 19:48:35 -0400 Subject: [PATCH 03/25] Rework precompile docs --- content/evm/_meta.js | 123 +- content/evm/precompile-P256.mdx | 95 ++ content/evm/precompile-addr.mdx | 99 ++ content/evm/precompile-bank.mdx | 111 ++ content/evm/precompile-distribution.mdx | 122 ++ ...usage.mdx => precompile-example-usage.mdx} | 0 content/evm/precompile-governance.mdx | 137 ++ content/evm/precompile-ibc.mdx | 103 ++ content/evm/precompile-json.mdx | 116 ++ content/evm/precompile-oracle.mdx | 122 ++ content/evm/precompile-pointer.mdx | 92 ++ content/evm/precompile-pointerview.mdx | 90 ++ content/evm/precompile-solo.mdx | 110 ++ content/evm/precompile-staking.mdx | 172 +++ content/evm/precompile-version-matrix.mdx | 208 +++ content/evm/precompiles/P256.mdx | 418 ------ content/evm/precompiles/_meta.js | 9 - content/evm/precompiles/distribution.mdx | 309 ---- content/evm/precompiles/governance.mdx | 1294 ----------------- content/evm/precompiles/json.mdx | 831 ----------- content/evm/precompiles/oracle.mdx | 720 --------- content/evm/precompiles/staking.mdx | 871 ----------- 22 files changed, 1645 insertions(+), 4507 deletions(-) create mode 100644 content/evm/precompile-P256.mdx create mode 100644 content/evm/precompile-addr.mdx create mode 100644 content/evm/precompile-bank.mdx create mode 100644 content/evm/precompile-distribution.mdx rename content/evm/{precompiles/example-usage.mdx => precompile-example-usage.mdx} (100%) create mode 100644 content/evm/precompile-governance.mdx create mode 100644 content/evm/precompile-ibc.mdx create mode 100644 content/evm/precompile-json.mdx create mode 100644 content/evm/precompile-oracle.mdx create mode 100644 content/evm/precompile-pointer.mdx create mode 100644 content/evm/precompile-pointerview.mdx create mode 100644 content/evm/precompile-solo.mdx create mode 100644 content/evm/precompile-staking.mdx create mode 100644 content/evm/precompile-version-matrix.mdx delete mode 100644 content/evm/precompiles/P256.mdx delete mode 100644 content/evm/precompiles/_meta.js delete mode 100644 content/evm/precompiles/distribution.mdx delete mode 100644 content/evm/precompiles/governance.mdx delete mode 100644 content/evm/precompiles/json.mdx delete mode 100644 content/evm/precompiles/oracle.mdx delete mode 100644 content/evm/precompiles/staking.mdx diff --git a/content/evm/_meta.js b/content/evm/_meta.js index 561b2420..f15b6329 100644 --- a/content/evm/_meta.js +++ b/content/evm/_meta.js @@ -9,33 +9,27 @@ export default { networks: 'Network Information', 'differences-with-ethereum': 'Divergence from Ethereum', - '-- seid CLI': { - type: 'separator', - title: 'seid CLI' - }, - 'installing-seid-cli': 'Installing seid CLI', - 'querying-the-evm': 'Querying the EVM', - 'transactions-with-seid': 'Transactions with seid', - 'best-practices': 'Best Practices', - '-- Frontend Development': { type: 'separator', title: 'Frontend Development' }, - 'sei-global-wallet': { - title: 'Sei Global Wallet' + 'seijs-sdk': { + title: '@sei-js SDK ↗', + href: 'https://sei-js.docs.sei.io/introduction' }, + 'sei-global-wallet': 'Sei Global Wallet', 'building-a-frontend': 'Building a Frontend', + 'ledger-ethers': 'Ledger with Ethers', '-- Smart Contracts': { type: 'separator', title: 'Smart Contracts' }, 'evm-general': 'EVM (General)', - 'evm-hardhat': 'EVM with Hardhat', - 'evm-foundry': 'EVM with Foundry', + 'evm-hardhat': 'Hardhat', + 'evm-foundry': 'Foundry', 'evm-wizard': { - title: 'EVM Contract Wizard', + title: 'Contract Wizard', theme: { sidebar: true, toc: false, @@ -44,66 +38,85 @@ export default { }, 'solidity-resources': 'Solidity Resources', 'optimizing-for-parallelization': 'Optimizing for Parallelization', - 'debugging-contracts': 'Debugging for EVM', - tracing: 'Debug Tracing', + 'debugging-contracts': 'Debugging', 'evm-verify-contracts': 'Verify Contracts', - precompiles: 'Precompiles', - '-- Sei-js': { + '-- Precompiles': { type: 'separator', - title: 'sei-js Library' - }, - 'seijs-introduction': { - title: 'Introduction to sei-js', - href: 'https://sei-js.docs.sei.io/introduction' - }, - 'scaffold-sei': { - title: 'Scaffold Sei', - href: 'https://sei-js.docs.sei.io/create-sei' + title: 'Precompiles' + }, + 'precompile-version-matrix': 'Version Matrix', + 'precompile-example-usage': 'Example Usage', + '---precompiles': { type: 'separator' }, + 'precompile-addr': 'Addr', + 'precompile-bank': 'Bank', + 'precompile-staking': 'Staking', + 'precompile-distribution': 'Distribution', + 'precompile-governance': 'Governance', + 'precompile-oracle': 'Oracle', + 'precompile-ibc': 'IBC', + 'precompile-pointer': 'Pointer', + 'precompile-pointerview': 'PointerView', + 'precompile-solo': 'Solo', + 'precompile-json': 'JSON', + 'precompile-P256': 'P256', + + '-- RPC': { + type: 'separator', + title: 'RPC' }, - 'mcp-server-seijs': { - title: 'MCP Server', - href: 'https://sei-js.docs.sei.io/mcp-server' + reference: 'RPC Reference', + 'rpc-websockets': 'WebSockets', + + '-- Tracing': { + type: 'separator', + title: 'Tracing' }, - 'sei-x402': { - title: 'X402', - href: 'https://sei-js.docs.sei.io/x402' + 'tracing-overview': 'Overview', + 'tracing-playbook': 'Playbook', + 'tracing-javascript-tracers': 'JavaScript Tracers', + 'tracing-troubleshooting': 'Troubleshooting', + + '-- Indexing': { + type: 'separator', + title: 'Indexing' }, - 'seijs-ledger': { - title: 'Ledger', - href: 'https://sei-js.docs.sei.io/ledger' + 'indexing-best-practices': 'Best Practices', + 'indexer-providers': 'Indexer Providers', + + '-- Advanced': { + type: 'separator', + title: 'Advanced' }, + 'pointers-deep-dive': 'Pointers', + transactions: 'Transactions', + 'ibc-protocol': 'IBC on EVM', + 'cosmwasm-precompiles': 'CosmWasm Precompiles', - '-- Ecosystem Tutorials': { + '-- Ecosystem': { type: 'separator', - title: 'Ecosystem Tutorials' + title: 'Ecosystem' }, - 'indexer-providers': 'Indexers', 'wallet-integrations': 'Wallet Integrations', bridging: 'Bridging', 'ai-tooling': 'AI Tooling', 'usdc-on-sei': 'USDC on Sei', - '-- Reference': { - type: 'separator', - title: 'Reference' - }, - transactions: 'Transaction Overview', - reference: 'RPC Reference', - tokens: 'View Tokens', - changelog: 'Changelog', - 'dappradar-guide': 'DappRadar Guide', 'ecosystem-contracts': 'Ecosystem Contracts', + 'dappradar-guide': 'DappRadar', - '-- Hardware Wallets': { + '-- CLI Tools': { type: 'separator', - title: 'Hardware Wallets' + title: 'CLI Tools' }, - 'ledger-ethers': 'Using Ledger with Ethers', + 'installing-seid-cli': 'Installing seid', + 'querying-the-evm': 'Querying', + 'transactions-with-seid': 'Transactions', + 'best-practices': 'Best Practices', - '-- CosmWasm Interoperability': { + '-- Meta': { type: 'separator', - title: 'CosmWasm Interoperability' + title: 'Meta' }, - 'cosmwasm-precompiles': 'Precompiles', - 'ibc-protocol': 'IBC on EVM' + tokens: 'View Tokens', + changelog: 'Changelog' }; diff --git a/content/evm/precompile-P256.mdx b/content/evm/precompile-P256.mdx new file mode 100644 index 00000000..899b79d8 --- /dev/null +++ b/content/evm/precompile-P256.mdx @@ -0,0 +1,95 @@ +--- +title: 'P256 Precompile' +description: 'Verify secp256r1/P-256 signatures from EVM contracts for passkey and hardware wallet authentication.' +keywords: ['p256', 'secp256r1', 'signature verification', 'passkey', 'webauthn'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# P256 Precompile + +**Address:** `0x0000000000000000000000000000000000001011` + +Verify secp256r1 (P-256) signatures on-chain for passkey, WebAuthn, and hardware wallet integration. + +Implementation of [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md). Up to 60x more gas-efficient than Solidity-based verification. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +interface IP256Verify { + function verify(bytes calldata input) external view returns (bytes memory response); +} +``` + +**Input format** (160 bytes total): + +- Bytes 0-31: message hash +- Bytes 32-63: signature `r` component +- Bytes 64-95: signature `s` component +- Bytes 96-127: public key `x` coordinate +- Bytes 128-159: public key `y` coordinate + +
+ +## Example + +```typescript copy +import { ethers } from 'ethers'; + +const P256 = '0x0000000000000000000000000000000000001011'; +const ABI = ['function verify(bytes input) view returns (bytes)']; + +const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); +const p256 = new ethers.Contract(P256, ABI, provider); + +// Prepare input (message hash + r + s + publicKeyX + publicKeyY) +const input = ethers.concat([messageHash, r, s, publicKeyX, publicKeyY]); +const result = await p256.verify(input); + +// Non-zero result = valid signature +const isValid = result !== ethers.ZeroHash; +``` + +## Use Cases + +- **PassKeys/WebAuthn**: Authenticate users via device biometrics +- **Apple Secure Enclave**: Verify signatures from iOS/macOS hardware keys +- **Android Keychain**: Support hardware-backed authentication +- **HSMs**: Integrate with enterprise security modules + +## Notes + +- Gas cost: ~48,000 per verification (300 gas/byte × 160 bytes) +- Input length must be exactly 160 bytes; shorter/longer inputs revert +- Returns `bytes32(0)` on verification failure or malformed input +- Public key validation is implicit; invalid curve points are rejected + +## Troubleshooting + + + +## References + +- ABI: [github.com/sei-protocol/sei-chain/precompiles/p256](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/p256) +- RIP-7212: [github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md) diff --git a/content/evm/precompile-addr.mdx b/content/evm/precompile-addr.mdx new file mode 100644 index 00000000..25a2f3a7 --- /dev/null +++ b/content/evm/precompile-addr.mdx @@ -0,0 +1,99 @@ +--- +title: 'Addr Precompile' +description: 'Resolve and work with Sei address formats from EVM.' +keywords: ['addr', 'address', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# Addr Precompile + +**Address:** `0x0000000000000000000000000000000000001004` + +Resolve associations between Sei bech32 accounts and EVM addresses. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +interface IAddrPrecompile { + function getSeiAddr(address addr) external view returns (string memory response); + function getEvmAddr(string memory addr) external view returns (address response); + function associate(string memory v, string memory r, string memory s, string memory customMessage) external returns (string memory seiAddr, address evmAddr); + function associatePubKey(string memory pubKeyHex) external returns (string memory seiAddr, address evmAddr); +} +``` + +
+ +## Example + +```typescript copy +import { ethers } from 'ethers'; + +const ADDR = '0x0000000000000000000000000000000000001004'; +const ABI = ['function getSeiAddr(address addr) view returns (string)', 'function associate(string v, string r, string s, string customMessage) returns (string seiAddr, address evmAddr)']; + +const provider = new ethers.BrowserProvider(window.ethereum); +await provider.send('eth_requestAccounts', []); +const addr = new ethers.Contract(ADDR, ABI, provider); + +const signer = await provider.getSigner(); +const seiAddr = await addr.getSeiAddr(await signer.getAddress()); + +if (!seiAddr) { + // Acquire payload via official helper (returns v,r,s,message) + const { v, r, s, message } = await buildAssociationPayload(); + await addr.associate(v, r, s, message); +} +``` + +## Notes + +- Association payloads must be generated client-side with the legacy Sei key; invalid signatures revert +- `associate` is non-payable and rejects any `msg.value` +- Use `getSeiAddr`/`getEvmAddr` to verify linkage before invoking Pointer or Solo precompiles +- v6.1.11 improves gas accounting for association lookups in `eth_estimateGas` + +## Troubleshooting + + + +## References + +- ABI: [github.com/sei-protocol/sei-chain/precompiles/addr](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/addr) +- Association tooling: [`@sei-js/precompiles`](https://www.npmjs.com/package/@sei-js/precompiles) diff --git a/content/evm/precompile-bank.mdx b/content/evm/precompile-bank.mdx new file mode 100644 index 00000000..5f5a3997 --- /dev/null +++ b/content/evm/precompile-bank.mdx @@ -0,0 +1,111 @@ +--- +title: 'Bank Precompile' +description: 'Transfer and query native balances from EVM.' +keywords: ['bank', 'balances', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# Bank Precompile + +**Address:** `0x0000000000000000000000000000000000001001` + +Transfer native/cw-denom balances and inspect supplies from EVM contracts. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +struct Coin { + uint256 amount; + string denom; +} + +interface IBankPrecompile { + function send(address fromAddress, address toAddress, string memory denom, uint256 amount) external returns (bool success); + function sendNative(string memory toNativeAddress) external payable returns (bool success); + function balance(address account, string memory denom) external view returns (uint256 amount); + function all_balances(address account) external view returns (Coin[] memory response); + function name(string memory denom) external view returns (string memory response); + function symbol(string memory denom) external view returns (string memory response); + function decimals(string memory denom) external view returns (uint8 response); + function supply(string memory denom) external view returns (uint256 response); +} +``` + +
+ +## Example + +```typescript copy +import { ethers } from 'ethers'; + +const BANK = '0x0000000000000000000000000000000000001001'; +const ABI = ['function sendNative(string toNativeAddress) payable returns (bool)', 'function balance(address account, string denom) view returns (uint256)']; + +const provider = new ethers.BrowserProvider(window.ethereum); +await provider.send('eth_requestAccounts', []); +const bank = new ethers.Contract(BANK, ABI, await provider.getSigner()); + +// Send 1 SEI to a bech32 recipient (value supplied in wei) +await bank.sendNative('sei1recipient...', { value: ethers.parseUnits('1', 18) }); + +// Check remaining balance for a CW20 denom +const bal = await bank.balance(await provider.getSigner().then((s) => s.getAddress()), 'cw20:sei1cw20...'); +console.log('Balance (raw units):', bal.toString()); +``` + +## Notes + +- `send` rejects calls where `msg.sender` is not the pointer registered for `denom` +- `sendNative` cannot be delegatecalled; revert occurs when invoked through `delegatecall` +- Zero-amount transfers short-circuit with `true` without consuming additional gas +- Synthetic bank logs include `synthetic=true` on v6.1.11+ for indexers + +## Troubleshooting + +, etc.) before sending.'], + ['set value field to non-zero', 'sendNative called with zero msg.value', 'Provide a non-zero wei amount for the transfer.'] + ]} +/> + +## References + +- ABI: [github.com/sei-protocol/sei-chain/precompiles/bank](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/bank) +- Pointer overview: [/evm/precompiles/pointer](/evm/precompiles/pointer) diff --git a/content/evm/precompile-distribution.mdx b/content/evm/precompile-distribution.mdx new file mode 100644 index 00000000..b6545f84 --- /dev/null +++ b/content/evm/precompile-distribution.mdx @@ -0,0 +1,122 @@ +--- +title: 'Distribution Precompile' +description: "Manage staking rewards and validator commissions through Sei's distribution precompile. EVM applications can withdraw rewards, set withdrawal addresses, and interact with Cosmos SDK distribution functionality." +keywords: ['distribution precompile', 'staking rewards', 'validator commissions', 'sei rewards', 'evm staking', 'cosmos distribution'] +--- + +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# Distribution Precompile + +**Address:** `0x0000000000000000000000000000000000001007` + +Manage staking rewards, validator commissions, and withdraw routing from EVM contracts. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +struct Coin { + uint256 amount; + uint256 decimals; + string denom; +} + +struct Reward { + Coin[] coins; + string validator_address; +} + +struct Rewards { + Reward[] rewards; + Coin[] total; +} + +interface IDistributionPrecompile { + function setWithdrawAddress(address withdrawAddr) external returns (bool success); + function withdrawDelegationRewards(string memory validator) external returns (bool success); + function withdrawMultipleDelegationRewards(string[] memory validators) external returns (bool success); + function withdrawValidatorCommission(string memory validator) external returns (bool success); + function rewards(address delegator) external view returns (Rewards memory response); +} +``` + +
+ +## Example + +```typescript copy +import { ethers } from 'ethers'; + +const DISTRIBUTION = '0x0000000000000000000000000000000000001007'; +const ABI = ['function setWithdrawAddress(address withdrawAddr) returns (bool)', 'function withdrawMultipleDelegationRewards(string[] validators) returns (bool)', 'function rewards(address delegator) view returns (tuple(tuple(uint256 amount, uint256 decimals, string denom)[] coins, string validator_address)[] rewards, tuple(uint256 amount, uint256 decimals, string denom)[] total)']; + +const provider = new ethers.BrowserProvider(window.ethereum); +await provider.send('eth_requestAccounts', []); +const signer = await provider.getSigner(); +const distribution = new ethers.Contract(DISTRIBUTION, ABI, signer); + +// Redirect rewards to a treasury contract +await distribution.setWithdrawAddress('0xYourTreasuryAddress'); + +// Withdraw rewards across two validators +await distribution.withdrawMultipleDelegationRewards(['seivaloper1...', 'seivaloper1...']); + +// Inspect totals (amounts are raw; divide by decimals for display) +const rewards = await distribution.rewards(await signer.getAddress()); +console.log(rewards.total); +``` + +## Notes + +- Withdraw calls revert during `staticcall`; they must be regular transactions +- Commission withdrawals succeed only when `msg.sender` matches the validator operator address +- Returned amounts are raw integers—divide by `decimals` before converting to display units +- v6.1.11+ emits synthetic distribution logs tagged with `synthetic=true` + +## Troubleshooting + + + +## References + +- ABI: [github.com/sei-protocol/sei-chain/precompiles/distribution](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/distribution) +- Staking guide: [/learn/general-staking](/learn/general-staking) diff --git a/content/evm/precompiles/example-usage.mdx b/content/evm/precompile-example-usage.mdx similarity index 100% rename from content/evm/precompiles/example-usage.mdx rename to content/evm/precompile-example-usage.mdx diff --git a/content/evm/precompile-governance.mdx b/content/evm/precompile-governance.mdx new file mode 100644 index 00000000..15d0f23a --- /dev/null +++ b/content/evm/precompile-governance.mdx @@ -0,0 +1,137 @@ +--- +title: 'Governance Precompile' +description: 'Submit proposals, vote, and deposit to Sei governance from EVM contracts.' +keywords: ['governance', 'proposals', 'voting', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# Governance Precompile + +**Address:** `0x0000000000000000000000000000000000001006` + +Interact with Sei's on-chain governance system from EVM contracts: submit proposals, cast votes, and add deposits. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +/// Submit a new governance proposal with JSON-encoded details. +/// @param proposalJSON JSON string: { "title": "...", "description": "...", "type": "Text", "is_expedited": false } +/// @return proposalID The ID of the created proposal +function submitProposal(string calldata proposalJSON) payable external returns (uint64 proposalID); + +/// Add a deposit to an existing proposal. +/// @param proposalID The ID of the proposal to deposit to +/// @return success Whether the deposit was accepted +function deposit(uint64 proposalID) payable external returns (bool success); + +/// Cast a vote on a proposal. +/// @param proposalID The ID of the proposal +/// @param option Vote option: 1=Yes, 2=Abstain, 3=No, 4=NoWithVeto +/// @return success Whether the vote was cast +function vote(uint64 proposalID, int32 option) external returns (bool success); + +struct WeightedVoteOption { + int32 option; // 1=Yes, 2=Abstain, 3=No, 4=NoWithVeto + string weight; // Decimal string (e.g., "0.7"); must sum to 1.0 across all options +} + +/// Cast a weighted vote (split voting power across multiple options). +/// @param proposalID The ID of the proposal +/// @param options Array of weighted vote options (MUST sum to exactly 1.0) +/// @return success Whether the vote was cast +function voteWeighted(uint64 proposalID, WeightedVoteOption[] calldata options) external returns (bool success); +``` + +
+ +## Example (submit proposal) + +```typescript copy +import { ethers } from 'ethers'; + +const GOV = '0x0000000000000000000000000000000000001006'; +const ABI = ['function submitProposal(string proposalJSON) payable returns (uint64)', 'function vote(uint64 proposalID, int32 option) returns (bool)', 'function deposit(uint64 proposalID) payable returns (bool)', 'function voteWeighted(uint64 proposalID, tuple(int32 option, string weight)[] options) returns (bool)']; + +const provider = new ethers.BrowserProvider(window.ethereum); +await provider.send('eth_requestAccounts', []); +const gov = new ethers.Contract(GOV, ABI, await provider.getSigner()); + +// Submit proposal with 3,500 SEI minimum deposit +const proposal = JSON.stringify({ + title: 'Parameter Update Proposal', + description: 'Adjust gas limits for improved performance.', + type: 'Text', + is_expedited: false +}); + +const tx = await gov.submitProposal(proposal, { value: ethers.parseEther('3500') }); +const receipt = await tx.wait(); +console.log('Proposal submitted:', receipt); +``` + +## Example (vote) + +```typescript copy +// Vote Yes on proposal #1 +await gov.vote(1n, 1); + +// Weighted vote (70% Yes, 30% Abstain) +await gov.voteWeighted(1n, [ + { option: 1, weight: '0.7' }, + { option: 2, weight: '0.3' } +]); +``` + +## Key Requirements + +- **Minimum deposit**: 3,500 SEI for standard proposals; 7,000 SEI for expedited +- **Voting power**: Must stake SEI with validators to participate +- **Weighted votes**: Options must sum to exactly 1.0 or transaction fails +- **Deposit period**: 2 days; proposals failing to reach minimum are rejected +- **Voting period**: 3 days (1 day for expedited) + +## Troubleshooting + + + +## References + +- ABI: [github.com/sei-protocol/sei-chain/precompiles/gov](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/gov) +- Governance guide: [/learn/general-governance](/learn/general-governance) diff --git a/content/evm/precompile-ibc.mdx b/content/evm/precompile-ibc.mdx new file mode 100644 index 00000000..39932335 --- /dev/null +++ b/content/evm/precompile-ibc.mdx @@ -0,0 +1,103 @@ +--- +title: 'IBC Precompile' +description: 'Initiate IBC transfers from EVM smart contracts.' +keywords: ['ibc', 'interchain', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# IBC Precompile + +**Address:** `0x0000000000000000000000000000000000001009` + +Initiate IBC transfers from EVM contracts to other IBC-enabled chains. + +Under SIP-3, inbound IBC transfers to legacy Cosmos addresses will be disabled, but **outbound** IBC transfers from EVM remain supported via this precompile. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +interface IIBCPrecompile { + function transfer( + string memory toAddress, + string memory port, + string memory channel, + string memory denom, + uint256 amount, + uint64 revisionNumber, + uint64 revisionHeight, + uint64 timeoutTimestamp, + string memory memo + ) external returns (bool success); + + function transferWithDefaultTimeout( + string memory toAddress, + string memory port, + string memory channel, + string memory denom, + uint256 amount, + string memory memo + ) external returns (bool success); +} +``` + +
+ +## Example + +```ts copy +import { ethers } from 'ethers'; +const IBC = '0x0000000000000000000000000000000000001009'; +const ABI = ['function transferWithDefaultTimeout(string toAddress, string port, string channel, string denom, uint256 amount, string memo) returns (bool)', 'function transfer(string toAddress, string port, string channel, string denom, uint256 amount, uint64 revisionNumber, uint64 revisionHeight, uint64 timeoutTimestamp, string memo) returns (bool)']; + +const provider = new ethers.BrowserProvider(window.ethereum); +await provider.send('eth_requestAccounts', []); +const ibc = new ethers.Contract(IBC, ABI, await provider.getSigner()); + +// Send 1 SEI to Osmosis +const tx = await ibc.transferWithDefaultTimeout( + 'osmo1recipient...', + 'transfer', + 'channel-0', + 'usei', + ethers.parseUnits('1', 6), // 1 SEI = 1_000_000 usei + '' // empty memo +); +await tx.wait(); +``` + +## Considerations + +- Requires active relayer between source and target chains +- Use `transferWithDefaultTimeout` for most cases (simplifies timeout management) +- For custom timeouts, use `transfer` and specify revision number/height + timestamp +- Ensure the channel is open and relayers are operational before attempting transfers + +## Troubleshooting + + diff --git a/content/evm/precompile-json.mdx b/content/evm/precompile-json.mdx new file mode 100644 index 00000000..48bd1742 --- /dev/null +++ b/content/evm/precompile-json.mdx @@ -0,0 +1,116 @@ +--- +title: 'JSON Precompile' +description: 'Parse and extract data from JSON payloads in EVM contracts.' +keywords: ['json', 'parsing', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# JSON Precompile + +**Address:** `0x0000000000000000000000000000000000001003` + +Parse JSON strings and extract typed data (bytes, uint256, arrays) from EVM contracts without external libraries. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +/// Extract data as bytes using a JSON key. +/// @param input JSON data as bytes +/// @param key JSON key to extract +/// @return response Extracted bytes +function extractAsBytes(bytes memory input, string memory key) external view returns (bytes memory response); + +/// Extract an array of bytes using a JSON key. +/// @param input JSON data as bytes +/// @param key JSON key to extract +/// @return response Array of extracted bytes +function extractAsBytesList(bytes memory input, string memory key) external view returns (bytes[] memory response); + +/// Extract data as uint256 using a JSON key. +/// @param input JSON data as bytes +/// @param key JSON key to extract +/// @return response Extracted uint256 +function extractAsUint256(bytes memory input, string memory key) external view returns (uint256 response); + +/// Extract bytes from a JSON array by index. +/// @param input JSON data as bytes +/// @param arrayIndex Zero-based array index +/// @return response Extracted bytes from array element +function extractAsBytesFromArray(bytes memory input, uint16 arrayIndex) external view returns (bytes memory response); +``` + +
+ +## Example + +```typescript copy +import { ethers } from 'ethers'; + +const JSON_PRECOMPILE = '0x0000000000000000000000000000000000001003'; +const ABI = ['function extractAsBytes(bytes input, string key) view returns (bytes)', 'function extractAsUint256(bytes input, string key) view returns (uint256)', 'function extractAsBytesList(bytes input, string key) view returns (bytes[])', 'function extractAsBytesFromArray(bytes input, uint16 arrayIndex) view returns (bytes)']; + +const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); +const json = new ethers.Contract(JSON_PRECOMPILE, ABI, provider); + +// Parse JSON response +const jsonData = ethers.toUtf8Bytes('{"price": "1.5", "assets": ["SEI", "USDC"]}'); + +const priceBytes = await json.extractAsBytes(jsonData, 'price'); +const price = ethers.toUtf8String(priceBytes); // "1.5" + +const priceUint = await json.extractAsUint256(jsonData, 'price'); // 1 (truncated) + +const assetsBytes = await json.extractAsBytesList(jsonData, 'assets'); +const assets = assetsBytes.map((b) => ethers.toUtf8String(b)); // ["SEI", "USDC"] +``` + +## Key Notes + +- Input must be valid UTF-8 encoded JSON bytes +- Returns empty bytes for missing keys (no revert) +- `extractAsUint256` truncates decimals; use `extractAsBytes` + parse for precision +- Nested keys use dot notation: `"data.user.balance"` + +## Troubleshooting + += array length', 'Query array length first or use extractAsBytesList for all elements.'] + ]} +/> + +## References + +- ABI: [github.com/sei-protocol/sei-chain/precompiles/json](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/json) diff --git a/content/evm/precompile-oracle.mdx b/content/evm/precompile-oracle.mdx new file mode 100644 index 00000000..0052a964 --- /dev/null +++ b/content/evm/precompile-oracle.mdx @@ -0,0 +1,122 @@ +--- +title: 'Oracle Precompile' +description: 'Access Sei oracle price feeds from EVM contracts.' +keywords: ['oracle', 'price feeds', 'twap', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# Oracle Precompile + +**Address:** `0x0000000000000000000000000000000000001006` + +Query real-time exchange rates and time-weighted average prices (TWAP) from Sei's native oracle directly in EVM contracts. + +Prices are USD-denominated decimal strings (e.g., "1.234567" = $1.234567). TWAP lookback limited to 60s–86,400s (1 min to 24 hours). + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +struct OracleExchangeRate { + string exchangeRate; + string lastUpdate; + int64 lastUpdateTimestamp; +} + +struct DenomOracleExchangeRatePair { + string denom; + OracleExchangeRate oracleExchangeRateVal; +} + +/// Get exchange rates for all supported denoms. +/// @return Array of denom + rate pairs +function getExchangeRates() external view returns (DenomOracleExchangeRatePair[] memory); + +struct OracleTwap { + string denom; + string twap; + int64 lookbackSeconds; +} + +/// Get TWAP for all denoms over a lookback period. +/// @param lookback_seconds Lookback in seconds (60–86,400) +/// @return Array of denom + TWAP pairs +function getOracleTwaps(uint64 lookback_seconds) external view returns (OracleTwap[] memory); +``` + +
+ +## Example + +```typescript copy +import { ethers } from 'ethers'; + +const ORACLE = '0x0000000000000000000000000000000000001006'; +const ABI = ['function getExchangeRates() view returns (tuple(string denom, tuple(string exchangeRate, string lastUpdate, int64 lastUpdateTimestamp) oracleExchangeRateVal)[])', 'function getOracleTwaps(uint64 lookback_seconds) view returns (tuple(string denom, string twap, int64 lookbackSeconds)[])']; + +const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); +const oracle = new ethers.Contract(ORACLE, ABI, provider); + +// Get current exchange rates +const rates = await oracle.getExchangeRates(); +const seiRate = rates.find((r) => r.denom === 'usei'); +console.log('SEI price:', seiRate.oracleExchangeRateVal.exchangeRate); + +// Get 1-hour TWAP +const twaps = await oracle.getOracleTwaps(3600); +const seiTwap = twaps.find((t) => t.denom === 'usei'); +console.log('SEI 1h TWAP:', seiTwap.twap); +``` + +## Supported Denoms + +Common denominations: + +- `usei` — Micro SEI +- `uusdc` — Micro USDC +- `uatom` — Micro ATOM +- `ueth` — Micro Ethereum +- `ubtc` — Micro Bitcoin + +Call `getExchangeRates()` to see the full list of available price feeds. + +## Key Notes + +- Prices are decimal strings; parse as needed (avoid precision loss with `parseFloat`) +- TWAP lookback: minimum 60s, maximum 86,400s (24 hours) +- Oracle updates every block; check `lastUpdateTimestamp` for staleness +- Empty results indicate denom not supported or oracle offline + +## Troubleshooting + + 86,400', 'Use value between 60 (1 min) and 86,400 (24 hours).'], + ['Denom not found', 'Requested denom not tracked by oracle', 'Call getExchangeRates() to list available denoms.'] + ]} +/> + +## References + +- ABI: [github.com/sei-protocol/sei-chain/precompiles/oracle](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/oracle) +- Oracle guide: [/learn/oracles](/learn/oracles) diff --git a/content/evm/precompile-pointer.mdx b/content/evm/precompile-pointer.mdx new file mode 100644 index 00000000..564d203b --- /dev/null +++ b/content/evm/precompile-pointer.mdx @@ -0,0 +1,92 @@ +--- +title: 'Pointer Precompile' +description: 'Bridge identities and assets between CW and EVM using pointers.' +keywords: ['pointer', 'interop', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# Pointer Precompile + +**Address:** `0x000000000000000000000000000000000000100b` + +Bridge identities/assets between CW and EVM. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +interface IPointerPrecompile { + function addCW20Pointer(string memory cwAddr) external payable returns (address pointer); + function addCW721Pointer(string memory cwAddr) external payable returns (address pointer); + function addCW1155Pointer(string memory cwAddr) external payable returns (address pointer); + function addNativePointer(string memory token) external payable returns (address pointer); +} +``` + +
+ +Pointer creation is a state-changing transaction and requires a fee in usei; rely on PointerView for lookups when possible. + +## Example + +```typescript copy +import { ethers } from 'ethers'; + +const POINTER = '0x000000000000000000000000000000000000100b'; +const ABI = ['function addCW20Pointer(string cwAddr) payable returns (address)', 'function addNativePointer(string token) payable returns (address)']; + +const provider = new ethers.BrowserProvider(window.ethereum); +await provider.send('eth_requestAccounts', []); +const pointer = new ethers.Contract(POINTER, ABI, await provider.getSigner()); + +// Register CW20 pointer (fee charged in usei) +await pointer.addCW20Pointer('sei1cw20...', { value: ethers.parseEther('0.01') }); + +// Register native denom pointer +await pointer.addNativePointer('usei', { value: ethers.parseEther('0.01') }); +``` + +## Notes + +- Pointer creation requires governance-approved fees; check release notes for current pricing +- Pointer contracts emit synthetic events tagged `synthetic=true` (v6.1.11+) +- Ensure CW contracts implement expected interfaces; invalid targets revert during registration + +## Troubleshooting + + diff --git a/content/evm/precompile-pointerview.mdx b/content/evm/precompile-pointerview.mdx new file mode 100644 index 00000000..df4172d7 --- /dev/null +++ b/content/evm/precompile-pointerview.mdx @@ -0,0 +1,90 @@ +--- +title: 'PointerView Precompile' +description: 'Query pointer state and ownership between CW and EVM.' +keywords: ['pointerview', 'interop', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# PointerView Precompile + +**Address:** `0x000000000000000000000000000000000000100A` + +Query registered pointers and their metadata. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +interface IPointerViewPrecompile { + function getCW20Pointer(string memory cwAddr) external view returns (address addr, uint16 version, bool exists); + function getCW721Pointer(string memory cwAddr) external view returns (address addr, uint16 version, bool exists); + function getCW1155Pointer(string memory cwAddr) external view returns (address addr, uint16 version, bool exists); + function getNativePointer(string memory token) external view returns (address addr, uint16 version, bool exists); +} +``` + +
+ +## Example + +```typescript copy +import { ethers } from 'ethers'; + +const POINTER_VIEW = '0x000000000000000000000000000000000000100A'; +const ABI = ['function getCW20Pointer(string cwAddr) view returns (address addr, uint16 version, bool exists)', 'function getNativePointer(string token) view returns (address addr, uint16 version, bool exists)']; + +const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); +const pointerView = new ethers.Contract(POINTER_VIEW, ABI, provider); + +const [cw20Addr, cw20Version, cw20Exists] = await pointerView.getCW20Pointer('sei1cw20...'); +if (cw20Exists) { + console.log(`ERC-20 pointer deployed at ${cw20Addr} (version ${cw20Version})`); +} + +const [nativeAddr] = await pointerView.getNativePointer('usei'); +console.log('Native pointer for usei:', nativeAddr); +``` + +## Notes + +- PointerView is read-only; use the Pointer precompile to create new mappings +- Results mirror on-chain state; cache responses client-side to avoid unnecessary RPC calls +- Use PointerView to confirm pointer ownership before invoking Pointer-dependent precompiles + +## Troubleshooting + + diff --git a/content/evm/precompile-solo.mdx b/content/evm/precompile-solo.mdx new file mode 100644 index 00000000..542e0256 --- /dev/null +++ b/content/evm/precompile-solo.mdx @@ -0,0 +1,110 @@ +--- +title: 'Solo Precompile' +description: 'Migrate balances and assets from legacy Sei addresses using the Solo precompile, with support for CW20/CW721 via claimSpecific.' +keywords: ['solo', 'migration', 'cw20', 'cw721', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { CardGrid, FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# Solo Precompile + +**Address:** `0x000000000000000000000000000000000000100C` + +The Solo precompile enables migration of balances and certain assets from legacy Sei addresses to an EVM address. It is the canonical path for user migration and is designed to be safe and auditable. + +Compatibility: Works today and remains valid under the proposed EVM‑only direction (SIP‑3). Solo cannot be called via CosmWasm, and Sei does not support EVM→CW→EVM call patterns. + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +interface ISoloPrecompile { + function claim(bytes calldata payload) external returns (bool); + function claimSpecific(bytes calldata payload) external returns (bool); +} +``` + +
+ +## Generating claim payloads + +The payload is a binary-encoded message that proves control over the legacy Sei address and specifies what to claim. The exact schema is versioned; use the official helpers where available. + +Always construct and sign payloads client‑side and verify the target recipient (msg.sender) is your intended EVM account before broadcasting. + +## Example + +```typescript copy +import { ethers } from 'ethers'; +import { SOLO_PRECOMPILE_ABI, SOLO_PRECOMPILE_ADDRESS } from '@sei-js/precompiles'; + +const provider = new ethers.BrowserProvider(window.ethereum); +await provider.send('eth_requestAccounts', []); +const signer = await provider.getSigner(); +const solo = new ethers.Contract(SOLO_PRECOMPILE_ADDRESS, SOLO_PRECOMPILE_ABI, signer); + +// Migrate all supported assets +const payload = ethers.getBytes('0x...'); // use official helper output +await solo.claim(payload, { gasLimit: 200_000 }); + +// Target a specific CW20 asset +const cw20Payload = ethers.getBytes('0x...'); +await solo.claimSpecific(cw20Payload, { gasLimit: 300_000 }); +``` + +## Notes + +- Solo rejects CosmWasm entrypoints; invoke only from EVM context +- Payloads are signature-bound; reuse of the same claim reverts with `already claimed` +- Review transaction receipts for synthetic logs tagged `synthetic=true` (v6.1.11+) +- For asset inventories, pair Solo with Pointer/PointerView before generating payloads + +## Troubleshooting + + + +## Related Docs + + diff --git a/content/evm/precompile-staking.mdx b/content/evm/precompile-staking.mdx new file mode 100644 index 00000000..668b6da1 --- /dev/null +++ b/content/evm/precompile-staking.mdx @@ -0,0 +1,172 @@ +--- +title: 'Staking Precompile' +description: 'Delegate, undelegate, and manage validators from EVM contracts.' +keywords: ['staking', 'delegation', 'validators', 'precompile', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; + +# Staking Precompile + +**Address:** `0x0000000000000000000000000000000000001005` + +Manage staking operations from EVM contracts: delegate, undelegate, redelegate, create validators, and query delegation state. + +**Decimal Precision**: `delegate` uses `msg.value` (18 decimals, auto-converted). `redelegate` and `undelegate` use 6-decimal precision (1 SEI = 1,000,000 usei). + +## Functions + + + +
+Full Solidity Interface + +```solidity copy +/// Delegate SEI to a validator. +/// @param valAddress Sei validator address (seivaloper...) +/// @return success Whether the delegation succeeded +function delegate(string memory valAddress) payable external returns (bool success); + +/// Redelegate from one validator to another. +/// @param srcAddress Source validator (seivaloper...) +/// @param dstAddress Destination validator (seivaloper...) +/// @param amount Amount in usei (6 decimals: 1 SEI = 1,000,000 usei) +/// @return success Whether the redelegation succeeded +function redelegate(string memory srcAddress, string memory dstAddress, uint256 amount) external returns (bool success); + +/// Undelegate SEI from a validator. +/// @param valAddress Validator to undelegate from (seivaloper...) +/// @param amount Amount in usei (6 decimals) +/// @return success Whether the undelegation succeeded +function undelegate(string memory valAddress, uint256 amount) external returns (bool success); + +/// Create a new validator. +/// @param pubKeyHex Hex-encoded consensus public key +/// @param moniker Validator display name +/// @param commissionRate Decimal string (e.g., "0.10" for 10%) +/// @param commissionMaxRate Max commission cap (decimal string) +/// @param commissionMaxChangeRate Max daily change (decimal string) +/// @param minSelfDelegation Minimum self-delegation in usei +/// @return success Whether the validator was created +function createValidator( + string memory pubKeyHex, + string memory moniker, + string memory commissionRate, + string memory commissionMaxRate, + string memory commissionMaxChangeRate, + uint256 minSelfDelegation +) payable external returns (bool success); + +/// Edit an existing validator's parameters. +/// @param moniker New display name +/// @param commissionRate New commission rate (decimal string) +/// @param minSelfDelegation New minimum self-delegation in usei +/// @return success Whether the edit succeeded +function editValidator( + string memory moniker, + string memory commissionRate, + uint256 minSelfDelegation +) external returns (bool success); + +struct Delegation { + Balance balance; + DelegationDetails delegation; +} + +struct Balance { + uint256 amount; + string denom; +} + +struct DelegationDetails { + string delegator_address; + uint256 shares; + uint256 decimals; + string validator_address; +} + +/// Query delegation for a delegator + validator pair. +/// @param delegator Delegator EVM address +/// @param valAddress Validator address (seivaloper...) +/// @return delegation Delegation state +function delegation(address delegator, string memory valAddress) external view returns (Delegation delegation); +``` + +
+ +## Example (delegate) + +```typescript copy +import { ethers } from 'ethers'; + +const STAKING = '0x0000000000000000000000000000000000001005'; +const ABI = ['function delegate(string valAddress) payable returns (bool)', 'function undelegate(string valAddress, uint256 amount) returns (bool)', 'function redelegate(string srcAddress, string dstAddress, uint256 amount) returns (bool)']; + +const provider = new ethers.BrowserProvider(window.ethereum); +await provider.send('eth_requestAccounts', []); +const staking = new ethers.Contract(STAKING, ABI, await provider.getSigner()); + +// Delegate 10 SEI +await staking.delegate('seivaloper1...', { value: ethers.parseEther('10') }); + +// Undelegate 5 SEI (note: 6 decimal precision) +await staking.undelegate('seivaloper1...', 5_000_000n); +``` + +## Key Notes + +- **delegate**: Use `msg.value` in wei (18 decimals); automatically truncated to 6 decimals +- **redelegate / undelegate**: Provide amounts in usei (6 decimals: 1 SEI = 1,000,000 usei) +- **Unbonding period**: 21 days; undelegated tokens are locked +- **Validator creation**: Requires `msg.value` for initial self-delegation +- **Commission changes**: Limited to 1% per day + +## Troubleshooting + + + +## References + +- ABI: [github.com/sei-protocol/sei-chain/precompiles/staking](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/staking) +- Staking guide: [/learn/general-staking](/learn/general-staking) diff --git a/content/evm/precompile-version-matrix.mdx b/content/evm/precompile-version-matrix.mdx new file mode 100644 index 00000000..a5811b3d --- /dev/null +++ b/content/evm/precompile-version-matrix.mdx @@ -0,0 +1,208 @@ +--- +title: 'Precompile Version Matrix' +description: 'Compatibility and availability of Sei precompiles across chain versions.' +keywords: ['precompiles', 'versions', 'compatibility', 'matrix'] +--- + +import { Callout } from 'nextra/components'; +import { VersionTimeline } from '../../mdx-components'; +import { CopyableAddress } from '../../src/components/CopyableAddress'; + +# Precompile Version Matrix + +Track precompile availability and behavioral changes across Sei chain releases. + +All precompile addresses are stable. Version changes affect behavior, gas costs, or available methods—not addresses. + +## Precompile Addresses + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PrecompileAddressAvailable Since
+ + Addr + + + + v5.5.2
+ + Bank + + + + v5.5.2
+ + Staking + + + + v5.5.2
+ + Distribution + + + + v5.5.2
+ + Governance + + + + v5.5.2
+ + Oracle + + + + v5.5.2
+ + IBC + + + + v5.5.2
+ + Pointer + + + + v5.5.2
+ + PointerView + + + + v5.5.2
+ + Solo + + + + v6.1.4
+ + JSON + + + + v5.5.2
+ + P256 + + + + v6.0.6
+
+ +## Behavioral Changes by Release + + + +Always test against the target network version. Testnet may run newer releases than mainnet; verify via eth_chainId and RPC endpoint documentation. Pair chain releases with matching go-ethereum fork versions (e.g., v1.15.7-sei-7 for v6.1.11). + +## References + +- Precompile source: [github.com/sei-protocol/sei-chain/precompiles](https://github.com/sei-protocol/sei-chain/tree/main/precompiles) +- Version history: Check individual precompile `versions` manifests (e.g., `precompiles/addr/versions`) +- Release notes: [CHANGELOG.md](https://github.com/sei-protocol/sei-chain/blob/main/CHANGELOG.md) diff --git a/content/evm/precompiles/P256.mdx b/content/evm/precompiles/P256.mdx deleted file mode 100644 index 501a7adc..00000000 --- a/content/evm/precompiles/P256.mdx +++ /dev/null @@ -1,418 +0,0 @@ ---- -title: 'P256 Precompile' -description: 'Use the P256 precompile to verify secp256r1/P-256 signatures directly from EVM smart contracts, enabling efficient passkey-based authentication and signature verification.' -keywords: ['p256 precompile', 'secp256r1', 'signature verification', 'passkey', 'webauthn', 'secure enclave'] ---- - -import { Callout } from 'nextra/components'; - -# P256 Precompile - -**Address**: `0x0000000000000000000000000000000000001011` - -This is the implementation of [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md), which provides a precompiled contract for verifying signatures in the `secp256r1` or `P-256` elliptic curve. - -## Overview - -The P256 precompile enables efficient signature verification for the `secp256r1` curve, which is widely used in modern security systems including: - -- Apple's Secure Enclave -- WebAuthn/FIDO2 -- Android Keychain -- Various hardware security modules (HSMs) -- PassKeys - -This precompile implementation is significantly more gas efficient (up to 60x) compared to Solidity-based implementations. - -## Interface - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -address constant P256VERIFY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001011; - -IP256VERIFY constant P256VERIFY_CONTRACT = IP256VERIFY(P256VERIFY_PRECOMPILE_ADDRESS); - -interface IP256VERIFY { - function verify( - bytes memory signature - ) external view returns (bytes memory response); -} -``` - -## Implementation Library - -Here's a complete implementation of the P256 library that provides a convenient wrapper around the precompile: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import "./ECDSA.sol"; -import "./P256Verify.sol"; - -/// @title P256 -/// @author klkvr -/// @author jxom -/// @notice Wrapper function to abstract low level details of call to the P256 -/// signature verification precompile as defined in EIP-7212, see -/// . -library P256 { - /// @notice P256VERIFY operation - /// @param digest 32 bytes of the signed data hash - /// @param signature Signature of the signer - /// @param publicKey Public key of the signer - /// @return success Represents if the operation was successful - function verify(bytes32 digest, ECDSA.Signature memory signature, ECDSA.PublicKey memory publicKey) - internal - view - 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; - - return success; - } -} -``` - -## Basic Usage - -### Using the P256 Library - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -import "./P256.sol"; -import "./ECDSA.sol"; - -contract P256Example { - using P256 for bytes32; - - function verifySignature( - bytes32 messageHash, - ECDSA.Signature memory signature, - ECDSA.PublicKey memory publicKey - ) public view returns (bool) { - return P256.verify(messageHash, signature, publicKey); - } -} -``` - -### Direct Precompile Usage - -For more control, you can call the precompile directly: - -```solidity copy -pragma solidity ^0.8.0; - -interface IP256Verify { - function verify(bytes calldata input) external view returns (bytes32); -} - -contract P256Verifier { - IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011); - - function verifySignature( - bytes32 messageHash, - bytes32 r, - bytes32 s, - bytes32 publicKeyX, - bytes32 publicKeyY - ) external view returns (bool) { - bytes memory input = abi.encodePacked( - messageHash, - r, - s, - publicKeyX, - publicKeyY - ); - - bytes32 result = P256_PRECOMPILE.verify(input); - return result != bytes32(0); - } -} -``` - -## Signature Format - -The signature verification requires the following components: - -- `digest`: 32 bytes of the signed data hash -- `signature`: Contains the r and s components of the signature -- `publicKey`: Contains the x and y coordinates of the public key - -The precompile expects these components to be encoded in a specific format: - -- First 32 bytes: message hash -- Next 32 bytes: `r` component of the signature -- Next 32 bytes: `s` component of the signature -- Next 32 bytes: `x` coordinate of the public key -- Next 32 bytes: `y` coordinate of the public key - -Total length: 160 bytes - -## Gas Costs - -The precompile is highly gas efficient compared to Solidity implementations. The exact gas cost per byte of verified data is set to `GasCostPerByte = 300`, which gives us: - -- **Total Cost**: 300 × 160 = **48,000 gas** per verification -- **Efficiency**: Up to 60x more efficient than pure Solidity implementations - -## Real-World Use Cases - -### WebAuthn/PassKeys Authentication - -```solidity copy -pragma solidity ^0.8.0; - -contract WebAuthnAuth { - IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011); - - struct PublicKey { - bytes32 x; - bytes32 y; - } - - mapping(address => PublicKey) public userKeys; - - function registerPasskey( - bytes32 keyX, - bytes32 keyY - ) external { - userKeys[msg.sender] = PublicKey(keyX, keyY); - } - - function authenticateWithPasskey( - bytes32 challengeHash, - bytes32 r, - bytes32 s - ) external view returns (bool) { - PublicKey memory userKey = userKeys[msg.sender]; - require(userKey.x != 0 && userKey.y != 0, "No passkey registered"); - - bytes memory input = abi.encodePacked( - challengeHash, - r, - s, - userKey.x, - userKey.y - ); - - bytes32 result = P256_PRECOMPILE.verify(input); - return result != bytes32(0); - } -} -``` - -### Apple Secure Enclave Integration - -```solidity copy -contract SecureEnclaveAuth { - event SecureOperation(address indexed user, bytes32 indexed operationHash); - - function executeSecureOperation( - bytes32 operationHash, - bytes32 r, - bytes32 s, - bytes32 enclaveKeyX, - bytes32 enclaveKeyY - ) external { - require( - verifyEnclaveSignature(operationHash, r, s, enclaveKeyX, enclaveKeyY), - "Invalid Secure Enclave signature" - ); - - emit SecureOperation(msg.sender, operationHash); - } - - function verifyEnclaveSignature( - bytes32 hash, - bytes32 r, - bytes32 s, - bytes32 x, - 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); - } -} -``` - -### Multi-Signature with Hardware Keys - -```solidity copy -contract HardwareMultiSig { - IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011); - - struct Signature { - bytes32 r; - bytes32 s; - } - - struct PublicKey { - bytes32 x; - bytes32 y; - } - - function verifyMultipleHardwareKeys( - bytes32 messageHash, - Signature[] calldata signatures, - PublicKey[] calldata publicKeys, - uint256 threshold - ) external view returns (bool) { - require(signatures.length == publicKeys.length, "Mismatched arrays"); - require(threshold <= signatures.length, "Invalid threshold"); - - uint256 validSignatures = 0; - - for (uint i = 0; i < signatures.length; i++) { - bytes memory input = abi.encodePacked( - messageHash, - signatures[i].r, - signatures[i].s, - publicKeys[i].x, - publicKeys[i].y - ); - - bytes32 result = P256_PRECOMPILE.verify(input); - if (result != bytes32(0)) { - validSignatures++; - } - } - - return validSignatures >= threshold; - } -} -``` - -## Security Considerations - - - **Important**: Always validate public keys and signature components before verification to prevent invalid curve point attacks. - - -### Public Key Validation - -```solidity copy -function isValidPublicKey(bytes32 x, bytes32 y) internal pure returns (bool) { - // Ensure point is not at infinity - if (x == 0 && y == 0) return false; - - // Additional validation should be performed for production use - // The precompile will reject invalid curve points - return true; -} -``` - -### Signature Malleability - -P256 signatures can be malleable. If your application requires unique signatures, implement additional checks: - -```solidity copy -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; -} -``` - -## JavaScript Integration - -### Preparing Input Data - -```javascript -// Example: Preparing P256 verification input -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); - - // Concatenate all components - return ethers.utils.concat([hash, r, s, x, y]); -} - -// Usage with ethers.js -async function verifyP256Signature(contract, messageHash, signature, publicKey) { - const input = prepareP256Input(messageHash, signature, publicKey); - const result = await contract.P256_PRECOMPILE.verify(input); - return result !== '0x0000000000000000000000000000000000000000000000000000000000000000'; -} -``` - -## Error Handling - -The P256 precompile returns zero on failure. Common failure cases include: - -1. **Invalid Input Length**: Input must be exactly 160 bytes -2. **Invalid Public Key**: Point not on the P256 curve -3. **Invalid Signature**: r or s values out of valid range -4. **Verification Failure**: Signature doesn't match message and public key - -```solidity copy -function safeVerifyP256( - bytes32 messageHash, - bytes32 r, - bytes32 s, - bytes32 publicKeyX, - bytes32 publicKeyY -) internal view returns (bool success, string memory error) { - // Validate inputs - if (!isValidPublicKey(publicKeyX, publicKeyY)) { - return (false, "Invalid public key"); - } - - if (r == 0 || s == 0) { - return (false, "Invalid signature components"); - } - - // Prepare input and verify - bytes memory input = abi.encodePacked( - messageHash, r, s, publicKeyX, publicKeyY - ); - - bytes32 result = P256_PRECOMPILE.verify(input); - return (result != bytes32(0), result == bytes32(0) ? "Verification failed" : ""); -} -``` - -## Testing - -### Unit Tests - -```solidity copy -contract P256Test { - IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011); - - function testValidSignature() public { - // Test with known valid P256 signature - bytes32 messageHash = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; - // Use actual test vectors for production tests - bytes32 r = 0x...; // Valid r component - bytes32 s = 0x...; // Valid s component - bytes32 x = 0x...; // Valid public key x - bytes32 y = 0x...; // Valid public key y - - bytes memory input = abi.encodePacked(messageHash, r, s, x, y); - bytes32 result = P256_PRECOMPILE.verify(input); - - assert(result != bytes32(0)); - } -} -``` - -## Performance Considerations - -- **Gas Efficiency**: 48,000 gas per verification vs 2M+ gas for Solidity implementations -- **Batch Operations**: Consider batching multiple verifications in a single transaction -- **Caching**: Cache public keys on-chain to reduce calldata for repeated verifications -- **Hardware Integration**: Particularly efficient for applications using hardware-backed keys - -For more information about the P256 precompile implementation, visit the [Sei Chain repository](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/p256). diff --git a/content/evm/precompiles/_meta.js b/content/evm/precompiles/_meta.js deleted file mode 100644 index 7b3b6619..00000000 --- a/content/evm/precompiles/_meta.js +++ /dev/null @@ -1,9 +0,0 @@ -export default { - 'example-usage': 'Example Usage', - distribution: 'Distribution', - governance: 'Governance', - json: 'JSON', - oracle: 'Oracle', - staking: 'Staking', - P256: 'P256' -}; diff --git a/content/evm/precompiles/distribution.mdx b/content/evm/precompiles/distribution.mdx deleted file mode 100644 index 9fe6df03..00000000 --- a/content/evm/precompiles/distribution.mdx +++ /dev/null @@ -1,309 +0,0 @@ ---- -title: 'Distribution Precompile' -description: "Manage staking rewards and validator commissions through Sei's distribution precompile. EVM applications can withdraw rewards, set withdrawal addresses, and interact with Cosmos SDK distribution functionality." -keywords: ['distribution precompile', 'staking rewards', 'validator commissions', 'sei rewards', 'evm staking', 'cosmos distribution'] ---- - -import { Callout } from 'nextra/components'; - -# Distribution Precompile - -**Address**: `0x0000000000000000000000000000000000001007` - -The distribution precompile provides EVM access to Cosmos SDK's distribution module. Smart contracts can manage staking rewards, validator commissions, and withdrawal addresses. This precompile is essential for DeFi applications that need to handle staking rewards programmatically. - -## Key Features - -- **Reward Management**: Withdraw delegation rewards from validators -- **Commission Handling**: Validators can withdraw earned commissions -- **Batch Operations**: Withdraw from multiple validators efficiently -- **Flexible Withdrawals**: Set custom withdrawal addresses -- **Comprehensive Queries**: Access detailed reward information - -## Interface Overview - -```solidity -interface IDistr { - // Transaction Methods - function setWithdrawAddress(address withdrawAddr) external returns (bool); - function withdrawDelegationRewards(string memory validator) external returns (bool); - function withdrawMultipleDelegationRewards(string[] memory validators) external returns (bool); - function withdrawValidatorCommission(string memory validator) external returns (bool); - - // Query Methods - function rewards(address delegatorAddress) external view returns (Rewards); -} -``` - -## Transaction Methods - -### setWithdrawAddress - -Sets the withdrawal address for staking rewards. By default, rewards are sent to the delegator's address, but this can be customized. - -```solidity -function setWithdrawAddress(address withdrawAddr) external returns (bool success); -``` - -**Parameters:** - -- `withdrawAddr`: EVM address where future rewards should be sent - -**Gas Cost**: ~30,000 gas - -**Example:** - -```solidity -// Set rewards to go to a treasury contract -bool success = DISTR_CONTRACT.setWithdrawAddress(0x742d35Cc6634C0532925a3b8D4C9db96590c6C8C); -require(success, "Failed to set withdraw address"); -``` - -### withdrawDelegationRewards - -Withdraws accumulated rewards from a specific validator. - -```solidity -function withdrawDelegationRewards(string memory validator) external returns (bool success); -``` - -**Parameters:** - -- `validator`: Sei validator address (e.g., "seivaloper1...") - -**Gas Cost**: ~50,000-80,000 gas (varies by reward amount) - -**Example:** - -```solidity -string memory validator = "seivaloper1xyz..."; -bool success = DISTR_CONTRACT.withdrawDelegationRewards(validator); -require(success, "Failed to withdraw rewards"); -``` - -### withdrawMultipleDelegationRewards - -Efficiently withdraws rewards from multiple validators in a single transaction. - -```solidity -function withdrawMultipleDelegationRewards(string[] memory validators) external returns (bool success); -``` - -**Parameters:** - -- `validators`: Array of Sei validator addresses - -**Gas Cost**: ~40,000 + (30,000 × number of validators) - -**Example:** - -```solidity -string[] memory validators = new string[](3); -validators[0] = "seivaloper1abc..."; -validators[1] = "seivaloper1def..."; -validators[2] = "seivaloper1ghi..."; - -bool success = DISTR_CONTRACT.withdrawMultipleDelegationRewards(validators); -require(success, "Failed to withdraw multiple rewards"); -``` - -**Batch Efficiency**: Using `withdrawMultipleDelegationRewards` is significantly more gas-efficient than multiple individual calls, especially when withdrawing from 3+ validators. - -### withdrawValidatorCommission - -Allows validators to withdraw their earned commission. Only callable by the validator operator. - -```solidity -function withdrawValidatorCommission(string memory validator) external returns (bool success); -``` - -**Parameters:** - -- `validator`: Sei validator address (must match caller's validator) - -**Gas Cost**: ~60,000-90,000 gas - -**Example:** - -```solidity -// Only works if caller is the validator operator -string memory myValidator = "seivaloper1myvalidator..."; -bool success = DISTR_CONTRACT.withdrawValidatorCommission(myValidator); -require(success, "Failed to withdraw commission"); -``` - -**Validator Only**: This method can only be called by the validator's operator address. Attempting to withdraw commission for a validator you don't operate will fail. - -## Query Methods - -### rewards - -Retrieves comprehensive reward information for a delegator across all validators. - -```solidity -function rewards(address delegatorAddress) external view returns (Rewards rewards); -``` - -**Data Structures:** - -```solidity -struct Coin { - uint256 amount; // Token amount (raw units) - uint256 decimals; // Decimal places for display - string denom; // Token denomination -} - -struct Reward { - Coin[] coins; // Reward coins from this validator - string validator_address; // Validator's Sei address -} - -struct Rewards { - Reward[] rewards; // Per-validator breakdown - Coin[] total; // Total rewards across all validators -} -``` - -**Example:** - -```solidity -Rewards memory userRewards = DISTR_CONTRACT.rewards(msg.sender); - -// Check total rewards -for (uint i = 0; i < userRewards.total.length; i++) { - Coin memory coin = userRewards.total[i]; - uint256 displayAmount = coin.amount / (10 ** coin.decimals); - // Process reward amount for coin.denom -} - -// Check per-validator rewards -for (uint i = 0; i < userRewards.rewards.length; i++) { - Reward memory reward = userRewards.rewards[i]; - // Process rewards from reward.validator_address -} -``` - -## Practical Examples - -### DeFi Yield Aggregator - -```solidity -contract YieldAggregator { - IDistr constant DISTR = IDistr(0x0000000000000000000000000000000000001007); - - mapping(address => string[]) public userValidators; - - function harvestRewards() external { - string[] memory validators = userValidators[msg.sender]; - require(validators.length > 0, "No validators to harvest from"); - - // Efficiently withdraw from all validators - bool success = DISTR.withdrawMultipleDelegationRewards(validators); - require(success, "Harvest failed"); - - // Additional logic to compound or distribute rewards - } - - function checkPendingRewards(address user) external view returns (uint256 totalSei) { - Rewards memory rewards = DISTR.rewards(user); - - for (uint i = 0; i < rewards.total.length; i++) { - if (keccak256(bytes(rewards.total[i].denom)) == keccak256(bytes("usei"))) { - totalSei = rewards.total[i].amount; - break; - } - } - } -} -``` - -### Validator Commission Manager - -```solidity -contract ValidatorManager { - IDistr constant DISTR = IDistr(0x0000000000000000000000000000000000001007); - - string public validatorAddress; - address public treasury; - - constructor(string memory _validator, address _treasury) { - validatorAddress = _validator; - treasury = _treasury; - - // Set commission withdrawals to go to treasury - DISTR.setWithdrawAddress(treasury); - } - - function withdrawCommission() external { - bool success = DISTR.withdrawValidatorCommission(validatorAddress); - require(success, "Commission withdrawal failed"); - } -} -``` - -## Error Handling - -Common error scenarios and how to handle them: - -```solidity -contract SafeDistribution { - IDistr constant DISTR = IDistr(0x0000000000000000000000000000000000001007); - - function safeWithdrawRewards(string memory validator) external returns (bool) { - // Check if there are rewards to withdraw first - Rewards memory rewards = DISTR.rewards(msg.sender); - - bool hasRewards = false; - for (uint i = 0; i < rewards.rewards.length; i++) { - if (keccak256(bytes(rewards.rewards[i].validator_address)) == - keccak256(bytes(validator))) { - hasRewards = rewards.rewards[i].coins.length > 0; - break; - } - } - - if (!hasRewards) { - return false; // No rewards to withdraw - } - - try DISTR.withdrawDelegationRewards(validator) returns (bool success) { - return success; - } catch { - return false; // Handle withdrawal failure gracefully - } - } -} -``` - -## Gas Optimization Tips - -1. **Batch Operations**: Use `withdrawMultipleDelegationRewards` for multiple validators -2. **Check Before Withdraw**: Query rewards first to avoid unnecessary transactions -3. **Set Withdraw Address Once**: Avoid repeated `setWithdrawAddress` calls -4. **Commission Timing**: Withdraw validator commission when amounts are substantial - -## Integration Patterns - -### Auto-Compounding Strategy - -```solidity -// Automatically reinvest rewards back into staking -function autoCompound() external { - // 1. Withdraw rewards - DISTR.withdrawMultipleDelegationRewards(getMyValidators()); - - // 2. Use staking precompile to re-delegate - // (Implementation depends on staking precompile integration) -} -``` - -### Treasury Management - -```solidity -// Route all rewards to a DAO treasury -function setupTreasuryWithdrawals(address treasury) external onlyOwner { - DISTR.setWithdrawAddress(treasury); -} -``` - -View the complete distribution precompile source code and ABI [here](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/distribution). diff --git a/content/evm/precompiles/governance.mdx b/content/evm/precompiles/governance.mdx deleted file mode 100644 index ccf6b5cb..00000000 --- a/content/evm/precompiles/governance.mdx +++ /dev/null @@ -1,1294 +0,0 @@ ---- -title: 'Governance Precompile Usage' -description: "Learn how to interact with Sei's governance precompile through ethers.js, enabling proposal submission, voting, token deposits, and governance queries to participate in on-chain governance directly from EVM applications." -keywords: ['governance precompile', 'ethers.js', 'proposal voting', 'blockchain governance', 'on-chain voting', 'sei evm', 'dao governance'] ---- - -import { Callout } from 'nextra/components'; - -# Governance Precompile Usage - -**Address:** `0x0000000000000000000000000000000000001006` - -The Sei governance precompile enables EVM applications to participate in Sei's on-chain governance process. This allows users and smart contracts to submit proposals, vote on them, deposit tokens, and query governance information directly through the EVM interface. - -**What is a precompile?** A precompile is a special smart contract deployed at a fixed address by the Sei protocol itself, that exposes custom native chain logic to EVM-based applications. It acts like a regular contract from the EVM's perspective, but executes privileged, low-level logic efficiently. - -## How Does the Governance Precompile Work? - -The governance precompile at address `0x0000000000000000000000000000000000001006` exposes functions like `vote()`, `voteWeighted()`, `deposit()`, and `submitProposal()`. - -- **Direct Integration:** EVM contracts and dApps can interact with governance like any other smart contract method. -- **Native Execution:** Governance operations are executed at the chain level for maximum efficiency. -- **Seamless:** No need for separate wallet integrations. - -## Use Cases - -- **Voting Interfaces:** Create user-friendly governance participation tools. -- **Automated Governance:** Smart contracts that vote based on predetermined strategies. -- **Governance Aggregators:** Build platforms that aggregate and simplify governance participation. - -## Sei Governance Overview - -Sei governance operates on a structured timeline with specific parameters: - -### Governance Process - -1. **Proposal Submission:** Submit proposal with minimum 3,500 SEI deposit -2. **Deposit Period:** 2 days to accumulate minimum deposit or reach expedited threshold -3. **Voting Period:** 3 days (1 day for expedited) of active voting once minimum deposit is met -4. **Tallying:** Results calculated based on quorum and voting thresholds - - To find out all about the governance process on Sei, go to the [governance explainer](/learn/general-governance). - - -**Critical Mainnet Considerations:** - -- **High Stakes:** Mainnet governance involves real SEI tokens and binding decisions -- **Minimum Deposit:** 3500 SEI minimum, 7000 SEI for expedited -- **Deposit Risk:** Deposits will be burned if your proposal receives >33.4% NoWithVeto votes -- **Voting Power Required:** Must stake SEI with validators to participate -- **Time Limits:** Strict 2-day deposit period and 3-day voting period -- **Real Impact:** Successful proposals can change network parameters - - - -## Functions - -The governance precompile exposes the following functions: - -### Transaction Functions - -```solidity -/// Cast a vote on the specified proposal. -/// @param proposalID The ID of the proposal to vote on. -/// @param option The option to vote for. (1=Yes, 2=Abstain, 3=No, 4=NoWithVeto) -/// @return Whether the vote was successfully cast. -function vote( - uint64 proposalID, - int32 option -) external returns (bool success); - -struct WeightedVoteOption { - int32 option; // Vote option (1=Yes, 2=Abstain, 3=No, 4=NoWithVeto) - string weight; // Weight as decimal string (e.g., "0.7") - MUST sum to exactly 1.0 across all options -} - -/// Cast a weighted vote on a governance proposal (vote splitting) -/// @param proposalID The ID of the proposal to vote on -/// @param options Array of weighted vote options, weights MUST sum to exactly 1.0 or transaction will fail -/// @return success Whether the vote was successfully cast -function voteWeighted( - uint64 proposalID, - WeightedVoteOption[] calldata options -) external returns (bool success); - -/// Deposit funds into the specified proposal. -/// @param proposalID The ID of the proposal to deposit to. -/// @return Whether the tokens were successfully deposited. -function deposit( - uint64 proposalID -) payable external returns (bool success); - -/// Submit a new governance proposal. Deposit should be provided via msg.value -/// @param proposalJSON JSON string containing proposal details e.g.: -/// { -/// "title": "Proposal Title", -/// "description": "Proposal Description", -/// "type": "Text", // Optional, defaults to "Text" if empty -/// "is_expedited": false // Optional -/// } -/// @return proposalID The ID of the created proposal - -function submitProposal( - string calldata proposalJSON -) payable external returns (uint64 proposalID); -``` - -## Using the Contract - -### Setup - -#### Prerequisites - -Before getting started, ensure you have: - -- **Node.js** (v16 or higher) -- **npm** or **yarn** package manager -- **MetaMask** or compatible EVM wallet configured for Sei Mainnet -- **SEI tokens** for gas fees and governance deposits (minimum 3,500 SEI for proposals) - -#### Install Dependencies - -Install the required packages for interacting with Sei precompiles: - -```bash copy -# Install ethers.js for smart contract interactions -npm install ethers - -# Install Sei EVM bindings for precompile addresses and ABIs -npm install @sei-js/precompiles@2.1.2 - -# Install dotenv for managing private keys (optional but recommended) -npm install dotenv -``` - -#### Import Precompile Components - -```typescript copy -// Import Governance precompile address and ABI -// View the entire ABI here: https://github.com/sei-protocol/sei-chain/tree/main/precompiles/gov -import { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } from '@sei-js/precompiles'; -import { ethers } from 'ethers'; -``` - -**Precompile Address:** The governance precompile is deployed at `0x0000000000000000000000000000000000001006` - -#### Get SEI Tokens - -Purchase SEI tokens from supported exchanges: - -- **Centralized Exchanges:** Binance, Coinbase, KuCoin, Gate.io -- **Decentralized Exchanges:** Astroport, Dragonswap (on Sei) -- **Bridge Options:** Various cross-chain bridges support SEI - -**Minimum Requirements:** You need at least 3,500 SEI to submit a governance proposal, plus additional tokens for gas fees and potential additional deposits. - -### Contract Initialization - -Set up your provider, signer, and contract instance: - -```typescript copy -// Using MetaMask as the signer and provider (browser environment) -const provider = new ethers.BrowserProvider(window.ethereum); -await provider.send('eth_requestAccounts', []); -const signer = await provider.getSigner(); - -// Or for Node.js environment -const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); -const signer = new ethers.Wallet('YOUR_PRIVATE_KEY', provider); - -// Create a contract instance for the governance precompile -const governance = new ethers.Contract(GOVERNANCE_PRECOMPILE_ADDRESS, GOVERNANCE_PRECOMPILE_ABI, signer); -``` - -## Vote Options - -The governance system uses the following vote options: - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Option - - Value - - Description - - Impact -
- Yes - - 1 - - Vote in favor of the proposal - - Proposal passes if threshold met -
- Abstain - - 2 - - Abstain from voting - - Counts toward quorum only -
- No - - 3 - - Vote against the proposal - - Opposes proposal -
- NoWithVeto - - 4 - - Vote against with veto - - Opposes Proposal & burns deposit if > 33.4% -
-
- -### Voting Power - -Your voting power is determined by: - -- **Staked SEI:** Amount of SEI you have staked with validators -- **Delegation:** SEI delegated to validators counts toward voting power -- **Liquid SEI:** Unstaked SEI does not provide voting power - -**Important:** Only staked SEI provides voting power. You must delegate your tokens to validators to participate in governance voting. - -## Validation Functions - -Before making governance transactions, use these helper functions to validate inputs: - -```typescript copy -// Validate vote option -function isValidVoteOption(option: number): boolean { - return option >= 1 && option <= 4; -} - -// Validate weighted vote options sum to 1.0 - CRITICAL FOR TRANSACTION SUCCESS -function validateWeightedOptions(options: Array<{ option: number; weight: string }>): boolean { - const totalWeight = options.reduce((sum, opt) => sum + parseFloat(opt.weight), 0); - - // Check if total weight equals 1.0 (with small tolerance for floating point precision) - // Note: Sei governance is strict - even small deviations will cause transaction failure - const isValidTotal = Math.abs(totalWeight - 1.0) < 0.000001; - - // Check if all options are valid - const allOptionsValid = options.every((opt) => isValidVoteOption(opt.option)); - - if (!isValidTotal) { - console.error(`Weights sum to ${totalWeight}, but MUST equal exactly 1.0`); - } - - return isValidTotal && allOptionsValid; -} - -// Validate proposal JSON structure -function validateProposalJSON(proposalJSON: string): boolean { - try { - const proposal = JSON.parse(proposalJSON); - return proposal.title && proposal.description && typeof proposal.title === 'string' && typeof proposal.description === 'string' && proposal.title.length > 0 && proposal.description.length > 0; - } catch { - return false; - } -} - -// Check if deposit meets minimum requirement -function validateDepositAmount(depositAmount: bigint, isExpedited: boolean = false): boolean { - const MINIMUM_DEPOSIT = ethers.parseUnits('3500', 18); // 3,500 SEI minimum - const EXPEDITED_DEPOSIT = ethers.parseUnits('7000', 18); // 7,000 SEI for expedited - - const requiredAmount = isExpedited ? EXPEDITED_DEPOSIT : MINIMUM_DEPOSIT; - return depositAmount >= requiredAmount; -} - -// Check if user has sufficient voting power -async function checkVotingPower(userAddress: string): Promise { - // This would typically query staking information - // Implementation depends on your specific setup - console.log(`Checking voting power for ${userAddress}`); - return ethers.parseUnits('0', 18); // Placeholder - implement actual check -} -``` - -## Step-by-Step Guide: Using the Governance Precompile - -### Submit a Proposal - - -**Important Deposit Requirements:** - -- **Minimum deposit:** 3,500 SEI required for proposal to enter voting period -- **Expedited processing:** 7,000 SEI if the voting period should only be 1 day long -- **Deposit period:** 2 days to meet minimum or proposal fails and deposits are burned -- **Multiple deposits:** Anyone can contribute to reach the minimum threshold - - - -```typescript copy -async function submitGovernanceProposal(title: string, description: string, depositAmount: bigint, isExpedited: boolean = false): Promise { - // Validate inputs - if (!title || !description) { - throw new Error('Title and description are required'); - } - - if (title.length < 5 || title.length > 200) { - throw new Error('Title must be between 5 and 200 characters'); - } - - if (description.length < 20 || description.length > 10000) { - throw new Error('Description must be between 20 and 10,000 characters'); - } - - // Proposal details - const proposal = { - title: title.trim(), - description: description.trim(), - type: 'Text', - is_expedited: isExpedited - }; - - // Convert to JSON string and validate - const proposalJSON = JSON.stringify(proposal); - if (!validateProposalJSON(proposalJSON)) { - throw new Error('Invalid proposal JSON structure'); - } - - // Validate deposit amount - if (!validateDepositAmount(depositAmount, isExpedited)) { - const required = isExpedited ? '7,000 SEI' : '3,500 SEI'; - const provided = ethers.formatEther(depositAmount); - throw new Error(`Insufficient deposit. Required: ${required}, Provided: ${provided} SEI`); - } - - // Check balance before submitting - const balance = await provider.getBalance(await signer.getAddress()); - const totalCost = depositAmount + ethers.parseUnits('0.1', 18); // Add gas buffer - - if (balance < totalCost) { - throw new Error(`Insufficient balance. Need ${ethers.formatEther(totalCost)} SEI (including gas)`); - } - - try { - console.log('Submitting governance proposal...'); - console.log(`Title: ${title}`); - console.log(`Deposit: ${ethers.formatEther(depositAmount)} SEI`); - console.log(`Expedited: ${isExpedited}`); - - const tx = await governance.submitProposal(proposalJSON, { - value: depositAmount, - gasLimit: 500000 - }); - - console.log('Transaction submitted:', tx.hash); - console.log('Waiting for confirmation...'); - - const receipt = await tx.wait(); - - // Extract proposal ID from events - let proposalID: bigint | null = null; - for (const log of receipt.logs) { - try { - const parsed = governance.interface.parseLog(log); - if (parsed && parsed.name === 'ProposalSubmitted') { - proposalID = parsed.args.proposalID; - break; - } - } catch { - continue; - } - } - - if (!proposalID) { - throw new Error('Could not extract proposal ID from transaction receipt'); - } - - console.log('✅ Proposal submitted successfully!'); - console.log(`Proposal ID: ${proposalID.toString()}`); - console.log(`Transaction hash: ${receipt.hash}`); - console.log(`View proposal: https://sei.explorers.guru/proposal/${proposalID.toString()}`); - - return proposalID; - } catch (error: any) { - console.error('Failed to submit proposal:', error); - - if (error.message.includes('insufficient funds')) { - throw new Error('Insufficient funds for proposal deposit and gas fees'); - } else if (error.message.includes('invalid deposit')) { - throw new Error('Deposit amount does not meet minimum requirements'); - } else { - throw error; - } - } -} - -// Usage example -const proposalTitle = 'Increase Block Size Limit'; -const proposalDescription = ` -This proposal suggests increasing the block size limit from the current value to improve transaction throughput on the Sei network. - -## Rationale -Current network usage patterns show we are approaching capacity limits during peak times. Increasing the block size will: - -1. Reduce transaction fees during high usage periods -2. Improve user experience with faster confirmation times -3. Support growing ecosystem adoption - -## Implementation -The parameter change would be implemented through the existing governance mechanism with a 2-week transition period. - -## Risks -- Slightly increased hardware requirements for validators -- Potential centralization concerns (mitigated by current validator distribution) - -The benefits significantly outweigh the minimal risks involved. -`.trim(); - -const depositAmount = ethers.parseUnits('4000', 18); // 4,000 SEI deposit -await submitGovernanceProposal(proposalTitle, proposalDescription, depositAmount, false); -``` - -### Vote on a Proposal - -```typescript copy -// Simple voting with comprehensive validation -async function voteOnProposal(proposalID: bigint, voteOption: number): Promise { - // Validate vote option - if (!isValidVoteOption(voteOption)) { - throw new Error('Invalid vote option. Must be 1 (Yes), 2 (Abstain), 3 (No), or 4 (NoWithVeto)'); - } - - // Check voting power (implement based on your staking setup) - const userAddress = await signer.getAddress(); - console.log(`Checking voting eligibility for ${userAddress}...`); - - // Verify proposal exists and is in voting period - try { - // You would typically query proposal status here - console.log(`Voting on proposal ${proposalID.toString()}...`); - - const voteOptionNames = { - 1: 'Yes', - 2: 'Abstain', - 3: 'No', - 4: 'NoWithVeto' - }; - - console.log(`Vote option: ${voteOptionNames[voteOption as keyof typeof voteOptionNames]}`); - - const tx = await governance.vote(proposalID, voteOption, { - gasLimit: 200000 - }); - - console.log('Transaction submitted:', tx.hash); - const receipt = await tx.wait(); - - console.log('✅ Vote cast successfully!'); - console.log(`Proposal ID: ${proposalID.toString()}`); - console.log(`Vote: ${voteOptionNames[voteOption as keyof typeof voteOptionNames]}`); - console.log(`Transaction hash: ${receipt.hash}`); - console.log(`View on explorer: https://seitrace.com/tx/${receipt.hash}`); - } catch (error: any) { - console.error('Failed to cast vote:', error); - - if (error.message.includes('proposal does not exist')) { - throw new Error('Proposal does not exist or voting period has ended'); - } else if (error.message.includes('already voted')) { - throw new Error('You have already voted on this proposal'); - } else if (error.message.includes('insufficient voting power')) { - throw new Error('No voting power - you must stake SEI to participate in governance'); - } else { - throw error; - } - } -} - -// Usage examples -const proposalID = 1n; - -// Vote Yes -await voteOnProposal(proposalID, 1); - -// Vote No with Veto (burns deposit if >33.4% of votes) -await voteOnProposal(proposalID, 4); -``` - -### Weighted Voting - -**Critical Requirement:** Weighted vote options MUST sum to exactly 1.0 or the transaction will fail. The Sei governance module enforces this strictly - even small rounding errors will cause rejection. - -```typescript copy -// Weighted voting with comprehensive validation -async function castWeightedVote(proposalID: bigint, weightedOptions: Array<{ option: number; weight: string }>): Promise { - // Comprehensive validation - if (weightedOptions.length === 0) { - throw new Error('At least one vote option is required'); - } - - if (weightedOptions.length > 4) { - throw new Error('Cannot have more than 4 vote options'); - } - - // Validate each option and check for duplicates - const seenOptions = new Set(); - let totalWeight = 0; - - for (const { option, weight } of weightedOptions) { - // Validate option - if (!isValidVoteOption(option)) { - throw new Error(`Invalid vote option: ${option}. Must be 1-4`); - } - - // Check for duplicates - if (seenOptions.has(option)) { - throw new Error(`Duplicate vote option: ${option}`); - } - seenOptions.add(option); - - // Validate weight - const weightNum = parseFloat(weight); - if (isNaN(weightNum) || weightNum <= 0 || weightNum > 1) { - throw new Error(`Invalid weight: ${weight}. Must be number between 0 and 1`); - } - - totalWeight += weightNum; - } - - // CRITICAL: Check total weight - if (Math.abs(totalWeight - 1.0) > 0.000001) { - throw new Error(`CRITICAL: Total weight must equal exactly 1.0, got ${totalWeight.toFixed(6)}. Transaction will fail.`); - } - - try { - console.log('Casting weighted vote...'); - console.log(`Proposal ID: ${proposalID.toString()}`); - console.log('Vote distribution:'); - - const voteOptionNames = { 1: 'Yes', 2: 'Abstain', 3: 'No', 4: 'NoWithVeto' }; - let totalPercent = 0; - - for (const { option, weight } of weightedOptions) { - const percentage = (parseFloat(weight) * 100).toFixed(1); - console.log(` ${percentage}% ${voteOptionNames[option as keyof typeof voteOptionNames]}`); - totalPercent += parseFloat(percentage); - } - - console.log(`Total: ${totalPercent.toFixed(1)}%`); - - const tx = await governance.voteWeighted(proposalID, weightedOptions, { - gasLimit: 300000 // Higher gas limit for weighted voting - }); - - console.log('Transaction submitted:', tx.hash); - const receipt = await tx.wait(); - - console.log('✅ Weighted vote cast successfully!'); - console.log(`Transaction hash: ${receipt.hash}`); - console.log(`View on explorer: https://seitrace.com/tx/${receipt.hash}`); - } catch (error: any) { - console.error('Failed to cast weighted vote:', error); - - if (error.message.includes('weights must sum to 1')) { - throw new Error('Vote weights must sum to exactly 1.0. Check your weight calculations.'); - } else { - throw error; - } - } -} - -// Usage examples - -// Example 1: Split decision (60% Yes, 40% Abstain) -const splitVote = [ - { option: 1, weight: '0.6' }, // 60% Yes - { option: 2, weight: '0.4' } // 40% Abstain - // Total: 0.6 + 0.4 = 1.0 ✅ -]; - -await castWeightedVote(proposalID, splitVote); - -// Example 2: Complex distribution -const complexVote = [ - { option: 1, weight: '0.45' }, // 45% Yes - { option: 2, weight: '0.25' }, // 25% Abstain - { option: 3, weight: '0.20' }, // 20% No - { option: 4, weight: '0.10' } // 10% NoWithVeto - // Total: 0.45 + 0.25 + 0.20 + 0.10 = 1.0 ✅ -]; - -await castWeightedVote(proposalID, complexVote); - -// Example 3: This would FAIL - weights don't sum to 1.0 -const badVote = [ - { option: 1, weight: '0.7' }, - { option: 2, weight: '0.2' } - // Total: 0.7 + 0.2 = 0.9 ❌ WILL FAIL -]; -// await castWeightedVote(proposalID, badVote); // Don't run this! -``` - -### Deposit to a Proposal - -```typescript copy -// Add deposit to help proposal reach minimum threshold -async function addDepositToProposal(proposalID: bigint, depositAmount: bigint): Promise { - if (depositAmount <= 0) { - throw new Error('Deposit amount must be greater than 0'); - } - - // Check balance - const userAddress = await signer.getAddress(); - const balance = await provider.getBalance(userAddress); - const totalCost = depositAmount + ethers.parseUnits('0.05', 18); // Add gas buffer - - if (balance < totalCost) { - throw new Error(`Insufficient balance. Need ${ethers.formatEther(totalCost)} SEI (including gas), ` + `but have ${ethers.formatEther(balance)} SEI`); - } - - try { - console.log('Adding deposit to proposal...'); - console.log(`Proposal ID: ${proposalID.toString()}`); - console.log(`Deposit amount: ${ethers.formatEther(depositAmount)} SEI`); - - const tx = await governance.deposit(proposalID, { - value: depositAmount, - gasLimit: 200000 - }); - - console.log('Transaction submitted:', tx.hash); - const receipt = await tx.wait(); - - console.log('✅ Deposit added successfully!'); - console.log(`Proposal ID: ${proposalID.toString()}`); - console.log(`Amount deposited: ${ethers.formatEther(depositAmount)} SEI`); - console.log(`Transaction hash: ${receipt.hash}`); - console.log(`View proposal: https://sei.explorers.guru/proposal/${proposalID.toString()}`); - } catch (error: any) { - console.error('Failed to add deposit:', error); - - if (error.message.includes('proposal does not exist')) { - throw new Error('Proposal does not exist'); - } else if (error.message.includes('deposit period ended')) { - throw new Error('Deposit period has ended for this proposal'); - } else { - throw error; - } - } -} - -// Usage example -const proposalID = 1n; -const additionalDeposit = ethers.parseUnits('1000', 18); // 1,000 SEI -await addDepositToProposal(proposalID, additionalDeposit); -``` - -## Complete Integration Example - -Create a comprehensive governance interaction script: - -```javascript copy filename="governance-mainnet-demo.js" -const { ethers } = require('ethers'); -const { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } = require('@sei-js/evm'); -require('dotenv').config(); - -// Validation functions -function isValidVoteOption(option) { - return option >= 1 && option <= 4; -} - -function validateWeightedOptions(options) { - const totalWeight = options.reduce((sum, opt) => sum + parseFloat(opt.weight), 0); - const isValidTotal = Math.abs(totalWeight - 1.0) < 0.000001; - const allOptionsValid = options.every((opt) => isValidVoteOption(opt.option)); - return isValidTotal && allOptionsValid; -} - -function validateProposalJSON(proposalJSON) { - try { - const proposal = JSON.parse(proposalJSON); - return proposal.title && proposal.description && typeof proposal.title === 'string' && typeof proposal.description === 'string' && proposal.title.length > 0 && proposal.description.length > 0; - } catch { - return false; - } -} - -function validateDepositAmount(depositAmount, isExpedited = false) { - const MINIMUM_DEPOSIT = ethers.parseUnits('3500', 18); // 3,500 SEI - const EXPEDITED_DEPOSIT = ethers.parseUnits('7000', 18); // 7,000 SEI - const required = isExpedited ? EXPEDITED_DEPOSIT : MINIMUM_DEPOSIT; - return depositAmount >= required; -} - -async function main() { - try { - // Connect to Sei Mainnet - console.log('🌐 Connecting to Sei Mainnet...'); - const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); - - // Use your private key from environment variable - const privateKey = process.env.PRIVATE_KEY; - if (!privateKey) { - throw new Error('Please set PRIVATE_KEY in .env file'); - } - - const wallet = new ethers.Wallet(privateKey, provider); - const userAddress = wallet.address; - - console.log(`👤 Wallet address: ${userAddress}`); - - // Check balance - const balance = await provider.getBalance(userAddress); - const balanceEther = ethers.formatEther(balance); - console.log(`💰 Wallet balance: ${balanceEther} SEI\n`); - - // Verify sufficient balance for mainnet governance - const minimumRequired = ethers.parseUnits('3600', 18); // 3,600 SEI (3,500 + buffer) - if (balance < minimumRequired) { - console.log('❌ Insufficient balance for mainnet governance participation'); - console.log(` Required: At least 3,600 SEI (3,500 for proposal + 100 buffer)`); - console.log(` Current: ${balanceEther} SEI`); - console.log(' Please acquire more SEI tokens from exchanges or DEXs'); - return; - } - - // Initialize governance precompile contract - const governance = new ethers.Contract(GOVERNANCE_PRECOMPILE_ADDRESS, GOVERNANCE_PRECOMPILE_ABI, wallet); - - console.log('🏛️ === Sei Mainnet Governance Demo ===\n'); - - // 1. Submit a governance proposal - console.log('1️⃣ Submitting a governance proposal...'); - - const proposal = { - title: 'Demo: EVM Governance Integration Test', - description: `This is a demonstration proposal submitted through the governance precompile to showcase seamless EVM-Cosmos governance integration. - -## Purpose -This proposal demonstrates: -- EVM smart contract governance participation -- Cross-environment compatibility -- Native governance functionality through precompiles - -## Technical Details -- Submitted via governance precompile at ${GOVERNANCE_PRECOMPILE_ADDRESS} -- Using ethers.js for transaction management -- Mainnet deployment with real SEI deposits - -## Voting Recommendation -This is a test proposal. Vote according to your preference to test the system.`, - type: 'Text', - is_expedited: false - }; - - const proposalJSON = JSON.stringify(proposal); - - // Validate proposal - if (!validateProposalJSON(proposalJSON)) { - throw new Error('Invalid proposal JSON structure'); - } - - // Use mainnet-appropriate deposit - const depositAmount = ethers.parseUnits('3500', 18); // Exactly minimum required - - if (!validateDepositAmount(depositAmount)) { - throw new Error('Deposit amount insufficient for mainnet'); - } - - try { - console.log(` 📋 Title: ${proposal.title}`); - console.log(` 💵 Deposit: ${ethers.formatEther(depositAmount)} SEI`); - console.log(' 🚀 Submitting transaction...'); - - const submitTx = await governance.submitProposal(proposalJSON, { - value: depositAmount, - gasLimit: 500000, - gasPrice: ethers.parseUnits('100', 'gwei') // Set appropriate gas price - }); - - console.log(` 📝 Transaction hash: ${submitTx.hash}`); - console.log(' ⏳ Waiting for confirmation...'); - - const submitReceipt = await submitTx.wait(); - - // Extract proposal ID safely - let proposalID = null; - for (const log of submitReceipt.logs) { - try { - const parsed = governance.interface.parseLog(log); - if (parsed && parsed.name === 'ProposalSubmitted') { - proposalID = parsed.args.proposalID; - break; - } - } catch { - continue; - } - } - - if (!proposalID) { - console.log(' ⚠️ Could not extract proposal ID. Check transaction manually.'); - console.log(` 🔗 Transaction: https://seitrace.com/tx/${submitReceipt.hash}`); - return; - } - - console.log(` ✅ Proposal submitted successfully!`); - console.log(` 🆔 Proposal ID: ${proposalID.toString()}`); - console.log(` 🔗 View proposal: https://sei.explorers.guru/proposal/${proposalID.toString()}`); - - // 2. Demonstrate voting - console.log(`\n2️⃣ Voting on proposal ${proposalID.toString()}...`); - - const voteOption = 1; // Yes - const voteNames = { 1: 'Yes', 2: 'Abstain', 3: 'No', 4: 'NoWithVeto' }; - - console.log(` 🗳️ Vote option: ${voteNames[voteOption]}`); - console.log(' 🚀 Submitting vote...'); - - const voteTx = await governance.vote(proposalID, voteOption, { - gasLimit: 200000, - gasPrice: ethers.parseUnits('100', 'gwei') - }); - - console.log(` 📝 Transaction hash: ${voteTx.hash}`); - await voteTx.wait(); - - console.log(' ✅ Vote cast successfully!'); - console.log(` 🔗 Transaction: https://seitrace.com/tx/${voteTx.hash}`); - - // 3. Demonstrate weighted voting - console.log(`\n3️⃣ Casting weighted vote on proposal ${proposalID.toString()}...`); - - const weightedOptions = [ - { option: 1, weight: '0.7' }, // 70% Yes - { option: 2, weight: '0.2' }, // 20% Abstain - { option: 3, weight: '0.1' } // 10% No - // CRITICAL: Total = 0.7 + 0.2 + 0.1 = 1.0 ✅ - ]; - - if (!validateWeightedOptions(weightedOptions)) { - throw new Error('CRITICAL: Invalid weighted options - transaction would fail'); - } - - console.log(' 📊 Vote distribution:'); - weightedOptions.forEach(({ option, weight }) => { - const percent = (parseFloat(weight) * 100).toFixed(1); - console.log(` ${percent}% ${voteNames[option]}`); - }); - - console.log(' 🚀 Submitting weighted vote...'); - - const weightedTx = await governance.voteWeighted(proposalID, weightedOptions, { - gasLimit: 300000, - gasPrice: ethers.parseUnits('100', 'gwei') - }); - - console.log(` 📝 Transaction hash: ${weightedTx.hash}`); - await weightedTx.wait(); - - console.log(' ✅ Weighted vote cast successfully!'); - console.log(` 🔗 Transaction: https://seitrace.com/tx/${weightedTx.hash}`); - - // 4. Add additional deposit - console.log(`\n4️⃣ Adding deposit to proposal ${proposalID.toString()}...`); - - const additionalDeposit = ethers.parseUnits('500', 18); // 500 SEI - - console.log(` 💵 Additional deposit: ${ethers.formatEther(additionalDeposit)} SEI`); - console.log(' 🚀 Submitting deposit...'); - - const depositTx = await governance.deposit(proposalID, { - value: additionalDeposit, - gasLimit: 200000, - gasPrice: ethers.parseUnits('100', 'gwei') - }); - - console.log(` 📝 Transaction hash: ${depositTx.hash}`); - await depositTx.wait(); - - console.log(' ✅ Additional deposit added successfully!'); - console.log(` 🔗 Transaction: https://seitrace.com/tx/${depositTx.hash}`); - - // 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('🔗 View all governance proposals: https://sei.explorers.guru/proposals'); - } catch (error) { - if (error.message.includes('insufficient funds')) { - console.log('\n❌ Insufficient funds for governance participation'); - console.log(' Ensure you have enough SEI for deposits and gas fees'); - } else if (error.message.includes('already voted')) { - console.log('\n⚠️ You have already voted on this proposal'); - } else { - throw error; - } - } - } catch (error) { - console.error('\n❌ Error:', error.message); - console.log('\n🔧 Troubleshooting tips:'); - console.log('1. Ensure PRIVATE_KEY is set in .env file'); - console.log('2. Verify sufficient SEI balance (minimum 3,600 SEI)'); - console.log('3. Check network connectivity to Sei mainnet'); - console.log('4. Confirm wallet has staked SEI for voting power'); - process.exit(1); - } -} - -// Run the demo -main().catch(console.error); -``` - -### Running the Example - -1. **Create a new directory and initialize npm:** - -```bash copy -mkdir sei-governance-mainnet -cd sei-governance-mainnet -npm init -y -``` - -2. **Install dependencies:** - -```bash copy -npm install ethers @sei-js/precompiles@^2.1.2 dotenv -``` - -3. **Create a `.env` file:** - -```bash copy -PRIVATE_KEY=your_private_key_here -``` - -4. **Create the demo file:** - Copy the complete integration example above into `governance-mainnet-demo.js` - -5. **Ensure you have sufficient SEI:** - - - Minimum 3,500 SEI for proposal deposit - - Additional SEI for gas fees and optional additional deposits - - Staked SEI for voting power - -6. **Run the script:** - -```bash copy -node governance-mainnet-demo.js -``` - -### Expected Output - -``` -🌐 Connecting to Sei Mainnet... -👤 Wallet address: 0x742d35Cc6634C0532925a3b844Bc6789e065f3B -💰 Wallet balance: 5000.5 SEI - -🏛️ === Sei Mainnet Governance Demo === - -1️⃣ Submitting a governance proposal... - 📋 Title: Demo: EVM Governance Integration Test - 💵 Deposit: 3500.0 SEI - 🚀 Submitting transaction... - 📝 Transaction hash: 0x123...abc - ⏳ Waiting for confirmation... - ✅ Proposal submitted successfully! - 🆔 Proposal ID: 42 - 🔗 View proposal: https://sei.explorers.guru/proposal/42 - -2️⃣ Voting on proposal 42... - 🗳️ Vote option: Yes - 🚀 Submitting vote... - 📝 Transaction hash: 0x456...def - ✅ Vote cast successfully! - 🔗 Transaction: https://seitrace.com/tx/0x456...def - -3️⃣ Casting weighted vote on proposal 42... - 📊 Vote distribution: - 70.0% Yes - 20.0% Abstain - 10.0% No - 🚀 Submitting weighted vote... - 📝 Transaction hash: 0x789...ghi - ✅ Weighted vote cast successfully! - 🔗 Transaction: https://seitrace.com/tx/0x789...ghi - -4️⃣ Adding deposit to proposal 42... - 💵 Additional deposit: 500.0 SEI - 🚀 Submitting deposit... - 📝 Transaction hash: 0xabc...123 - ✅ Additional deposit added successfully! - 🔗 Transaction: https://seitrace.com/tx/0xabc...123 - -🎉 === Governance Demo Completed Successfully! === -📊 Proposal ID: 42 -💰 Total deposited: 4000.0 SEI -🔗 View all governance proposals: https://sei.explorers.guru/proposals -``` - -## Troubleshooting - -### Common Issues and Solutions - -#### Insufficient Deposit - -```typescript copy -// Check minimum deposit requirement based on proposal type -const MINIMUM_DEPOSIT = ethers.parseUnits('3500', 18); // 3,500 SEI minimum -const EXPEDITED_DEPOSIT = ethers.parseUnits('7000', 18); // 7,000 SEI for expedited - -function checkDepositRequirement(depositAmount: bigint, isExpedited: boolean = false): void { - const requiredAmount = isExpedited ? EXPEDITED_DEPOSIT : MINIMUM_DEPOSIT; - const networkName = 'mainnet'; - - if (depositAmount < requiredAmount) { - const required = ethers.formatEther(requiredAmount); - const provided = ethers.formatEther(depositAmount); - throw new Error(`Deposit too low for ${networkName}${isExpedited ? ' (expedited)' : ''}. Required: ${required} SEI, Provided: ${provided} SEI`); - } -} -``` - -#### No Voting Power - -```typescript copy -// Check if user has staked SEI for voting power -async function checkVotingEligibility(userAddress: string): Promise { - console.log('Checking voting eligibility...'); - - // Note: You would typically integrate with staking queries here - // This is a placeholder for the actual implementation - - console.log('⚠️ Reminder: You must have staked SEI to participate in governance voting'); - console.log(' Liquid SEI does not provide voting power'); - console.log(' Delegate your SEI to validators to gain voting power'); - console.log(' Visit: https://app.sei.io/stake to stake your tokens'); -} -``` - -#### Invalid Vote Option - -```typescript copy -// Validate vote option with descriptive error -function validateVoteOptionWithDescription(option: number): void { - const voteOptions = { - 1: 'Yes - Vote in favor of the proposal', - 2: 'Abstain - Neutral vote that counts toward quorum', - 3: 'No - Vote against the proposal', - 4: 'NoWithVeto - Strong opposition that can burn deposits' - }; - - if (!(option in voteOptions)) { - const validOptions = Object.entries(voteOptions) - .map(([key, value]) => `${key}: ${value}`) - .join('\n'); - - throw new Error(`Invalid vote option: ${option}\n\nValid options:\n${validOptions}`); - } -} -``` - -#### Weighted Vote Validation - -```typescript copy -// Comprehensive weighted vote validation with helpful errors -function validateWeightedVoteComprehensive(options: Array<{ option: number; weight: string }>): void { - if (options.length === 0) { - throw new Error('At least one vote option is required'); - } - - if (options.length > 4) { - throw new Error('Cannot have more than 4 vote options'); - } - - let totalWeight = 0; - const seenOptions = new Set(); - const voteOptionNames = { 1: 'Yes', 2: 'Abstain', 3: 'No', 4: 'NoWithVeto' }; - - for (let i = 0; i < options.length; i++) { - const { option, weight } = options[i]; - - // Validate option - if (option < 1 || option > 4 || !Number.isInteger(option)) { - throw new Error( - `Invalid vote option at index ${i}: ${option}. Must be integer 1-4\n` + - `Valid options: ${Object.entries(voteOptionNames) - .map(([k, v]) => `${k}=${v}`) - .join(', ')}` - ); - } - - // Check for duplicate options - if (seenOptions.has(option)) { - throw new Error(`Duplicate vote option: ${option} (${voteOptionNames[option as keyof typeof voteOptionNames]})\n` + `Each vote option can only be used once`); - } - seenOptions.add(option); - - // Validate weight - const weightNum = parseFloat(weight); - if (isNaN(weightNum)) { - throw new Error(`Invalid weight at index ${i}: "${weight}" is not a valid number`); - } - - if (weightNum <= 0) { - throw new Error(`Invalid weight at index ${i}: ${weight}. Weight must be greater than 0`); - } - - if (weightNum > 1) { - throw new Error(`Invalid weight at index ${i}: ${weight}. Weight cannot exceed 1.0`); - } - - totalWeight += weightNum; - } - - // Check total weight - CRITICAL for transaction success - if (Math.abs(totalWeight - 1.0) > 0.000001) { - const weightSummary = options.map(({ option, weight }) => `${voteOptionNames[option as keyof typeof voteOptionNames]}: ${weight}`).join(', '); - - throw new Error(`CRITICAL: Total weight must equal exactly 1.0, got ${totalWeight.toFixed(6)}\n` + `Current weights: ${weightSummary}\n` + `Sum: ${totalWeight.toFixed(6)}\n` + `Transaction will fail if weights don't sum to 1.0`); - } -} -``` - -### Error Code Reference - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Error - - Cause - - Solution -
- proposal does not exist - - Invalid proposal ID or proposal expired - - Verify proposal ID and ensure voting period is still active -
- inactive proposal - - Voting or deposit period has ended - - Check proposal status and timeline -
- insufficient deposit - - Deposit below 3,500 SEI - - Increase deposit to the minimum required amount -
- invalid vote option - - Vote option not 1-4 - - Use a valid option (1-Yes, 2-Abstain, 3-No, 4-NoWithVeto) -
- already voted - - Address has already voted on this proposal - - Each address can vote only once per proposal -
- weights must sum to 1 - - Weighted votes don’t sum to 1.0 - - CRITICAL — adjust weights so they total exactly 1.0 -
- insufficient voting power - - No staked SEI - - Delegate SEI to validators to gain voting power -
- deposit period ended - - Deposits no longer accepted - - Deposit period closes 2 days after submission -
-
- - - -**Need Help?** If you encounter issues: - -- **Discord:** [Sei Community Discord](https://discord.gg/sei) -- **GitHub:** [Sei Protocol Repository](https://github.com/sei-protocol/sei-chain) -- **Explorer:** [View Proposals on Sei Explorer](https://sei.explorers.guru/proposals) - - diff --git a/content/evm/precompiles/json.mdx b/content/evm/precompiles/json.mdx deleted file mode 100644 index ebaf6a4b..00000000 --- a/content/evm/precompiles/json.mdx +++ /dev/null @@ -1,831 +0,0 @@ ---- -title: 'JSON Precompile Usage' -description: "Learn how to interact with Sei's JSON precompile through ethers.js, enabling efficient parsing, manipulation, and querying of JSON data within EVM smart contracts to overcome Solidity's native limitations for handling complex data structures." -keywords: ['json precompile', 'ethers.js', 'json parsing', 'smart contract data', 'sei evm', 'blockchain data handling', 'data extraction'] ---- - -import { Callout } from 'nextra/components'; - -# JSON Precompile Usage - -**Address:** `0x0000000000000000000000000000000000001003` - -The Sei JSON precompile allows EVM applications to efficiently parse and query JSON data directly within smart contracts. This enables complex data handling capabilities that are not natively available in Solidity, making it easier to work with structured data from external sources or APIs. - -**What is a precompile?** A precompile is a special smart contract deployed at a fixed address by the Sei protocol itself, that exposes custom native chain logic to EVM-based applications. It acts like a regular contract from the EVM's perspective, but executes privileged, low-level logic efficiently. - -## How Does the JSON Precompile Work? - -The JSON precompile at address `0x0000000000000000000000000000000000001003` exposes functions like `extractAsBytes()`, `extractAsBytesList()`, and `extractAsUint256()`. - -- **Direct Integration:** EVM contracts and dApps can parse JSON data like any other smart contract method. -- **Native Execution:** JSON parsing is executed at the native level for maximum efficiency. -- **Seamless Bridge:** No need for complex workarounds or external libraries for JSON handling. - -## Use Cases - -- **Oracle Integration:** Parse complex oracle responses containing multiple data points. -- **DeFi Applications:** Process structured price feeds and market data. -- **Gaming:** Handle complex game state and player data stored in JSON format. -- **Cross-Chain Communication:** Parse messages and data from other chains. -- **NFT Metadata:** Extract and manipulate NFT metadata stored in JSON format. - -## Functions - -The JSON precompile exposes the following functions: - -### Query Functions - -```solidity -/// Extracts data as bytes from the input using the specified key. -/// @param input The input data. -/// @param key The key to extract. -/// @return The extracted data as bytes. -function extractAsBytes( - bytes memory input, - string memory key -) external view returns (bytes memory response); - -/// Extracts data as a list of bytes from the input using the specified key. -/// @param input The input data. -/// @param key The key to extract. -/// @return The extracted data as bytes collection. -function extractAsBytesList( - bytes memory input, - string memory key -) external view returns (bytes[] memory response); - -/// Extracts data as a uint256 from the input using the specified key. -/// @param input The input data. -/// @param key The key to extract. -/// @return The extracted uint256. -function extractAsUint256( - bytes memory input, - string memory key -) external view returns (uint256 response); -``` - -## Using the Precompile - -### Setup - -#### Prerequisites - -Before getting started, ensure you have: - -- **Node.js** (v16 or higher) -- **npm** or **yarn** package manager -- **MetaMask** or compatible EVM wallet configured for Sei Mainnet -- **SEI tokens** for gas fees - -#### Install Dependencies - -Install the required packages for interacting with Sei precompiles: - -```bash copy -# Install ethers.js for smart contract interactions -npm install ethers - -# Install Sei EVM bindings for precompile addresses and ABIs -npm install @sei-js/precompiles@^2.1.2 - -# Install dotenv for managing private keys (recommended for security) -npm install dotenv -``` - -#### Import Precompile Components - -```typescript copy -// Import JSON precompile address and ABI -// View the entire ABI here: https://github.com/sei-protocol/sei-chain/tree/main/precompiles/json -import { JSON_PRECOMPILE_ABI, JSON_PRECOMPILE_ADDRESS } from '@sei-js/precompiles'; -import { ethers } from 'ethers'; -``` - -**Precompile Address:** The JSON precompile is deployed at `0x0000000000000000000000000000000000001003` - -### Contract Initialization - -Set up your provider, signer, and contract instance: - -```typescript copy -// Using MetaMask as the signer and provider (browser environment) -const provider = new ethers.BrowserProvider(window.ethereum); -await provider.send('eth_requestAccounts', []); -const signer = await provider.getSigner(); - -// Or for Node.js environment -const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); -const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider); - -// Create a contract instance for the JSON precompile -const jsonPrecompile = new ethers.Contract(JSON_PRECOMPILE_ADDRESS, JSON_PRECOMPILE_ABI, signer); -``` - -## Data Type Handling - -The JSON precompile has specific limitations for different data types: - -### Supported Data Types - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Function - - Supports - - Limitations -
- extractAsBytes - - Strings, Objects, Arrays - - Returns raw bytes - needs conversion -
- extractAsUint256 - - Integers only - - No decimals, booleans, or negative numbers -
- extractAsBytesList - - Arrays of strings/objects - - Each element returned as bytes -
-
- -### Data Type Conversion Strategies - -```typescript copy -// For decimal numbers - store as integers with known precision -const priceData = { price: 250 }; // Represents 2.50 with 2 decimal places -const price = await jsonPrecompile.extractAsUint256(inputData, 'price'); -const actualPrice = parseFloat(price.toString()) / 100; // Convert back to 2.50 - -// For boolean values - use 0/1 integers -const statusData = { isActive: 1 }; // 1 = true, 0 = false -const isActiveInt = await jsonPrecompile.extractAsUint256(inputData, 'isActive'); -const isActive = isActiveInt === 1n; - -// For complex objects - extract as bytes and parse -const userData = { user: { name: 'Alice', balance: 1000 } }; -const userBytes = await jsonPrecompile.extractAsBytes(inputData, 'user'); -const userString = ethers.toUtf8String(userBytes); -const userObject = JSON.parse(userString); -``` - -## Error Handling Utilities - -Create comprehensive error handling for all extraction functions: - -```typescript copy -// Safe extraction for bytes data -async function safeExtractBytes(jsonPrecompile: ethers.Contract, data: any, key: string, defaultValue: string = ''): Promise { - try { - const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); - const result = await jsonPrecompile.extractAsBytes(inputData, key); - return ethers.toUtf8String(result); - } catch (error: any) { - console.log(`Key "${key}" not found or invalid, using default value`); - return defaultValue; - } -} - -// Safe extraction for uint256 data -async function safeExtractUint256(jsonPrecompile: ethers.Contract, data: any, key: string, defaultValue: bigint = 0n): Promise { - try { - const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); - const result = await jsonPrecompile.extractAsUint256(inputData, key); - return result; - } catch (error: any) { - console.log(`Key "${key}" not found or not a valid integer, using default value`); - return defaultValue; - } -} - -// Safe extraction for array data -async function safeExtractBytesList(jsonPrecompile: ethers.Contract, data: any, key: string, defaultValue: string[] = []): Promise { - try { - const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); - const result = await jsonPrecompile.extractAsBytesList(inputData, key); - return result.map((bytes: any) => ethers.toUtf8String(bytes)); - } catch (error: any) { - console.log(`Key "${key}" not found or not a valid array, using default value`); - return defaultValue; - } -} -``` - -## Step-by-Step Guide: Using the JSON Precompile - -### Extract String Data - -```typescript copy -// JSON data with string values -const data = { name: 'Sei Token', symbol: 'SEI' }; -const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); - -// Extract string using safe method -const name = await safeExtractBytes(jsonPrecompile, data, 'name'); -console.log('Name:', name); // Output: "Sei Token" - -// Or direct extraction with error handling -try { - const symbolBytes = await jsonPrecompile.extractAsBytes(inputData, 'symbol'); - const symbol = ethers.toUtf8String(symbolBytes); - console.log('Symbol:', symbol); // Output: "SEI" -} catch (error) { - console.error('Failed to extract symbol:', error.message); -} -``` - -### Extract Numeric Data - -```typescript copy -// JSON data with numbers (integers only) -const data = { price: 42, volume: 1000000 }; -const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); - -// Extract integer using safe method -const price = await safeExtractUint256(jsonPrecompile, data, 'price'); -console.log('Price:', price.toString()); // Output: "42" - -// Handle decimal numbers by using integer representation -const decimalData = { rate: 275 }; // Represents 2.75 with 2 decimal places -const rate = await safeExtractUint256(jsonPrecompile, decimalData, 'rate'); -const actualRate = Number(rate) / 100; // Convert to 2.75 -console.log('Rate:', actualRate); -``` - -### Extract Array Data - -```typescript copy -// JSON data with array -const data = { tokens: ['SEI', 'USDC', 'USDT'] }; -const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); - -// Extract array using safe method -const tokens = await safeExtractBytesList(jsonPrecompile, data, 'tokens'); -console.log('Tokens:', tokens); // Output: ["SEI", "USDC", "USDT"] - -// Handle mixed arrays by extracting as bytes and parsing -const mixedData = { values: [42, 'test', 100] }; -const valuesBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(mixedData)), 'values'); -const valuesString = ethers.toUtf8String(valuesBytes); -const valuesArray = JSON.parse(valuesString); -console.log('Mixed values:', valuesArray); // Output: [42, "test", 100] -``` - -### Extract Nested Data - -**Important:** The JSON precompile does not support dot notation for nested objects. Instead, extract the parent object and parse it manually. - -```typescript copy -// For nested data, extract the parent object first -const data = { - user: { - wallet: { - balance: 1000, - currency: 'SEI' - } - } -}; - -// Method 1: Extract parent object and parse manually (recommended) -const userBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(data)), 'user'); -const userData = JSON.parse(ethers.toUtf8String(userBytes)); -const balance = userData.wallet.balance; -const currency = userData.wallet.currency; - -console.log('Balance:', balance); // Output: 1000 -console.log('Currency:', currency); // Output: "SEI" - -// Method 2: Extract deeper nested object -const walletBytes = await jsonPrecompile.extractAsBytes( - ethers.toUtf8Bytes(JSON.stringify(data)), - 'wallet' // Direct key, not dot notation -); -const walletData = JSON.parse(ethers.toUtf8String(walletBytes)); -console.log('Wallet data:', walletData); // Output: { balance: 1000, currency: "SEI" } -``` - -## Complete Integration Example - -Create a comprehensive JSON parsing application for mainnet: - -```javascript copy filename="json-precompile-mainnet.js" -// json-precompile-mainnet.js -const { ethers } = require('ethers'); -const { JSON_PRECOMPILE_ABI, JSON_PRECOMPILE_ADDRESS } = require('@sei-js/precompiles'); -require('dotenv').config(); - -// Safe extraction utilities -async function safeExtractBytes(jsonPrecompile, data, key, defaultValue = '') { - try { - const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); - const result = await jsonPrecompile.extractAsBytes(inputData, key); - return ethers.toUtf8String(result); - } catch (error) { - console.log(` ⚠️ Key "${key}" not found, using default: "${defaultValue}"`); - return defaultValue; - } -} - -async function safeExtractUint256(jsonPrecompile, data, key, defaultValue = 0n) { - try { - const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); - const result = await jsonPrecompile.extractAsUint256(inputData, key); - return result; - } catch (error) { - console.log(` ⚠️ Key "${key}" not found or invalid integer, using default: ${defaultValue}`); - return defaultValue; - } -} - -async function safeExtractBytesList(jsonPrecompile, data, key, defaultValue = []) { - try { - const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); - const result = await jsonPrecompile.extractAsBytesList(inputData, key); - return result.map((bytes) => ethers.toUtf8String(bytes)); - } catch (error) { - console.log(` ⚠️ Key "${key}" not found or invalid array, using default: [${defaultValue.join(', ')}]`); - return defaultValue; - } -} - -async function main() { - try { - // Connect to Sei Mainnet - console.log('🌐 Connecting to Sei Mainnet...'); - const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); - - // Use private key from environment variables (secure approach) - const privateKey = process.env.PRIVATE_KEY; - if (!privateKey) { - throw new Error('Please set PRIVATE_KEY in .env file'); - } - - const wallet = new ethers.Wallet(privateKey, provider); - console.log(`👤 Wallet address: ${wallet.address}`); - - // Check balance - const balance = await provider.getBalance(wallet.address); - const balanceEther = ethers.formatEther(balance); - console.log(`💰 Wallet balance: ${balanceEther} SEI\n`); - - // Initialize JSON precompile contract - const jsonPrecompile = new ethers.Contract(JSON_PRECOMPILE_ADDRESS, JSON_PRECOMPILE_ABI, wallet); - - console.log('📄 === Sei Mainnet JSON Precompile Demo ===\n'); - - // Complex sample data - simulating a real-world oracle response - const oracleData = { - market: { - prices: { - SEI: 275, // 2.75 USD (stored as integer with 2 decimal places) - BTC: 5000000, // 50,000.00 USD - ETH: 300000 // 3,000.00 USD - }, - volume: { - daily: 1500000000, // $15M daily volume - weekly: 8500000000 // $85M weekly volume - } - }, - metadata: { - timestamp: '2024-12-27T12:00:00Z', - source: 'MainnetPriceOracle', - version: '2.1.0', - isLive: 1 // 1 = true, 0 = false - }, - assets: ['SEI', 'BTC', 'ETH', 'USDC'], - validators: { - active: 125, - total: 200 - } - }; - - console.log('📋 Input JSON Data:'); - console.log(JSON.stringify(oracleData, null, 2)); - console.log(); - - // 1. Extract string data with error handling - console.log('1️⃣ Extracting String Data...'); - - // Extract metadata object first, then parse individual fields - const metadataBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(oracleData)), 'metadata'); - const metadata = JSON.parse(ethers.toUtf8String(metadataBytes)); - - console.log(` 📊 Source: ${metadata.source}`); - console.log(` 🔢 Version: ${metadata.version}`); - console.log(` ⏰ Timestamp: ${metadata.timestamp}`); - console.log(` 🟢 Is Live: ${metadata.isLive === 1 ? 'Yes' : 'No'}`); - - // Test missing key - const missingKey = await safeExtractBytes(jsonPrecompile, oracleData, 'nonexistent', 'N/A'); - console.log(` ❓ Missing Key: ${missingKey}`); - - // 2. Extract numeric data with proper object parsing - console.log('\n2️⃣ Extracting Numeric Data...'); - - // Extract market object first - const marketBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(oracleData)), 'market'); - const market = JSON.parse(ethers.toUtf8String(marketBytes)); - - // Extract validators object - const validatorsBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(oracleData)), 'validators'); - const validators = JSON.parse(ethers.toUtf8String(validatorsBytes)); - - // Convert integers back to decimals - const seiPrice = market.prices.SEI / 100; // 2 decimal places - const btcPrice = market.prices.BTC / 100; // 2 decimal places - const isLive = metadata.isLive === 1; // Convert to boolean - - console.log(` 💰 SEI Price: ${seiPrice} (raw: ${market.prices.SEI})`); - console.log(` ₿ BTC Price: ${btcPrice.toLocaleString()} (raw: ${market.prices.BTC})`); - console.log(` 💹 Daily Volume: ${market.volume.daily.toLocaleString()}`); - console.log(` ✅ Active Validators: ${validators.active}`); - console.log(` 🔴 Is Live: ${isLive} (raw: ${metadata.isLive})`); - - // 3. Extract array data - console.log('\n3️⃣ Extracting Array Data...'); - const assets = await safeExtractBytesList(jsonPrecompile, oracleData, 'assets'); - const invalidArray = await safeExtractBytesList(jsonPrecompile, oracleData, 'invalid.array', ['DEFAULT']); - - console.log(` 🪙 Assets: [${assets.join(', ')}]`); - console.log(` ❌ Invalid Array: [${invalidArray.join(', ')}]`); - - // 4. Extract complex nested objects - console.log('\n4️⃣ Extracting Complex Nested Data...'); - console.log(' 📈 Market Data:'); - console.log(` Prices: SEI=${market.prices.SEI / 100}, BTC=${market.prices.BTC / 100}, ETH=${market.prices.ETH / 100}`); - console.log(` Volume: Daily=${market.volume.daily.toLocaleString()}, Weekly=${market.volume.weekly.toLocaleString()}`); - console.log(` 👥 Validator Data:`); - console.log(` Active: ${validators.active}, Total: ${validators.total}`); - - // 5. Gas optimization demonstration - console.log('\n5️⃣ Gas Usage Analysis...'); - const gasEstimates = { - small: 50000, // Simple key extraction - medium: 80000, // Nested object extraction - large: 120000 // Complex array/object processing - }; - - console.log(' ⛽ Recommended Gas Limits:'); - console.log(` Simple extraction: ${gasEstimates.small.toLocaleString()} gas`); - console.log(` Nested extraction: ${gasEstimates.medium.toLocaleString()} gas`); - console.log(` Complex processing: ${gasEstimates.large.toLocaleString()} gas`); - - // 6. Performance test with corrected key extraction - console.log('\n6️⃣ Performance & Error Testing...'); - const testCases = [ - { key: 'metadata', expected: 'object', description: 'metadata object' }, - { key: 'market', expected: 'object', description: 'market data' }, - { key: 'assets', expected: 'array', description: 'assets array' }, - { key: 'nonexistent', expected: 'error', description: 'missing key' } - ]; - - for (const testCase of testCases) { - try { - const start = Date.now(); - const result = await safeExtractBytes(jsonPrecompile, oracleData, testCase.key); - const duration = Date.now() - start; - const preview = result.length > 50 ? result.substring(0, 47) + '...' : result; - console.log(` ✅ ${testCase.description}: "${preview}" (${duration}ms)`); - } catch (error) { - console.log(` ❌ ${testCase.description}: ${error.message}`); - } - } - - console.log('\n🎉 === JSON Precompile Demo Completed Successfully! ==='); - } catch (error) { - console.error('\n❌ Error:', error.message); - console.log('\n🔧 Troubleshooting tips:'); - console.log('1. Ensure PRIVATE_KEY is set in .env file'); - console.log('2. Verify sufficient SEI balance for gas fees'); - console.log('3. Check network connectivity to Sei mainnet'); - console.log('4. Validate JSON structure before processing'); - process.exit(1); - } -} - -// Run the demo -main().catch(console.error); -``` - -### Running the Mainnet Example - -1. **Create a new directory and initialize npm:** - -```bash copy -mkdir sei-json-mainnet -cd sei-json-mainnet -npm init -y -``` - -2. **Install dependencies:** - -```bash copy -npm install ethers @sei-js/precompiles@^2.1.2 dotenv -``` - -3. **Create a `.env` file:** - -```bash copy -PRIVATE_KEY=your_private_key_here -``` - -**Security Note:** Never commit private keys to version control! The `.env` file should be added to your `.gitignore`. - -4. **Create the demo file:** - Copy the complete integration example above into `json-precompile-mainnet.js` - -5. **Ensure you have SEI tokens:** - - - SEI for gas fees (small amounts needed for view functions) - -6. **Run the script:** - -```bash copy -node json-precompile-mainnet.js -``` - -### Expected Output - -``` -🌐 Connecting to Sei Mainnet... -👤 Wallet address: 0x742d35Cc6634C0532925a3b844Bc6789e065f3B -💰 Wallet balance: 15.5 SEI - -📄 === Sei Mainnet JSON Precompile Demo === - -📋 Input JSON Data: -{ - "market": { - "prices": { - "SEI": 275, - "BTC": 5000000, - "ETH": 300000 - }, - "volume": { - "daily": 1500000000, - "weekly": 8500000000 - } - }, - "metadata": { - "timestamp": "2024-12-27T12:00:00Z", - "source": "MainnetPriceOracle", - "version": "2.1.0", - "isLive": 1 - }, - "assets": ["SEI", "BTC", "ETH", "USDC"], - "validators": { - "active": 125, - "total": 200 - } -} - -1️⃣ Extracting String Data... - 📊 Source: MainnetPriceOracle - 🔢 Version: 2.1.0 - ⏰ Timestamp: 2024-12-27T12:00:00Z - ⚠️ Key "nonexistent.key" not found, using default: "N/A" - ❓ Missing Key: N/A - -2️⃣ Extracting Numeric Data... - 💰 SEI Price: $2.75 (raw: 275) - ₿ BTC Price: $50,000 (raw: 5000000) - 💹 Daily Volume: $1,500,000,000 - ✅ Active Validators: 125 - 🔴 Is Live: true (raw: 1) - -3️⃣ Extracting Array Data... - 🪙 Assets: [SEI, BTC, ETH, USDC] - ⚠️ Key "invalid.array" not found or invalid array, using default: [DEFAULT] - ❌ Invalid Array: [DEFAULT] - -4️⃣ Extracting Complex Nested Data... - 📈 Market Data: - Prices: SEI=$2.75, BTC=$50000, ETH=$3000 - Volume: Daily=$1,500,000,000, Weekly=$8,500,000,000 - -5️⃣ Gas Usage Analysis... - ⛽ Recommended Gas Limits: - Simple extraction: 50,000 gas - Nested extraction: 80,000 gas - 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) - -🎉 === JSON Precompile Demo Completed Successfully! === -``` - -## Advanced Usage Examples - -### Oracle Price Feed Integration - -```typescript copy -// Advanced oracle integration with comprehensive error handling -class SeiPriceOracle { - private jsonPrecompile: ethers.Contract; - - constructor(jsonPrecompile: ethers.Contract) { - this.jsonPrecompile = jsonPrecompile; - } - - async parsePriceFeed(oracleResponse: any): Promise<{ - prices: Map; - timestamp: string; - isValid: boolean; - }> { - try { - // Extract timestamp - const timestamp = await safeExtractBytes(this.jsonPrecompile, oracleResponse, 'metadata.timestamp'); - - // Extract validation flag - const isValidRaw = await safeExtractUint256(this.jsonPrecompile, oracleResponse, 'metadata.isValid'); - const isValid = isValidRaw === 1n; - - // 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); - } - - return { prices, timestamp, isValid }; - } catch (error) { - console.error('Failed to parse oracle response:', error); - return { - prices: new Map(), - timestamp: '', - isValid: false - }; - } - } -} -``` - -## Troubleshooting - -### Common Issues and Solutions - -#### Key Not Found - -```typescript copy -// Comprehensive error handling for missing keys -async function handleMissingKeys(jsonPrecompile: ethers.Contract, data: any) { - const keys = ['required.key', 'optional.key', 'deeply.nested.key']; - - for (const key of keys) { - try { - const result = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(data)), key); - console.log(`✅ ${key}: ${ethers.toUtf8String(result)}`); - } catch (error: any) { - if (error.message.includes('key not found')) { - console.log(`⚠️ Key "${key}" not found in JSON structure`); - } else { - console.log(`❌ Error extracting "${key}": ${error.message}`); - } - } - } -} -``` - -### Error Code Reference - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Error - - Cause - - Solution -
- key not found - - Specified key doesn't exist in JSON - - Verify key path and JSON structure -
- invalid JSON format - - Malformed JSON input - - Use JSON.stringify() to ensure valid format -
- execution reverted - - Gas limit too low - - Increase gas limit based on data complexity -
- invalid integer - - Non-numeric value for extractAsUint256 - - Ensure the value is a valid integer -
- out of gas - - Insufficient gas for large JSON - - Use calculateGasLimit() for dynamic estimation -
-
- -## Key Considerations and Tricks - - - -- **Integers Only:** `extractAsUint256` only handles integers - no decimals, booleans, or negative numbers -- **Decimal Handling:** Store decimal numbers as integers with known precision (e.g., 275 for 2.75 with 2 decimal places) -- **Boolean Values:** Use 0/1 integers to represent false/true -- **Key Paths:** Extract parent objects first, then parse manually - dot notation may not be supported -- **Arrays:** Must use `extractAsBytesList()` for array data -- **Gas Costs:** Large JSON objects require higher gas limits -- **Encoding:** Always use UTF-8 encoding with `ethers.toUtf8Bytes()` -- **Error Handling:** Always implement fallback values for production applications - - - -**Need Help?** If you encounter issues not covered here, check the [Sei Discord](https://discord.gg/sei) or [GitHub repository](https://github.com/sei-protocol/sei-chain) for community support. diff --git a/content/evm/precompiles/oracle.mdx b/content/evm/precompiles/oracle.mdx deleted file mode 100644 index 9abb1c96..00000000 --- a/content/evm/precompiles/oracle.mdx +++ /dev/null @@ -1,720 +0,0 @@ ---- -title: 'Oracle Precompile Usage' -description: "Learn how to interact with Sei's Oracle precompile through ethers.js, enabling real-time access to exchange rates, TWAPs, and other economic data directly in your EVM smart contracts for DeFi applications." -keywords: ['oracle precompile', 'ethers.js', 'price feeds', 'exchange rates', 'twap', 'defi development', 'sei evm', 'blockchain oracles'] ---- - -import { Callout } from 'nextra/components'; - -# Oracle Precompile Usage - -**Address:** `0x0000000000000000000000000000000000001008` - -The Sei Oracle precompile enables EVM applications to query real-time price feed data directly from Sei's native Oracle module. This provides access to exchange rates, Time-Weighted Average Prices (TWAPs), and other economic data essential for DeFi applications. - -**What is a precompile?** A precompile is a special smart contract deployed at a fixed address by the Sei protocol itself, that exposes custom native chain logic to EVM-based applications. It acts like a regular contract from the EVM's perspective, but executes privileged, low-level logic efficiently. - -## How Does the Oracle Precompile Work? - -The Oracle precompile at address `0x0000000000000000000000000000000000001008` exposes functions like `getExchangeRates()` and `getOracleTwaps()`. - -- **Direct Integration:** EVM contracts and dApps can query price data like any other smart contract method. -- **Native Execution:** Oracle queries are executed at the Cosmos SDK level for maximum efficiency. -- **Real-Time Data:** Access live price feeds maintained by Sei's validator network. - -## Use Cases - -- **DeFi Protocols:** Build lending platforms, DEXs, and derivatives with reliable price feeds. -- **Stablecoins:** Implement price-stable tokens with accurate exchange rate data. -- **Liquidation Systems:** Create robust liquidation mechanisms using real-time prices. -- **Trading Bots:** Develop automated trading strategies with TWAP data. -- **Risk Management:** Build portfolio management tools with accurate asset valuations. - -## Functions - -The Oracle precompile exposes the following functions: - -### Query Functions - -```solidity -struct OracleExchangeRate { - string exchangeRate; - string lastUpdate; - int64 lastUpdateTimestamp; -} - -struct DenomOracleExchangeRatePair { - string denom; - OracleExchangeRate oracleExchangeRateVal; -} - -/// Retrieves the exchange rates for all denominations. -/// @return An array of denomination and exchange rate pairs. -function getExchangeRates() external view returns (DenomOracleExchangeRatePair[] memory); - -struct OracleTwap { - string denom; - string twap; - int64 lookbackSeconds; -} - -/// Retrieves Oracle Time-Weighted Average Prices (TWAPs) for the specified lookback period. -/// @param lookback_seconds The lookback period in seconds (min: 60, max: 86400). -/// @return An array of denomination and TWAP pairs. -function getOracleTwaps( - uint64 lookback_seconds -) external view returns (OracleTwap[] memory); -``` - -**Price Format:** Exchange rates are returned as decimal strings (e.g., "1.234567" represents $1.234567). Prices are denominated in USD unless otherwise specified. - -**TWAP Limitations:** Lookback periods are limited to 1 minute (60 seconds) minimum and 24 hours (86400 seconds) maximum. Requesting data outside this range may result in empty results. - -## Supported Denominations - -Common denominations available in the oracle include: - -- `usei` - Micro SEI (1 SEI = 1,000,000 usei) -- `uusdc` - Micro USDC -- `uatom` - Micro ATOM -- `uosmo` - Micro OSMO -- `ueth` - Micro Ethereum -- `ubtc` - Micro Bitcoin -- `uusdt` - Micro USDT - -The exact list of supported denominations may vary by network. Use `getExchangeRates()` to see all currently available price feeds. - -## Using the Contract - -### Setup - -#### Prerequisites - -Before getting started, ensure you have: - -- **Node.js** (v18 or higher recommended) -- **npm** or **yarn** package manager -- **MetaMask** or compatible EVM wallet -- **SEI tokens** for gas fees (typically 0.001-0.01 SEI per oracle query) - -#### Install Dependencies - -Install the required packages for interacting with Sei precompiles: - -```bash copy -# Install ethers.js for smart contract interactions -npm install ethers - -# Install Sei EVM bindings for precompile addresses and ABIs -npm install @sei-js/precompiles@2.1.2 - -# Install dotenv for managing private keys (optional but recommended) -npm install dotenv -``` - -#### Import Precompile Components - -```typescript copy -// Import Oracle precompile address and ABI -// View the entire ABI here: https://github.com/sei-protocol/sei-chain/tree/main/precompiles/oracle -import { ORACLE_PRECOMPILE_ABI, ORACLE_PRECOMPILE_ADDRESS } from '@sei-js/precompiles'; -import { ethers } from 'ethers'; -``` - -**Precompile Address:** The Oracle precompile is deployed at `0x0000000000000000000000000000000000001008` - -#### Get SEI Tokens - -Purchase SEI tokens from supported exchanges. You will need a small amount of SEI in your wallet to pay for gas fees when making calls to the Oracle precompile. - -### Contract Initialization - -Set up your provider, signer, and contract instance: - -```typescript copy -// Using MetaMask as the signer and provider (browser environment) -const provider = new ethers.BrowserProvider(window.ethereum); -await provider.send('eth_requestAccounts', []); -const signer = await provider.getSigner(); - -// Or for Node.js environment -const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); -const signer = new ethers.Wallet('YOUR_PRIVATE_KEY', provider); - -// Create a contract instance for the Oracle precompile -const oracle = new ethers.Contract(ORACLE_PRECOMPILE_ADDRESS, ORACLE_PRECOMPILE_ABI, signer); -``` - -## Helper Functions - -Use these helper functions for price data handling: - -```typescript copy -// Helper functions for Oracle data conversion -class OracleDataHelper { - // Convert exchange rate string to number - static rateToNumber(rateString: string): number { - try { - const rate = parseFloat(rateString); - if (isNaN(rate) || !isFinite(rate)) { - throw new Error(`Invalid rate: ${rateString}`); - } - return rate; - } catch (error) { - console.error('Error parsing rate:', rateString, error); - return 0; - } - } - - // Format timestamp to readable date - static formatTimestamp(timestamp: bigint | number): string { - try { - return new Date(Number(timestamp) * 1000).toISOString(); - } catch (error) { - console.error('Error formatting timestamp:', timestamp, error); - return 'Invalid timestamp'; - } - } - - // Calculate price from exchange rate (assuming USD base) - static calculatePrice(exchangeRate: string, amount: number = 1): number { - return this.rateToNumber(exchangeRate) * amount; - } - - // Find specific denom in exchange rates - static findDenom(rates: any[], denom: string): any { - return rates.find((rate) => rate.denom === denom); - } - - // Check if price data is stale (older than 5 minutes) - static isPriceStale(timestamp: bigint | number): boolean { - const now = Math.floor(Date.now() / 1000); - const priceTime = Number(timestamp); - return now - priceTime > 300; // 5 minutes - } -} -``` - -## Step-by-Step Guide: Using the Oracle Precompile - -### Get Exchange Rates - -```typescript copy -// Get all exchange rates -const exchangeRates = await oracle.getExchangeRates(); - -// Display rates -exchangeRates.forEach((rate) => { - const price = OracleDataHelper.rateToNumber(rate.oracleExchangeRateVal.exchangeRate); - const timestamp = rate.oracleExchangeRateVal.lastUpdateTimestamp; - const isStale = OracleDataHelper.isPriceStale(timestamp); - - console.log(`${rate.denom}: $${price.toFixed(6)}${isStale ? ' (STALE)' : ''}`); - console.log(`Last Update: ${OracleDataHelper.formatTimestamp(timestamp)}`); -}); -``` - -### Get TWAP Data - -```typescript copy -// Get 1-hour TWAP (3600 seconds) -const lookbackSeconds = 3600; -const twaps = await oracle.getOracleTwaps(lookbackSeconds); - -// Display TWAPs -twaps.forEach((twap) => { - console.log(`${twap.denom} TWAP: ${twap.twap}`); - console.log(`Lookback: ${twap.lookbackSeconds} seconds`); -}); -``` - -### Query Specific Token Price - -```typescript copy -// Get exchange rates and find specific token -const rates = await oracle.getExchangeRates(); -const seiRate = rates.find((r) => r.denom === 'usei'); - -if (seiRate) { - const price = parseFloat(seiRate.oracleExchangeRateVal.exchangeRate); - console.log(`SEI Price: $${price}`); -} -``` - -### Monitor Price Changes - -```typescript copy -// Monitor price changes every 30 seconds (recommended minimum for mainnet) -async function monitorPrices() { - const previousPrices = new Map(); - - const monitor = async () => { - try { - const rates = await oracle.getExchangeRates(); - - rates.forEach((rate) => { - const currentPrice = OracleDataHelper.rateToNumber(rate.oracleExchangeRateVal.exchangeRate); - const previousPrice = previousPrices.get(rate.denom); - - if (previousPrice) { - const change = ((currentPrice - previousPrice) / previousPrice) * 100; - console.log(`${rate.denom}: $${currentPrice.toFixed(6)} (${change >= 0 ? '+' : ''}${change.toFixed(2)}%)`); - } - - previousPrices.set(rate.denom, currentPrice); - }); - } catch (error) { - console.error('Monitor error:', error); - } - }; - - // Initial fetch - await monitor(); - - // Monitor every 30 seconds - setInterval(monitor, 30000); -} -``` - -## Complete Integration Example - -Create a file named `oracle-demo.js`: - -```javascript copy file="oracle-demo.js" -const { ethers } = require('ethers'); -const { ORACLE_PRECOMPILE_ABI, ORACLE_PRECOMPILE_ADDRESS } = require('@sei-js/precompiles'); -require('dotenv').config(); - -// Helper functions -function safeParseFloat(str) { - try { - const num = parseFloat(str); - return isNaN(num) ? 0 : num; - } catch { - return 0; - } -} - -function formatTimestamp(timestamp) { - try { - return new Date(Number(timestamp) * 1000).toISOString(); - } catch { - return 'Invalid timestamp'; - } -} - -function isPriceStale(timestamp, maxAgeSeconds = 300) { - const now = Math.floor(Date.now() / 1000); - return now - Number(timestamp) > maxAgeSeconds; -} - -async function main() { - try { - // Connect to Sei Mainnet - console.log('🔗 Connecting to Sei Mainnet...'); - const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); - - // Verify connection to mainnet - const network = await provider.getNetwork(); - if (Number(network.chainId) !== 1329) { - throw new Error(`Expected Sei Mainnet (1329), but connected to chain ${network.chainId}`); - } - console.log(`✅ Connected to Sei Mainnet (Chain ID: 1329)`); - - // Setup wallet - const privateKey = process.env.PRIVATE_KEY; - if (!privateKey) { - throw new Error('Please set PRIVATE_KEY in .env file'); - } - - const wallet = new ethers.Wallet(privateKey, provider); - console.log('👛 Wallet address:', wallet.address); - - // Check balance - const balance = await provider.getBalance(wallet.address); - const balanceInSei = ethers.formatEther(balance); - console.log('💰 Wallet balance:', balanceInSei, 'SEI'); - - if (parseFloat(balanceInSei) < 0.01) { - console.warn('⚠️ Low balance detected. You may need more SEI for gas fees.'); - console.log('💡 Purchase SEI from supported exchanges like Binance, Coinbase, or KuCoin'); - } - - // Initialize Oracle precompile contract - const oracle = new ethers.Contract(ORACLE_PRECOMPILE_ADDRESS, ORACLE_PRECOMPILE_ABI, wallet); - - console.log('\n' + '='.repeat(50)); - console.log('🔮 Oracle Precompile Demo - Sei Mainnet'); - console.log('='.repeat(50)); - - // 1. Get all exchange rates - console.log('\n1️⃣ Fetching current exchange rates...'); - - const exchangeRates = await oracle.getExchangeRates(); - - if (!exchangeRates || exchangeRates.length === 0) { - console.log('📭 No exchange rates available on mainnet'); - return; - } - - console.log(`📊 Found ${exchangeRates.length} price feeds on Sei Mainnet:`); - - // Display all available rates - exchangeRates.forEach((rate, index) => { - const price = safeParseFloat(rate.oracleExchangeRateVal.exchangeRate); - const timestamp = rate.oracleExchangeRateVal.lastUpdateTimestamp; - const lastUpdate = formatTimestamp(timestamp); - const isStale = isPriceStale(timestamp); - - console.log(`\n ${index + 1}. ${rate.denom}:`); - console.log(` 💲 Price: $${price.toFixed(6)}${isStale ? ' ⚠️ (STALE)' : ''}`); - console.log(` ⏰ Last Update: ${lastUpdate}`); - }); - - // 2. Get TWAP data - console.log('\n2️⃣ Fetching 1-hour TWAP data...'); - const lookbackSeconds = 3600; // 1 hour - - try { - const twaps = await oracle.getOracleTwaps(lookbackSeconds); - - if (twaps && twaps.length > 0) { - console.log(`📈 Found ${twaps.length} TWAP values:`); - - twaps.forEach((twap, index) => { - const twapPrice = safeParseFloat(twap.twap); - console.log(`\n ${index + 1}. ${twap.denom}:`); - console.log(` 📊 1hr TWAP: $${twapPrice.toFixed(6)}`); - console.log(` ⏱️ Lookback: ${twap.lookbackSeconds} seconds`); - }); - } else { - console.log('📭 No TWAP data available for the requested period'); - } - } catch (twapError) { - console.error('❌ Failed to fetch TWAP data:', twapError.message); - } - - // 3. Focus on SEI price analysis - console.log('\n3️⃣ SEI Price Analysis...'); - - const seiRate = exchangeRates.find((r) => r.denom === 'usei'); - if (seiRate) { - const seiPrice = safeParseFloat(seiRate.oracleExchangeRateVal.exchangeRate); - const timestamp = seiRate.oracleExchangeRateVal.lastUpdateTimestamp; - - console.log(`\n 💎 SEI (usei) Details:`); - console.log(` 💲 Current Price: $${seiPrice.toFixed(6)}`); - console.log(` ⏰ Last Update: ${formatTimestamp(timestamp)}`); - console.log(` 🔄 Price Age: ${Math.floor(Date.now() / 1000) - Number(timestamp)} seconds`); - - // Market cap calculation (approximate) - const circulatingSupply = 3_90000000; // Approximate SEI supply - const marketCap = seiPrice * circulatingSupply; - console.log(` 📊 Est. Market Cap: $${(marketCap / 1000000).toFixed(2)}M`); - } else { - console.log(' ❌ SEI price not found in oracle data'); - } - - // 4. DeFi use case - Advanced collateral calculation - console.log('\n4️⃣ Advanced DeFi Example - Multi-asset portfolio...'); - - const portfolio = [ - { asset: 'usei', amount: 1000 }, - { asset: 'uusdc', amount: 500 }, - { asset: 'uatom', amount: 50 } - ]; - - let totalPortfolioValue = 0; - const portfolioBreakdown = []; - - console.log(`\n 💼 Portfolio Analysis:`); - - portfolio.forEach(({ asset, amount }) => { - const assetRate = exchangeRates.find((r) => r.denom === asset); - if (assetRate) { - const price = safeParseFloat(assetRate.oracleExchangeRateVal.exchangeRate); - const value = price * amount; - totalPortfolioValue += value; - - portfolioBreakdown.push({ - asset, - amount, - price, - value, - percentage: 0 // Will calculate after total - }); - - console.log(` 🪙 ${asset}: ${amount} tokens × $${price.toFixed(6)} = $${value.toFixed(2)}`); - } - }); - - // Calculate percentages - portfolioBreakdown.forEach((item) => { - item.percentage = (item.value / totalPortfolioValue) * 100; - }); - - console.log(`\n 📊 Portfolio Summary:`); - console.log(` 💰 Total Value: $${totalPortfolioValue.toFixed(2)}`); - - portfolioBreakdown.forEach(({ asset, value, percentage }) => { - console.log(` 📈 ${asset}: $${value.toFixed(2)} (${percentage.toFixed(1)}%)`); - }); - - // Lending calculations - const maxLTV = 0.75; // 75% max loan-to-value - const maxBorrow = totalPortfolioValue * maxLTV; - console.log(` 🏦 Max Borrowing Power (75% LTV): $${maxBorrow.toFixed(2)}`); - - // 5. Gas cost analysis - console.log('\n5️⃣ Gas cost analysis on mainnet...'); - - try { - const feeData = await provider.getFeeData(); - const exchangeRateGas = await oracle.getExchangeRates.estimateGas(); - const twapGas = await oracle.getOracleTwaps.estimateGas(3600); - - const exchangeRateCost = exchangeRateGas * (feeData.gasPrice || 0n); - const twapCost = twapGas * (feeData.gasPrice || 0n); - - console.log(`\n ⛽ Mainnet Gas Estimates:`); - console.log(` 📊 getExchangeRates(): ${exchangeRateGas.toString()} gas`); - console.log(` 📈 getOracleTwaps(): ${twapGas.toString()} gas`); - console.log(` 💰 Exchange Rate Cost: ~${ethers.formatEther(exchangeRateCost)} SEI`); - console.log(` 💰 TWAP Cost: ~${ethers.formatEther(twapCost)} SEI`); - - // Current SEI price for USD conversion - if (seiRate) { - const seiPrice = safeParseFloat(seiRate.oracleExchangeRateVal.exchangeRate); - const exchangeRateUSD = parseFloat(ethers.formatEther(exchangeRateCost)) * seiPrice; - const twapUSD = parseFloat(ethers.formatEther(twapCost)) * seiPrice; - - console.log(` 💵 Exchange Rate Cost: ~$${exchangeRateUSD.toFixed(4)} USD`); - console.log(` 💵 TWAP Cost: ~$${twapUSD.toFixed(4)} USD`); - } - } catch (gasError) { - console.log(' ⚠️ Could not estimate gas costs:', gasError.message); - } - } catch (error) { - console.error('\n❌ Demo failed:', error.message); - - // Enhanced error diagnostics for mainnet - if (error.message.includes('1329')) { - console.log("💡 Network issue: Make sure you're connecting to Sei Mainnet"); - console.log(' - RPC URL: https://evm-rpc.sei-apis.com'); - console.log(' - Chain ID: 1329'); - } else if (error.code === 'INSUFFICIENT_FUNDS') { - console.log('💡 Buy SEI tokens from:'); - console.log(' - Binance: https://www.binance.com/en/trade/SEI_USDT'); - console.log(' - Coinbase: https://www.coinbase.com/price/sei'); - console.log(' - KuCoin: https://www.kucoin.com/trade/SEI-USDT'); - } else if (error.code === 'NETWORK_ERROR') { - console.log('💡 Network troubleshooting:'); - console.log(' - Check internet connection'); - console.log(' - Verify RPC endpoint is accessible'); - console.log(' - Try alternative RPC if available'); - } - - process.exit(1); - } -} - -// Graceful shutdown -process.on('SIGINT', () => { - console.log('\n🛑 Demo interrupted'); - process.exit(0); -}); - -// Run the demo -if (require.main === module) { - main().catch(console.error); -} - -module.exports = { main }; -``` - -### Running the Example - -1. **Create a new directory and initialize npm:** - -```bash copy -mkdir sei-oracle-mainnet -cd sei-oracle-mainnet -npm init -y -``` - -2. **Install dependencies:** - -```bash copy -npm install ethers @sei-js/precompiles@2.1.2 dotenv -``` - -3. **Create a `.env` file:** - -```bash copy -PRIVATE_KEY=your_private_key_here_without_0x_prefix -``` - -4. **Create the demo file:** - Copy the complete integration example above into `oracle-demo.js` - -5. **Run the script:** - -```bash copy -node oracle-demo.js -``` - -### Expected Output on Mainnet - -``` -🔗 Connecting to Sei Mainnet... -✅ Connected to Sei Mainnet (Chain ID: 1329) -👛 Wallet address: 0x742d35Cc6634C0532925a3b844Bc6789e065f3B -💰 Wallet balance: 5.2 SEI - -================================================== -🔮 Oracle Precompile Demo - Sei Mainnet -================================================== - -1️⃣ Fetching current exchange rates... -📊 Found 12 price feeds on Sei Mainnet: - - 1. usei: - 💲 Price: $0.421500 - ⏰ Last Update: 2024-01-15T15:45:32.000Z - - 2. uusdc: - 💲 Price: $1.000100 - ⏰ Last Update: 2024-01-15T15:45:32.000Z - - 3. uatom: - 💲 Price: $9.875000 - ⏰ Last Update: 2024-01-15T15:45:32.000Z - -2️⃣ Fetching 1-hour TWAP data... -📈 Found 12 TWAP values: - - 1. usei: - 📊 1hr TWAP: $0.419200 - ⏱️ Lookback: 3600 seconds - -3️⃣ SEI Price Analysis... - - 💎 SEI (usei) Details: - 💲 Current Price: $0.421500 - ⏰ Last Update: 2024-01-15T15:45:32.000Z - 🔄 Price Age: 45 seconds - 📊 Est. Market Cap: $164.4M - -4️⃣ Advanced DeFi Example - Multi-asset portfolio... - - 💼 Portfolio Analysis: - 🪙 usei: 1000 tokens × $0.421500 = $421.50 - 🪙 uusdc: 500 tokens × $1.000100 = $500.05 - 🪙 uatom: 50 tokens × $9.875000 = $493.75 - - 📊 Portfolio Summary: - 💰 Total Value: $1415.30 - 📈 usei: $421.50 (29.8%) - 📈 uusdc: $500.05 (35.3%) - 📈 uatom: $493.75 (34.9%) - 🏦 Max Borrowing Power (75% LTV): $1061.48 - -5️⃣ Gas cost analysis on mainnet... - - ⛽ Mainnet Gas Estimates: - 📊 getExchangeRates(): 52341 gas - 📈 getOracleTwaps(): 58967 gas - 💰 Exchange Rate Cost: ~0.002617 SEI - 💰 TWAP Cost: ~0.002948 SEI - 💵 Exchange Rate Cost: ~$0.0011 USD - 💵 TWAP Cost: ~$0.0012 USD - -================================================== -✅ Oracle demo completed successfully on Sei Mainnet! -================================================== -``` - -## Production Best Practices - - -**Mainnet Production Considerations:** - -- **Real Money at Risk:** Always test thoroughly on testnet first -- **Gas Optimization:** Oracle calls cost real SEI - optimize frequency -- **Price Validation:** Implement multiple layers of price validation: Always validate price freshness before usage -- **Fallback Mechanisms:** Have backup price sources for critical applications -- **Monitoring:** Set up alerts for oracle failures or stale data -- **Rate Limiting:** Respect RPC rate limits to avoid being blocked - - - -## Error Code Reference - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Error - - Cause - - Solution -
- no oracle data - - Oracle module not active - - Check network configuration -
- denom not found - - Asset not in oracle - - Verify supported denoms -
- stale price data - - Old timestamp - - Check lastUpdateTimestamp -
- invalid lookback - - Lookback too long - - Use shorter TWAP period -
-
diff --git a/content/evm/precompiles/staking.mdx b/content/evm/precompiles/staking.mdx deleted file mode 100644 index 6726334d..00000000 --- a/content/evm/precompiles/staking.mdx +++ /dev/null @@ -1,871 +0,0 @@ ---- -title: 'Staking Precompile' -description: "Learn how to interact with Sei's Staking precompile through ethers.js, enabling delegation, undelegation, redelegation, and validator management directly in your EVM smart contracts for DeFi and staking applications." -keywords: ['staking precompile', 'ethers.js', 'delegation', 'validators', 'staking rewards', 'defi development', 'sei evm', 'blockchain staking'] ---- - -import { Callout } from 'nextra/components'; -import { Tabs } from 'nextra/components'; - -# Staking Precompile - -**Address:** `0x0000000000000000000000000000000000001005` - -The Sei staking precompile allows EVM applications to interact directly with Sei's native staking module through standard smart contract calls. This enables delegation, undelegation, redelegation, validator management, and staking queries directly from your dApps without needing separate Cosmos SDK integration. - - **What is a precompile?** A precompile is a special smart contract deployed at a fixed address by the Sei protocol itself, that exposes custom native chain logic to EVM-based applications. It acts like a regular contract from the EVM's perspective, but executes privileged, low-level logic efficiently. - -## How Does the Staking Precompile Work? - -The staking precompile at address `0x0000000000000000000000000000000000001005` exposes functions like `delegate()`, `undelegate()`, `redelegate()`, and `delegation()`. - -- **Direct Integration:** EVM contracts and dApps can call staking functions like any other smart contract method. -- **Native Execution:** Operations are executed at the Cosmos SDK level for maximum efficiency and security. -- **Seamless Bridge:** No need for separate wallet integrations or complex cross-chain interactions. - -## Use Cases - -- **DeFi Integration:** Build liquid staking protocols and yield farming strategies. -- **Delegation Services:** Create user-friendly interfaces for staking operations. -- **Portfolio Management:** Automate staking strategies and delegation rebalancing. -- **Validator Management:** Create and manage validators programmatically. - -## What You'll Learn in This Guide - -By the end of this guide, you'll be able to: - -- **Integrate Staking Operations** - Call delegation, undelegation, and redelegation functions directly from your EVM contracts and dApps -- **Handle Decimal Precision** - Master the critical precision patterns for different operations to avoid common formatting errors -- **Manage Validator Operations** - Query validators programmatically using the precompile interface -- **Build Portfolio Tools** - Implement automated rebalancing and delegation tracking for staking management applications -- **Navigate Staking Risks** - Understand unbonding periods, slashing mechanics, and validator selection for safe staking operations - -## Functions - -The staking precompile exposes the following functions: - -### Transaction Functions - -```solidity -/// Delegates Sei to the specified validator. -/// @dev This function truncates msg.value to 6 decimal places for interaction with the staking module -/// @param valAddress The Sei address of the validator. -/// @return Whether the delegation was successful. -function delegate( - string memory valAddress -) payable external returns (bool success); - -/// Redelegates Sei from one validator to another. -/// @dev The amount should be in 6 decimal precision, not 18. 1 SEI = 1_000_000 uSEI -/// @param srcAddress The Sei address of the validator to move delegations from. -/// @param dstAddress The Sei address of the validator to move delegations to. -/// @param amount The amount of Sei to move from srcAddress to dstAddress. -/// @return Whether the redelegation was successful. -function redelegate( - string memory srcAddress, - string memory dstAddress, - uint256 amount -) external returns (bool success); - -/// Undelegates Sei from the specified validator. -/// @dev The amount should be in 6 decimal precision, not 18. 1 SEI = 1_000_000 uSEI -/// @param valAddress The Sei address of the validator to undelegate from. -/// @param amount The amount of Sei to undelegate. -/// @return Whether the undelegation was successful. -function undelegate( - string memory valAddress, - uint256 amount -) external returns (bool success); -``` - -### Query Functions - -```solidity -struct Delegation { - Balance balance; - DelegationDetails delegation; -} - -struct Balance { - uint256 amount; - string denom; -} - -struct DelegationDetails { - string delegator_address; - uint256 shares; - uint256 decimals; - string validator_address; -} - -/// Queries delegation for a given delegator and validator address. -/// @param delegator The address of the delegator. -/// @param valAddress The Sei address of the validator. -/// @return The delegation information. -/// To calculate the actual amount, divide the shares by decimals. -function delegation( - address delegator, - string memory valAddress -) external view returns (Delegation delegation); -``` - -## Using the Precompile - -### Setup - -#### Prerequisites - -Before getting started, ensure you have: - -- **Node.js** (v16 or higher) -- **npm** or **yarn** package manager -- **EVM-compatible wallet** -- **SEI tokens** for gas fees and staking operations - -#### Install Dependencies - -Install the required packages for interacting with Sei precompiles: - -```bash copy -# Install ethers.js for smart contract interactions -npm install ethers - -# Install Sei EVM bindings for precompile addresses and ABIs -npm install @sei-js/precompiles@2.1.2 -``` - -#### Import Precompile Components - -```typescript copy -// Import Staking precompile address and ABI -// View the entire ABI here: https://github.com/sei-protocol/sei-chain/tree/evm/precompiles/staking -import { STAKING_PRECOMPILE_ABI, STAKING_PRECOMPILE_ADDRESS } from '@sei-js/precompiles'; -import { ethers } from 'ethers'; -``` - - **Precompile Address:** The staking precompile is deployed at `0x0000000000000000000000000000000000001005` - -### Contract Initialization - -Set up your provider, signer, and contract instance: - -```typescript copy -// Using EVM-compatible wallet as the signer and provider -const provider = new ethers.BrowserProvider(window.ethereum); -await provider.send('eth_requestAccounts', []); -const signer = await provider.getSigner(); - -// Create a contract instance for the staking precompile -const staking = new ethers.Contract(STAKING_PRECOMPILE_ADDRESS, STAKING_PRECOMPILE_ABI, signer); -``` - -## Critical: Understanding Decimal Precision - -One of the most important concepts to understand when working with the Sei staking precompile: - -### Mixed Decimal Precision System - -The staking precompile operates with **different decimal precision for different operations** due to bridging EVM and Cosmos standards: - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FunctionInput/OutputDecimal PrecisionExample
- delegate() - msg.value (input)18 decimals (wei) - ethers.parseUnits('1', 18) -
- undelegate() - amount (input)6 decimals (uSEI) - 1000000 (represents 1 SEI) -
- redelegate() - amount (input)6 decimals (uSEI) - 500000 (represents 0.5 SEI) -
- delegation() - return values (output)6 decimals (uSEI) - 1000000 (represents 1 SEI) -
-
- -### How This Works in Practice - -**delegate() Function (Uses 18 decimals):** - -- `delegate()` - accepts `msg.value` in wei (18 decimals) - -**undelegate() and redelegate() Functions (Use 6 decimals):** - -- `undelegate()` - expects amount parameter in 6 decimals (uSEI) -- `redelegate()` - expects amount parameter in 6 decimals (uSEI) - -**Reading/Query Operations (Return 6 decimals):** - -- `delegation()` - returns amounts in 6 decimal precision - -### Why This Mixed System Exists - -1. **delegate() EVM Compatibility:** Uses standard 18-decimal wei format for EVM `msg.value` consistency -2. **Other Operations Cosmos Integration:** `undelegate()` and `redelegate()` use 6-decimal uSEI precision to match native Cosmos operations -3. **Query Consistency:** All query results use 6-decimal precision for consistent reading - -### Best Practice: Different Conversion for Different Functions - -When working with user inputs, use the appropriate conversion for each function: - -```typescript copy -// Best practice for handling user input amounts -function prepareAmountForDelegate(seiAmount: number): bigint { - // For delegate() - convert to 18 decimals (wei) - const fixedAmount = seiAmount.toFixed(6); - return ethers.parseUnits(fixedAmount, 18); -} - -function prepareAmountForUndelegateRedelegate(seiAmount: number): bigint { - // For undelegate() and redelegate() - convert to 6 decimals (uSEI) - return ethers.parseUnits(seiAmount.toFixed(6), 6); -} - -// Usage examples -const userInputAmount = 1.23456789; // User enters this - -// For delegate() -const delegateAmount = prepareAmountForDelegate(userInputAmount); // 18 decimals - -// For undelegate() and redelegate() -const undelegateAmount = prepareAmountForUndelegateRedelegate(userInputAmount); // 6 decimals -``` - -## Decimal Conversion Helpers - -Use these helper functions to avoid precision errors: - -```typescript copy -// Helper functions for amount conversion -class SeiAmountConverter { - // Convert SEI to wei (18 decimals) - ONLY for delegate() - static seiToWei(seiAmount: number): bigint { - return ethers.parseUnits(seiAmount.toFixed(6), 18); - } - - // Convert SEI to uSEI (6 decimals) - for undelegate() and redelegate() - static seiToUsei(seiAmount: number): bigint { - return ethers.parseUnits(seiAmount.toFixed(6), 6); - } - - // Convert reading results (6 decimals) to SEI - static useiToSei(useiAmount: number | bigint): number { - return Number(useiAmount) / 1000000; - } - - // Convert wei to SEI (18 decimals) - for delegate operations - static weiToSei(weiAmount: bigint): number { - return Number(ethers.formatEther(weiAmount)); - } -} - -// Usage examples - Use correct decimals for each function -const delegateAmount = SeiAmountConverter.seiToWei(1); // For delegate() - 18 decimals -const undelegateAmount = SeiAmountConverter.seiToUsei(0.5); // For undelegate() - 6 decimals -const redelegateAmount = SeiAmountConverter.seiToUsei(2); // For redelegate() - 6 decimals -``` - -## Step-by-Step Guide: Using the Staking Precompile - -### Delegate Tokens - - - - -```typescript copy -// Sei validator address you want to delegate to (seivaloper... format) -const validatorAddress = 'seivaloper1xyz...'; - -const amount = 1; // Amount in SEI to delegate - -const amountTrimmed = amount.toFixed(6); // Ensure 6 decimal precision for msg.value - -// Delegate 1 SEI - Uses 18 decimals for msg.value -const amountToDelegate = ethers.parseUnits(amountTrimmed, 18); - -const tx = await staking.delegate(validatorAddress, { value: amountToDelegate }); - -const receipt = await tx.wait(); -console.log('Delegation completed:', receipt); -``` - - - -```typescript copy -function delegateToValidator(string memory validatorAddress) external payable { - require(msg.value > 0, "Amount must be greater than 0"); - - bool success = ISTAKING(0x0000000000000000000000000000000000001005).delegate{value: msg.value}(validatorAddress); - require(success, "Delegation failed"); - - emit Delegated(msg.sender, validatorAddress, msg.value); - } -``` - - - - -### Undelegate Tokens - - - - -```typescript copy -// Undelegate 1 SEI from a validator - Uses 6 decimals for amount parameter -const validatorAddress = 'seivaloper1xyz...'; - -const amount = 1; // Amount in SEI to undelegate -const amountTrimmed = amount.toFixed(6); // Ensure 6 decimal precision -const amountToUndelegate = ethers.parseUnits(amountTrimmed, 6); // 1 SEI in 6 decimals - -const tx = await staking.undelegate(validatorAddress, amountToUndelegate); -const receipt = await tx.wait(); - -console.log('Undelegation started:', receipt); -``` - - - -```typescript copy - function undelegateFromValidator( - string memory validatorAddress, - uint256 amount - ) external { - require(amount > 0, "Amount must be greater than 0"); - - bool success = ISTAKING(0x0000000000000000000000000000000000001005).undelegate(validatorAddress, amount); - require(success, "Undelegation failed"); - - emit Undelegated(msg.sender, validatorAddress, amount); - } - -``` - - - - - **IMPORTANT:** Undelegated tokens are subject to a **21-day unbonding period** during which they cannot be transferred and do not earn rewards. - -### Redelegate Tokens - - - - -```typescript copy -// Redelegate 0.5 SEI from one validator to another - Uses 6 decimals for amount parameter -const srcValidator = 'seivaloper1abc...'; -const dstValidator = 'seivaloper1xyz...'; - -const amount = 0.5; // Amount in SEI to redelegate -const amountTrimmed = amount.toFixed(6); // Ensure 6 decimal precision -const amountToRedelegate = ethers.parseUnits(amountTrimmed, 6); // 0.5 SEI in 6 decimals - -const tx = await staking.redelegate(srcValidator, dstValidator, amountToRedelegate); -const receipt = await tx.wait(); - -console.log('Redelegation completed:', receipt); -``` - - - -```typescript copy - function redelegateBetweenValidators( - string memory fromValidator, - string memory toValidator, - uint256 amount - ) external { - require(amount > 0, "Amount must be greater than 0"); - - bool success = ISTAKING(0x0000000000000000000000000000000000001005).redelegate(fromValidator, toValidator, amount); - require(success, "Redelegation failed"); - - emit Redelegated(msg.sender, fromValidator, toValidator, amount); - } -``` - - - - - -**Redelegation restrictions:** - -- Maximum 7 redelegations per validator pair per 21-day period - -- After redelegating from Validator A to Validator B, you cannot redelegate from Validator B to another validator for 21 days - -- Each redelegation has its own 21-day cooldown period - - - -### Query a Delegation - - - - -```typescript copy -// Get your EVM address as the delegator -const delegator = await signer.getAddress(); - -try { - const delegationInfo = await staking.delegation(delegator, validatorAddress); - - console.log('Delegation details:', { - amount: delegationInfo.balance.amount.toString(), - denom: delegationInfo.balance.denom, - shares: delegationInfo.delegation.shares.toString(), - decimals: delegationInfo.delegation.decimals.toString(), - delegator_address: delegationInfo.delegation.delegator_address, - validator_address: delegationInfo.delegation.validator_address - }); - - // Convert delegation amount from uSEI to SEI for readable display - const delegationAmountSei = ethers.formatUnits(delegationInfo.balance.amount, 6); - console.log('Delegation amount (SEI):', delegationAmountSei); -} catch (error) { - if (error.message.includes('delegation not found') || error.message.includes('no delegation')) { - console.log('No delegation found for this validator'); - } else { - console.error('Error querying delegation:', error); - } -} -``` - - - -```typescript copy - function getDelegationInfo( - address delegator, - string memory validatorAddress - ) external view returns (IStakingPrecompile.Delegation memory) { - return ISTAKING(0x0000000000000000000000000000000000001005).delegation(delegator, validatorAddress); - } -``` - - - - -## Advanced Usage Examples - -### Portfolio Rebalancing - -```typescript copy -async function rebalanceStaking( - fromValidator: string, - toValidator: string, - amount: number // Amount in SEI -) { - try { - // Convert to 6 decimal precision for redelegate (uses 6 decimals) - const amountIn6Decimals = ethers.parseUnits(amount.toFixed(6), 6); - - const tx = await staking.redelegate(fromValidator, toValidator, amountIn6Decimals); - - const receipt = await tx.wait(); - console.log('Rebalancing completed:', receipt); - - return receipt; - } catch (error) { - console.error('Rebalancing failed:', error); - throw error; - } -} -``` - -### Complete Integration Example - - - - -```typescript copy -async function stakingExample() { - // Setup - const provider = new ethers.BrowserProvider(window.ethereum); - await provider.send('eth_requestAccounts', []); - const signer = await provider.getSigner(); - const staking = new ethers.Contract(STAKING_PRECOMPILE_ADDRESS, STAKING_PRECOMPILE_ABI, signer); - - const delegator = await signer.getAddress(); - const validatorAddress = 'seivaloper1xyz...'; - - try { - // 1. Check current delegation (with error handling) - console.log('=== Checking Current Delegation ==='); - try { - const currentDelegation = await staking.delegation(delegator, validatorAddress); - const currentAmountSei = ethers.formatUnits(currentDelegation.balance.amount, 6); - console.log('Current delegation amount (SEI):', currentAmountSei); - } catch (error) { - if (error.message.includes('delegation not found') || error.message.includes('no delegation')) { - console.log('No existing delegation found for this validator'); - } else { - throw error; // Re-throw if it's a different error - } - } - - // 2. Delegate additional tokens (uses 18 decimals) - console.log('=== Delegating Tokens ==='); - const amount = 1; - const amountTrimmed = amount.toFixed(6); // Ensure 6 decimal precision - const amountToDelegate = ethers.parseUnits(amountTrimmed, 18); - const delegateTx = await staking.delegate(validatorAddress, { - value: amountToDelegate, - gasLimit: 300000 - }); - await delegateTx.wait(); - console.log('Delegation successful:', delegateTx.hash); - - // 3. Check updated delegation - const updatedDelegation = await staking.delegation(delegator, validatorAddress); - const updatedAmountSei = ethers.formatUnits(updatedDelegation.balance.amount, 6); - console.log('Updated delegation amount (SEI):', updatedAmountSei); - - // 4. Undelegate partial amount (uses 6 decimals) - console.log('=== Undelegating Tokens ==='); - const undelegateAmount = ethers.parseUnits('0.5', 6); // 0.5 SEI in 6 decimals - const undelegateTx = await staking.undelegate(validatorAddress, undelegateAmount); - await undelegateTx.wait(); - console.log('Undelegation successful:', undelegateTx.hash); - } catch (error) { - console.error('Operation failed:', error); - } -} -``` - - - - -**Contract Example** - -```solidity copy -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface IStakingPrecompile { - - struct Delegation { - Balance balance; - DelegationDetails delegation; - } - - struct Balance { - uint256 amount; - string denom; - } - - struct DelegationDetails { - string delegator_address; - uint256 shares; - uint256 decimals; - string validator_address; - } - - function delegate(string memory valAddress) payable external returns (bool success); - - function undelegate( - string memory valAddress, - uint256 amount - ) external returns (bool success); - - function redelegate( - string memory srcAddress, - string memory dstAddress, - uint256 amount - ) external returns (bool success); - - function delegation( - address delegator, - string memory valAddress - ) external view returns (Delegation memory); -} - -contract StakingManager { - IStakingPrecompile constant STAKING = IStakingPrecompile(0x0000000000000000000000000000000000001005); - - event Delegated(address indexed user, string validator, uint256 amount); - event Undelegated(address indexed user, string validator, uint256 amount); - event Redelegated(address indexed user, string fromValidator, string toValidator, uint256 amount); - - // Delegate user's SEI to a validator - function delegateToValidator(string memory validatorAddress) external payable { - require(msg.value > 0, "Amount must be greater than 0"); - - bool success = STAKING.delegate{value: msg.value}(validatorAddress); - require(success, "Delegation failed"); - - emit Delegated(msg.sender, validatorAddress, msg.value); - } - - // Undelegate SEI from a validator (amount in 6 decimal precision - uSEI) - function undelegateFromValidator( - string memory validatorAddress, - uint256 amount - ) external { - require(amount > 0, "Amount must be greater than 0"); - - bool success = STAKING.undelegate(validatorAddress, amount); - require(success, "Undelegation failed"); - - emit Undelegated(msg.sender, validatorAddress, amount); - } - - // Redelegate SEI between validators (amount in 6 decimal precision - uSEI) - function redelegateBetweenValidators( - string memory fromValidator, - string memory toValidator, - uint256 amount - ) external { - require(amount > 0, "Amount must be greater than 0"); - - bool success = STAKING.redelegate(fromValidator, toValidator, amount); - require(success, "Redelegation failed"); - - emit Redelegated(msg.sender, fromValidator, toValidator, amount); - } - - // Get delegation info for a user and validator - function getDelegationInfo( - address delegator, - string memory validatorAddress - ) external view returns (IStakingPrecompile.Delegation memory) { - return STAKING.delegation(delegator, validatorAddress); - } - - // Helper function to convert SEI to uSEI (6 decimals) - function seiToUsei(uint256 seiAmount) public pure returns (uint256) { - return seiAmount * 1e6; - } - - // Helper function to convert uSEI to SEI - function useiToSei(uint256 useiAmount) public pure returns (uint256) { - return useiAmount / 1e6; - } -} -``` - - **Note:** This is a simplified example for demonstration purposes. The above example works such that when someone delegates through the above contract, the delegation will be recorded under the above contract address and not the user. So maintain correct mapping in the contract for tracking user delegations - -**Client Integration Example** - -```javascript copy -const abi = require('./artifacts/contracts/Staking.sol/StakingManager.json'); -const { ethers } = require('hardhat'); -const dotenv = require('dotenv'); -dotenv.config(); - -async function main() { - const [deployer] = await ethers.getSigners(); - console.log('Deploying contracts with the account:', deployer.address); - - const balance = await deployer.provider.getBalance(deployer.address); - console.log('Account balance:', ethers.formatEther(balance)); - - const StakingManager = await ethers.getContractFactory('StakingManager'); - console.log('Deploying StakingManager...'); - const stakingManager = await StakingManager.deploy(); - - await stakingManager.waitForDeployment(); - console.log('StakingManager deployed to:', await stakingManager.getAddress()); - - const validatorAddress = 'seival......123'; - - const amount = 1; - const trimmedAmount = amount.toFixed(6); - const formattedAmount = ethers.parseEther(trimmedAmount.toString()); - - const delegate = await stakingManager.delegateToValidator(validatorAddress, { value: formattedAmount }); - - await delegate.wait(); - console.log('Delegation transaction hash:', delegate.hash); - - // Use deployer address for querying delegation - const queryDelegation = await stakingManager.getDelegationInfo(deployer.address, validatorAddress); - console.log('Delegation Info:', queryDelegation); - - const undelegateAmount = ethers.parseUnits(trimmedAmount.toString(), 6); - const undelegate = await stakingManager.undelegateFromValidator(validatorAddress, undelegateAmount); - - await undelegate.wait(); - console.log('Undelegation transaction hash:', undelegate.hash); - - const redelegate = await stakingManager.redelegateBetweenValidators( - validatorAddress, - 'seival.....123', // Replace with the new validator address - undelegateAmount - ); - - await redelegate.wait(); - console.log('Redelegation transaction hash:', redelegate.hash); -} - -main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); -``` - - - - -## Security Considerations & Risks - -### Unbonding Period - -- **21-day lock:** Undelegated tokens cannot be transferred or earn rewards for 21 days -- **No exceptions:** This cannot be canceled once initiated -- **Planning:** Consider this when managing liquidity needs - -### Validator Selection Criteria - -- **Commission Rate:** Lower commission means more rewards for you (typically 1-10%) -- **Uptime:** Check for validators with high uptime (99%+) - -### Redelegation Complexity - -- **7-transaction limit:** You can only redelegate from the same validator to the same destination validator 7 times in 21 days -- **Serial blocking:** After redelegating A→B, you cannot redelegate B→C for 21 days -- **Each redelegation starts its own 21-day timer** - -## Troubleshooting - -### Common Issues and Solutions - -#### Gas-Related Issues - -```typescript copy -// Set appropriate gas limits for different operations -const delegateTx = await staking.delegate(validatorAddress, { - value: amount, - gasLimit: 300000 -}); -``` - -### Error Code Reference - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ErrorCauseSolution
- insufficient funds - Not enough SEI for operationCheck balance and reduce amount
- validator does not exist - Invalid validator addressVerify validator address format
- invalid delegation amount - Amount formatting issueUse correct decimal precision
- commission rate too high - Commission above max rateLower commission rate
- commission change too frequent - Changing commission within 24hWait 24 hours between changes
- self delegation too low - Below minimum self-delegationIncrease self-delegation amount
- too many redelegations - Exceeded 7 redelegation limitWait for earliest redelegation to expire
-
- -## Important Notes - - Remember the key rule: delegate() uses 18 decimals, undelegate()/redelegate() use 6 decimals, delegation() returns 6 decimals! - -### Decimal Precision - -- **delegate()** uses 18 decimals (wei) for msg.value -- **undelegate() and redelegate()** use 6 decimals (uSEI) for amount parameters -- **Query results** return amounts in 6 decimal precision -- **Best practice:** Use appropriate conversion functions for each operation - -### Validator Addresses - -- Use valid Sei validator addresses with `seivaloper1...` prefix -- These are Cosmos-format addresses, not EVM addresses - -### Staking Risks - -- **Unbonding:** 21-day waiting period for undelegated tokens -- **Redelegation limits:** Complex rules around frequency and serial operations - -### Commission and Rewards - -- Validators keep a commission percentage -- Rewards are distributed proportionally to delegation amounts -- Choose validators wisely based on commission, uptime, and governance participation From 52857b6b54d7586f3c572726a338e314edabb56b Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 19:52:02 -0400 Subject: [PATCH 04/25] Flatten tracing docs --- content/evm/indexing-best-practices.mdx | 98 +++++++ content/evm/pointers-deep-dive.mdx | 29 ++ content/evm/reference.mdx | 248 ++++++++---------- content/evm/rpc-websockets.mdx | 164 ++++++++++++ ...ers.mdx => tracing-javascript-tracers.mdx} | 0 .../index.mdx => tracing-overview.mdx} | 0 content/evm/tracing-playbook.mdx | 72 +++++ ...ooting.mdx => tracing-troubleshooting.mdx} | 0 content/evm/tracing/_meta.js | 6 - 9 files changed, 466 insertions(+), 151 deletions(-) create mode 100644 content/evm/indexing-best-practices.mdx create mode 100644 content/evm/pointers-deep-dive.mdx create mode 100644 content/evm/rpc-websockets.mdx rename content/evm/{tracing/javascript-tracers.mdx => tracing-javascript-tracers.mdx} (100%) rename content/evm/{tracing/index.mdx => tracing-overview.mdx} (100%) create mode 100644 content/evm/tracing-playbook.mdx rename content/evm/{tracing/troubleshooting.mdx => tracing-troubleshooting.mdx} (100%) delete mode 100644 content/evm/tracing/_meta.js diff --git a/content/evm/indexing-best-practices.mdx b/content/evm/indexing-best-practices.mdx new file mode 100644 index 00000000..6de39dd3 --- /dev/null +++ b/content/evm/indexing-best-practices.mdx @@ -0,0 +1,98 @@ +--- +title: 'Indexing Best Practices' +description: 'Reliable log ingestion on Sei: windows, backfills, reorgs, rate limits, and schema design.' +keywords: ['indexing', 'logs', 'reorgs', 'backfill', 'best practices'] +--- + +import { Callout } from 'nextra/components'; +import { CardGrid } from '../../mdx-components'; + +# Indexing Best Practices + +## Windows & Limits + +Sei inherits Tendermint finality (single block confirmation) but enforces RPC limits to protect full nodes. Respect the following caps when building log indexers: + +- `eth_getLogs` window default: **2,000 blocks**. +- Open-ended queries (no `fromBlock`/`toBlock`) cap at **10,000 logs**; always anchor both block bounds. +- Subscription catch-up delivers up to **512 events per batch** before backpressure kicks in. +- Archive providers may expose wider windows; detect and adapt via feature flags. + +## Backfill Strategy + +- Backfill newest → oldest to keep current data fresh while draining history. +- Use chunk sizes of 250–500 blocks to balance node load and throughput. +- Persist checkpoints after each successful window to resume without duplicate ingestion. +- When replaying synthetic events, record the `synthetic` log flag ([v6.1.11+](https://github.com/sei-protocol/sei-chain/releases/tag/v6.1.11)) to differentiate from contract-emitted logs. + +## Reorg Handling + +Sei produces instant finality; however, nodes may restart mid-ingest. Implement safeguards anyway: + +- Persist the highest fully processed height; after restarts resume from `height - safetyBuffer` (recommend 10 blocks). +- Verify block hash continuity even under finality to detect provider restarts. +- For websocket subscribers, rehydrate missed blocks using `eth_getLogs` if the connection drops for more than 3 seconds. + +## Rate Limits & Retries + +- Throttle requests to one window per 200 ms unless working with a dedicated archive endpoint. +- Respect HTTP `429` responses—exponential backoff starting at 1s, max 30s. +- Retry `EOF` and transient network errors up to 5 attempts; avoid retrying contract-level reverts. +- Encode idempotency by hashing blockNumber + logIndex to prevent duplicate writes when retries occur. + +## Schema Design + +- Model logs with composite unique key `(blockNumber, transactionHash, logIndex)`. +- Store `topics` as arrays with a secondary index on `topic0` for fast event filtering. +- Persist `synthetic` boolean to separate Cosmos module events from contract logs. +- Keep raw `data` hex for replay; additionally decode known ABI payloads into typed columns for analytics surfaces. + +## Flow At A Glance + +
+
+
+
+ 01 +

Discover latest height

+
+

Call `eth_blockNumber` every loop and persist the safe tip before starting a window.

+
+
+
+ 02 +

Windowed `eth_getLogs`

+
+

Query bounded ranges (≤2k blocks) and honour the log cap. Store the `synthetic` flag with each event.

+
+
+
+ 03 +

Write & checkpoint

+
+

Commit the batch, advance the checkpoint (`height`, `txHash`, `logIndex`), then throttle before the next loop.

+
+
+
+ +## Reference Implementations + + diff --git a/content/evm/pointers-deep-dive.mdx b/content/evm/pointers-deep-dive.mdx new file mode 100644 index 00000000..c2b4dbf5 --- /dev/null +++ b/content/evm/pointers-deep-dive.mdx @@ -0,0 +1,29 @@ +--- +title: 'Pointers Deep Dive' +description: 'Design patterns for bridging identities and assets between CW and EVM using Pointer & PointerView.' +keywords: ['pointer', 'pointerview', 'cw20', 'erc1155', 'erc721', 'interop'] +--- + +import { Callout } from 'nextra/components'; + +# Pointers Deep Dive + +Patterns for mapping CW assets/contracts to EVM via Pointer and querying via PointerView. + +## Mapping patterns + +Use Pointer for ownership/actions; use PointerView for safe queries. + +## Identifiers + + + +## Examples + +## Troubleshooting diff --git a/content/evm/reference.mdx b/content/evm/reference.mdx index 494b9792..206e66c7 100644 --- a/content/evm/reference.mdx +++ b/content/evm/reference.mdx @@ -5,51 +5,90 @@ keywords: ['json-rpc', 'api reference', 'evm endpoints', 'blockchain api', 'sei --- import { Callout } from 'nextra/components'; - -# RPC Reference Guide - -Sei provides comprehensive RPC (Remote Procedure Call) support for its EVM implementation, including both standard Ethereum JSON-RPC endpoints and Sei-specific custom extensions that enhance functionality for developers. - -## Table of Contents - -- [Overview](#overview) -- [Standard Ethereum Endpoints](#standard-ethereum-endpoints) -- [Sei Custom Endpoints](#sei-custom-endpoints) -- [Understanding Synthetic Transactions](#understanding-synthetic-transactions) -- [Use Case Examples](#use-case-examples) -- [Performance Considerations](#performance-considerations) -- [Error Handling](#error-handling) -- [Advanced Topics](#advanced-topics) - -## Overview - -Sei supports the [Ethereum JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/) with some Sei-specific extensions to support cross-VM operations, synthetic transactions, and other advanced features. - -All endpoints follow the standard JSON-RPC format: - -
-JSON-RPC Request/Response Format - -**Request Format** - -- HTTP method: always "`GET`" -- Header: `accept: application/json` -- Header: `content-type: application/json` -- Body (JSON): - - `id`: an arbitrary string identifier - - `jsonrpc`: always "2.0" - - `method`: endpoint name (e.g. "eth_sendRawTransaction") - - `params`: an array that differs from endpoint to endpoint - -**Response Format** - -- Body (JSON): - - `id`: the same identifier in request - - `jsonrpc`: always "2.0" - - `result`: an object that differs from endpoint to endpoint - - `error` (if applicable): error details - -
+import { TroubleshootingTable } from '../../mdx-components'; + +# RPC Reference + +Sei implements the [Ethereum JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/) with Sei-specific extensions for synthetic transactions and cross-VM operations. + +Full Ethereum compatibility maintained. Use standard tooling (ethers.js, viem, web3.js) without modification. + +## Configuration & Limits + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterDefaultDescription
+ max_log_no_block + 10,000Max logs returned when querying without block range
+ max_blocks_for_log + 2,000Max block window for `eth_getLogs` queries
+ max_subscriptions_new_head + 10,000Concurrent `newHeads` WS subscriptions per node
+ trace_timeout + 10sTimeout for `debug_trace*` calls
+ max_concurrent_trace_calls + 10Concurrent trace operations allowed
+
+ +## Namespace Differences + +### Gas accounting guarantees (v6.1.11+) + +As of [v6.1.11](https://github.com/sei-protocol/sei-chain/releases/tag/v6.1.11), `gasUsed` now always reflects the real gas charged on Sei for both blocks and receipts. Historical blocks may still show legacy values. + +- `eth_getBlock*` and `eth_getTransactionReceipt` expose gas numbers that now match Tendermint execution exactly. Receipt `gasUsed` = execution gas – refund; block `gasUsed` = sum of receipt gas. +- `eth_blockNumber` / `eth_getBlock*` no longer report negative or inflated `gasUsed` for synthetic transactions. +- Synthetic events emitted by Cosmos modules include a `synthetic` flag in the log metadata (see [Understanding Synthetic Transactions](#understanding-synthetic-transactions)). +- Pointer precompile and association flows are accounted for during estimation; the `eth_estimateGas` fallback now succeeds for wrapped pointer calls that previously under-estimated gas. + +### `eth_` Endpoints (Standard) + +- **EVM transactions only** - Compatible with all Ethereum tooling +- Ignores Cosmos-native transactions +- Transaction indices skip non-EVM txs + +### `sei_` Endpoints (Extended) + +- **Both EVM + Cosmos transactions** - Complete chain view +- Includes synthetic logs from Cosmos modules (Bank, Distribution, Staking) +- Transaction indices include all txs sequentially +- Adds `synthetic: true` flag to Cosmos-originated events (v6.1.11+) + +Use `sei_` endpoints for indexers needing cross-VM visibility. Use `eth_` for pure EVM applications. ## Standard Ethereum Endpoints @@ -1122,103 +1161,22 @@ When using Sei's custom endpoints, consider these performance recommendations: - The exclusion endpoints generally perform additional computation, so use them when needed - For high-volume applications, consider caching results -## Error Handling - -Sei's custom endpoints return standard Ethereum JSON-RPC errors plus some additional error codes: - -| Code | Message | Description | -| -------- | --------------------- | ---------------------------------------- | -| `-32000` | Invalid input | Generic input error | -| `-32001` | Resource not found | The requested resource was not found | -| `-32002` | Resource unavailable | The resource exists but is not available | -| `-32003` | Transaction rejected | The transaction was rejected | -| `-32004` | Method not supported | The method is not supported | -| `-32005` | Limit exceeded | Request exceeds defined limit | -| `-32006` | Version not supported | JSON-RPC version is not supported | -| `-32500` | Cross-VM error | Error in cross-VM operation | -| `-32501` | Synthetic tx error | Error processing synthetic transaction | - -Example error response: - -```json copy -{ - "jsonrpc": "2.0", - "id": 1, - "error": { - "code": -32500, - "message": "Cross-VM operation failed", - "data": { - "details": "CosmWasm contract execution reverted" - } - } -} -``` - -## Advanced Topics - -
-Creating Custom Tracers with Sei Extensions - -Sei supports custom JavaScript tracers with additional capabilities for cross-VM operations: - -```javascript copy -// Custom tracer with Sei extensions -const customTracer = { - // Standard tracer functions - step: function (log, db) { - /* ... */ - }, - fault: function (log, db) { - /* ... */ - }, - result: function (ctx, db) { - /* ... */ - }, - - // Sei extension to process synthetic operations - syntheticOp: function (op, db) { - // Handle synthetic operations - if (op.type === 'cwTransfer') { - // Process CosmWasm transfer - } - return null; - } -}; - -// Convert to string for JSON-RPC call -const tracerJson = JSON.stringify(customTracer); - -// Use the custom tracer -web3.eth.send('debug_traceTransaction', [txHash, { tracer: tracerJson }]); -``` - -
- -
-Subscribing to Synthetic Events - -Sei supports subscriptions that include synthetic events: - -```javascript copy -const Web3 = require('web3'); -const web3 = new Web3('wss://evm-rpc.sei-apis.com'); - -// Subscribe to all logs including synthetic ones -const subscription = web3.eth.subscribe('sei_logs', { - address: '0x123...', - topics: [...] -}, (error, log) => { - if (!error) { - console.log(`New log: ${log.transactionHash} (Synthetic: ${!!log.synthetic})`); - } -}); - -// Unsubscribe -subscription.unsubscribe((error, success) => { - if (success) { - console.log('Successfully unsubscribed'); - } -}); -``` - -
+## Troubleshooting + + 2,000 or result > 10,000 logs', 'Reduce block window; paginate with smaller ranges.'], + ['filter not found', 'Filter ID expired (120s timeout)', 'Recreate filter; poll more frequently.'], + ['no new subscription can be created', 'newHeads limit (10,000) reached', 'Reuse subscriptions; request node operator to increase limit.'], + ['execution reverted', 'eth_call or eth_estimateGas failed', 'Check contract state; inspect revert reason in error.data.'], + ['nonce too low / nonce too high', 'Transaction rejected during pre-checks', 'Query eth_getTransactionCount for current nonce; ensure sequential submission.'] + ]} +/> + +## References + +- Ethereum JSON-RPC spec: [ethereum.org/developers/docs/apis/json-rpc](https://ethereum.org/en/developers/docs/apis/json-rpc/) +- Sei RPC implementation: [github.com/sei-protocol/sei-chain/evmrpc](https://github.com/sei-protocol/sei-chain/tree/main/evmrpc) +- Node configuration: [EVM RPC Config](/node/evmrpc-config) +- WebSocket guide: [WebSockets & Subscriptions](/evm/rpc/websockets) +- Tracing playbook: [Debug Tracing](/evm/tracing) diff --git a/content/evm/rpc-websockets.mdx b/content/evm/rpc-websockets.mdx new file mode 100644 index 00000000..f09520ae --- /dev/null +++ b/content/evm/rpc-websockets.mdx @@ -0,0 +1,164 @@ +--- +title: 'WebSockets & Subscriptions' +description: 'Design robust WS consumers on Sei: caps, heartbeats, reconnect, replay, and best practices.' +keywords: ['websocket', 'subscriptions', 'newHeads', 'logs', 'reliability', 'sei evm'] +--- + +import { Callout } from 'nextra/components'; +import { TroubleshootingTable } from '../../mdx-components'; + +# WebSockets & Subscriptions + +Real-time event streams via WebSocket subscriptions (`eth_subscribe`) for `newHeads`, `logs`, and `newPendingTransactions`. + +## Configuration Limits + +| Parameter | Default | Description | +| :--------------------------- | :-------- | :------------------------------------------------------------------ | +| `max_subscriptions_new_head` | 10,000 | Maximum concurrent `newHeads` subscriptions per node | +| Subscription buffer | 10 blocks | Internal buffer per subscription; slow consumers trigger disconnect | +| Connection capacity | 100 | Maximum subscriptions per connection (all types combined) | + +Nodes enforce `max_subscriptions_new_head` globally. When the limit is reached, new `eth_subscribe('newHeads')` calls return `no new subscription can be created`. + +## Supported Subscription Types + +### newHeads + +Streams new block headers as they're produced (~400ms on Sei). + +```typescript copy +// Function: eth_subscribe('newHeads') +import WebSocket from 'ws'; + +const ws = new WebSocket('wss://evm-rpc.sei-apis.com'); + +ws.on('open', () => { + ws.send( + JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'eth_subscribe', + params: ['newHeads'] + }) + ); +}); + +ws.on('message', (data) => { + const msg = JSON.parse(data.toString()); + if (msg.method === 'eth_subscription') { + console.log('New block:', msg.params.result.number); + } +}); +``` + +### logs + +Streams logs matching filter criteria (address, topics). + +```typescript copy +// Function: eth_subscribe('logs', filter) +ws.send( + JSON.stringify({ + jsonrpc: '2.0', + id: 2, + method: 'eth_subscribe', + params: [ + 'logs', + { + address: '0xYourContract...', + topics: ['0xYourEventSignature...'] + } + ] + }) +); +``` + +### newPendingTransactions + +Streams transaction hashes as they enter the mempool. + +```typescript copy +// Function: eth_subscribe('newPendingTransactions') +ws.send( + JSON.stringify({ + jsonrpc: '2.0', + id: 3, + method: 'eth_subscribe', + params: ['newPendingTransactions'] + }) +); +``` + +## Connection Management + +**Heartbeats:** + +- Send ping frames every 30–60 seconds +- Missing pongs indicate stale connection; reconnect immediately + +**Backoff on reconnect:** + +- Use exponential backoff with jitter (start 1s, max 30s) +- After reconnect, fetch latest block via HTTP before resubscribing to establish checkpoint + +**Subscription cleanup:** + +- Always call `eth_unsubscribe` before closing connection +- Node auto-cleans subscriptions on disconnect, but explicit cleanup is best practice + +## Replay & Gap Handling + +When a WebSocket disconnects: + +1. **Track last processed block** (store `block.number` and `block.hash`) +2. **On reconnect**, fetch current head via HTTP `eth_blockNumber` +3. **Backfill gaps** using `eth_getLogs` with block ranges ≤ 2,000 blocks (respects `MaxBlocksForLog`) +4. **Deduplicate** by `(transactionHash, logIndex)` to handle overlaps +5. **Resume** subscription from current head + +```typescript copy +// Gap backfill pattern +async function backfillGap(fromBlock: number, toBlock: number) { + const logs = await httpProvider.send('eth_getLogs', [ + { + fromBlock: `0x${fromBlock.toString(16)}`, + toBlock: `0x${toBlock.toString(16)}`, + address: contractAddress + } + ]); + + // Deduplicate and process + const seen = new Set(); + for (const log of logs) { + const key = `${log.transactionHash}-${log.logIndex}`; + if (!seen.has(key)) { + seen.add(key); + processLog(log); + } + } +} +``` + +## Best Practices + +- **One subscription per topic** - Fan out internally rather than creating duplicate `newHeads` subscriptions +- **Monitor buffer health** - Track dropped subscriptions (channel closure) as a signal of slow consumption +- **Hybrid approach** - Use WebSocket for real-time updates, HTTP for historical queries and backfills +- **Avoid trace/sim during WS handling** - Offload heavy `debug_traceTransaction` calls to background workers + +## Troubleshooting + + + +## References + +- WebSocket implementation: [github.com/sei-protocol/sei-chain/evmrpc/subscribe.go](https://github.com/sei-protocol/sei-chain/blob/main/evmrpc/subscribe.go) +- Configuration: [EVM RPC Config](/node/evmrpc-config) diff --git a/content/evm/tracing/javascript-tracers.mdx b/content/evm/tracing-javascript-tracers.mdx similarity index 100% rename from content/evm/tracing/javascript-tracers.mdx rename to content/evm/tracing-javascript-tracers.mdx diff --git a/content/evm/tracing/index.mdx b/content/evm/tracing-overview.mdx similarity index 100% rename from content/evm/tracing/index.mdx rename to content/evm/tracing-overview.mdx diff --git a/content/evm/tracing-playbook.mdx b/content/evm/tracing-playbook.mdx new file mode 100644 index 00000000..21408043 --- /dev/null +++ b/content/evm/tracing-playbook.mdx @@ -0,0 +1,72 @@ +--- +title: 'Tracing Playbook' +description: 'Operational guide for debug tracing on Sei: timeouts, concurrency caps, lookback, JS tracers.' +keywords: ['tracing', 'debug_trace', 'lookback', 'timeout', 'concurrency'] +--- + +import { Callout } from 'nextra/components'; +import { KeyValueTable } from '../../mdx-components'; + +# Tracing Playbook + +## Limits & Tuning + + + Sei enforces limits to protect validators. Tune requests to stay within max_trace_lookback_blocks, trace_timeout, and max_concurrent_trace_calls. + + + + +Recommended tuning: + +- Prefer block ranges under 500 for iterative analysis; pipeline requests if you need full-day coverage. +- Set `tracerConfig.timeout` inside your request to a value `<=` cluster timeout; the lower of the two values wins. +- Use `disableStorage` and `disableStack` flags when you only need execution logs to reduce payload size. +- New frame length guard means stringifying massive intermediate objects will revert—log hashes or sample slices instead. + +## JS Tracers + +Sei ships go-ethereum v1.15.7-sei-7 with hardened JS tracer handling: + +- **Nonce stability**: the replay engine correctly bumps nonces across synthetic transactions. Custom tracers can rely on `result.stateDiff` matching post-state. +- **Panic surfacing**: runtime panics now return `{ error: { message, stack } }` instead of disconnecting the RPC session. Always inspect `error.data.trace` for the failed frame. +- **Length guard**: the runtime rejects trace frames >64 KB to prevent DoS vectors. For large responses, emit checkpoints (block hash, tx hash) and pull additional data using standard RPC calls. + +Example request: + +```json copy +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_traceTransaction", + "params": [ + "0x", + { + "tracer": "callTracer", + "timeout": "20s", + "tracerConfig": { + "enableNonce": true, + "onlyTopCall": false + } + } + ] +} +``` + +If you run custom tracers, lint them through `@sei-js/evm`'s `tracer-lint` script to ensure they respect nonce handling and frame-size limits. + +## Troubleshooting + +| Symptom | Cause | Fix | +| :------------------------------------------------------ | :---------------------------------------- | :----------------------------------------------------------------------------------- | +| `frame exceeded limit` | Tracer emitted >64 KB payload | Log hashes or slices; reduce recursion depth. | +| Response contains `{ error: { message: 'panic: ...' }}` | Runtime panic bubbled from tracer | Review `error.data.trace`, patch tracer, rerun. | +| `trace window exceeded` | Request spans beyond configured lookback | Reduce block range or contact ops for higher limit. | +| Empty `stateDiff` on synthetic tx | Trace is evaluating a Cosmos-only message | Use pointer precompile events or `eth_getTransactionReceipt` for synthetic metadata. | diff --git a/content/evm/tracing/troubleshooting.mdx b/content/evm/tracing-troubleshooting.mdx similarity index 100% rename from content/evm/tracing/troubleshooting.mdx rename to content/evm/tracing-troubleshooting.mdx diff --git a/content/evm/tracing/_meta.js b/content/evm/tracing/_meta.js deleted file mode 100644 index 5728cc12..00000000 --- a/content/evm/tracing/_meta.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - index: 'Overview', - 'javascript-tracers': 'JavaScript Tracers', - // 'end-to-end-examples': 'Complete Workflows', - troubleshooting: 'Troubleshooting Guide' -}; From 0924bf8043f52e5fd3d23f0a11e0358efc493f4f Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 19:52:32 -0400 Subject: [PATCH 05/25] Update node docs --- content/node/_meta.js | 1 + content/node/evmrpc-config.mdx | 67 ++++++++++++++++++++++++++++ content/node/technical-reference.mdx | 5 +++ content/node/troubleshooting.mdx | 58 +++++++++++++++++++++++- 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 content/node/evmrpc-config.mdx diff --git a/content/node/_meta.js b/content/node/_meta.js index 1609b88c..61c5cbfe 100644 --- a/content/node/_meta.js +++ b/content/node/_meta.js @@ -19,6 +19,7 @@ export default { title: 'Advanced Operations' }, 'advanced-config-monitoring': 'Advanced Configuration & Monitoring', + 'evmrpc-config': 'EVMRPC Config Runbook', 'technical-reference': 'Technical Reference', 'ibc-relayer': 'IBC Relayers' }; diff --git a/content/node/evmrpc-config.mdx b/content/node/evmrpc-config.mdx new file mode 100644 index 00000000..2c69926c --- /dev/null +++ b/content/node/evmrpc-config.mdx @@ -0,0 +1,67 @@ +--- +title: 'EVMRPC Config Runbook' +description: 'Field-by-field guide to evmrpc configuration with recommended values and SRE recipes.' +keywords: ['evmrpc', 'config', 'rate limits', 'tracing', 'operations', 'node'] +--- + +import { Callout } from 'nextra/components'; + +# EVMRPC Config Runbook + +This page mirrors `evmrpc/config.go` and provides recommended values and operational guidance. + +## Server + +- `http_enabled`, `http_port` +- `ws_enabled`, `ws_port` +- `cors_origins`, `ws_origins` + +## Timeouts + +- `read_timeout`, `read_header_timeout`, `write_timeout`, `idle_timeout` + +## Simulation + +- `simulation_gas_limit` +- `simulation_evm_timeout` + +## Filters & mempool + +- `filter_timeout` +- `checktx_timeout` +- `max_tx_pool_txs` +- `slow` +- `flush_receipt_sync` + +## Method controls & limits + +- `deny_list[]`: fail‑fast methods +- `max_log_no_block`: cap logs if no range specified (open‑ended) +- `max_blocks_for_log`: cap range span for `eth_getLogs` +- `max_subscriptions_new_head`: cap concurrent `newHeads` + +## Tracing & simulation concurrency + +- `max_concurrent_trace_calls`: 0 for unlimited; recommended: based on CPU cores +- `max_concurrent_simulation_calls` +- `max_trace_lookback_blocks`: 0 for unlimited; tune for storage/perf +- `trace_timeout` + +## Telemetry + +- `rpc_stats_interval`: periodic stats logging + +Start conservative, raise caps only after measuring saturation and latency. + +## Recommended defaults (production hint) + +- `max_blocks_for_log`: 2,000–5,000 +- `max_log_no_block`: 10,000 +- `max_concurrent_trace_calls`: ≤ CPU cores +- `trace_timeout`: 10s–30s depending on workload + +## SRE recipes + +- Spikes in `debug_trace*`: reduce `max_concurrent_trace_calls`, increase `trace_timeout` cautiously +- Log backfills: enforce windowing via `max_blocks_for_log` and educate clients +- WS churn: lower `max_subscriptions_new_head`, advise fanning‑out per process diff --git a/content/node/technical-reference.mdx b/content/node/technical-reference.mdx index 129d511c..3f8eb13b 100644 --- a/content/node/technical-reference.mdx +++ b/content/node/technical-reference.mdx @@ -160,6 +160,7 @@ max_open_connections = 900 timeout_broadcast_tx_commit = "10s" # Consensus Configuration +# Vote extensions removed in v0.6.4. Leave legacy flags unset. [consensus] wal_file = "data/cs.wal/wal" timeout_propose = "3s" @@ -170,6 +171,10 @@ timeout_precommit = "1s" timeout_precommit_delta = "500ms" timeout_commit = "1s" double_sign_check_height = 0 +# Deprecated (v0.5.x only; remove if present) +# create_empty_blocks = true +# create_empty_blocks_interval = "0s" +# vote_extensions_enable_height = 0 ``` diff --git a/content/node/troubleshooting.mdx b/content/node/troubleshooting.mdx index 252f7723..9989f106 100644 --- a/content/node/troubleshooting.mdx +++ b/content/node/troubleshooting.mdx @@ -13,6 +13,23 @@ operation. Here are the most frequent errors you might encounter and their solutions: +### Gas Accounting & RPC Anomalies ([v6.1.11+](https://github.com/sei-protocol/sei-chain/releases/tag/v6.1.11)) + +```text copy +Observation: Transaction receipts show `gasUsed = 0` +Resolution: Ensure the node is running sei-chain v6.1.11 or later. Replay the transaction with `seid tx ... --trace` or query via a node on the latest release. +``` + +```text copy +Observation: `eth_estimateGas` fails for pointer migrations +Resolution: Pointer association logic now runs inside the estimator. Retry on v6.1.11 or later and ensure the caller is correctly associated. +``` + +```text copy +Observation: `debug_traceTransaction` returns `frame exceeded limit` +Resolution: New 64 KB frame guard rejects oversized tracer responses. Reduce output size (log hashes, paginate) or split complex tracers. +``` + ### Consensus Errors When you encounter consensus errors, quick and appropriate action is essential: @@ -81,6 +98,9 @@ seid query staking validator $(seid tendermint show-validator) # Monitor real-time logs journalctl -fu seid -o cat +# Inspect mempool contention +seid tx mempool pending --output json | jq '.total' + # View system resource usage top -p $(pgrep seid) ``` @@ -351,13 +371,13 @@ Do this in your preferred way. Next, do a soft rollback with: -```bash +```bash copy seid rollback ``` And then a hard rollback with: -```bash +```bash copy seid rollback --hard ``` @@ -372,3 +392,37 @@ failed to initialize database: resource temporarily unavailable This means that you did not shutdown the node properly. Try to shutdown or kill the `seid` process directly in that case. If this doesn't help, restart your machine. Then try the rollback steps again. + +## Operator Quick Checks + +**Confirm accurate `gasUsed`** + +```bash +seid query tx TXHASH --output json | jq '.gas_used' +``` + +Expect non-zero values for transactions executed on v6.1.11 or later. + +**Inspect pointer associations** + +```bash +seid query evm pointers associated EVMADDR +``` + +Ensures Solo migrations target the correct Sei bech32 owners. + +**Validate tracer frame guard** + +```bash +curl -X POST $RPC/debug_traceTransaction ... +``` + +Requests should fail fast with `frame exceeded limit` if tracers emit >64 KB. + +**Measure mempool contention** + +```bash +seid tx mempool pending --output json | jq '.total' +``` + +Totals should remain stable (under 10k). Spikes indicate duplicate cache issues. From 2fe88cb833c04d452ad02226c80c31ba19339f64 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 19:52:53 -0400 Subject: [PATCH 06/25] Refresh indexer docs --- content/evm/indexer-providers/alchemy.mdx | 123 ++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 content/evm/indexer-providers/alchemy.mdx diff --git a/content/evm/indexer-providers/alchemy.mdx b/content/evm/indexer-providers/alchemy.mdx new file mode 100644 index 00000000..f0070c52 --- /dev/null +++ b/content/evm/indexer-providers/alchemy.mdx @@ -0,0 +1,123 @@ +--- +title: 'Alchemy Subgraphs' +--- + +import { Callout } from 'nextra/components'; +import { CardGrid, TroubleshootingTable } from '../../../mdx-components'; + +# Alchemy Subgraphs + +Standardized guide: Overview → Quickstart → Windows & Limits → Backfill → Reorgs → Examples → Troubleshooting. + +## Overview + +Alchemy Subgraphs delivers managed GraphQL indexing with seamless compatibility with The Graph tooling. A dedicated Sei pipeline ([v6.1.11+](https://github.com/sei-protocol/sei-chain/releases/tag/v6.1.11)) adds: + +- Native Sei EVM support with < 2 block lag under steady load. +- Automatic retries for synthetic transaction logs. +- Managed backfills with configurable block windows (default 2,000). +- Integrated monitoring dashboards (error rates, sync status, latency percentiles). + +Existing Graph CLI projects can be pointed at Alchemy by switching the gateway endpoint and redeploying without code changes. + +## Quickstart + +1. [Create an Alchemy account](https://www.alchemy.com/signup) and request access to the Sei Subgraphs beta. +2. Install the CLI: `npm install -g @graphprotocol/graph-cli` (compatible with Alchemy's gateway). +3. Initialise your subgraph: `graph init --from-example sei/my-dapp`. +4. Update the network configuration: + +```yaml copy +# subgraph.yaml excerpt +dataSources: + - kind: ethereum/contract + network: sei-mainnet + source: + address: '0x...' + startBlock: 12000000 + mapping: + apiVersion: 0.0.9 + language: wasm/assemblyscript +``` + +5. Authenticate: `graph auth --node https://subgraphs.sei.alchemy.com/api `. +6. Deploy: `graph deploy --node https://subgraphs.sei.alchemy.com/api sei/my-dapp`. + +## Windows & Limits + +- Default backfill window: **2,000 blocks** (adaptive). Use CLI flag `--block-window` to customise. +- Daily compute quota: 5 hours build time on the free tier; contact support for dedicated workers. +- Concurrent backfills per project: 2. +- Reorg depth assumption: 0 (Sei finality). Payloads auto-commit once ingested; manual rewinds available via dashboard. +- Synthetic event handling: automatically tagged via `synthetic=true` metadata. + +## Backfill Strategy + +- Stage subgraph versions: keep `production` indexing current data while `backfill` version replays history. +- Enable automatic snapshotting every 250k blocks to accelerate restarts. +- Use the CLI `graph build` locally with `--ipfs /tmp/ipfs` to catch ABI/schema errors before deploying. +- Pair backfills with the [Indexing Best Practices](../indexing/best-practices) guidance for retry behaviour. + +## Reorg Handling + +Sei blocks do not revert, but Alchemy maintains a virtual reorg buffer for consistency: + +- Recent blocks (last 20) stay in a mutable buffer for 5 minutes; this allows manual rewinds if upstream data is corrected. +- The dashboard shows `Finalized` vs `Buffered` heights; monitor and alert if the buffer grows unexpectedly (indicates provider lag). +- If a rewind is triggered (rare), redeploy the subgraph to the prior snapshot via the console. + +## Build Pipeline Snapshot + +
+
+ deployment loop +

Sei subgraph lifecycle

+
+
+
+

1. Generate schema & mappings

+

Run `graph codegen` locally; ensure synthetic fields are added to the schema.

+
+
+

2. Build & validate

+

Use `graph build` with a local IPFS daemon (or the bundled mock) to catch ABI mismatches fast.

+
+
+

3. Deploy to Alchemy

+

`graph deploy --node https://subgraphs.sei.alchemy.com/api` and watch the dashboard until lag shrinks under 2 blocks.

+
+
+
+ +## Implementation References + + + +## Troubleshooting + + 10 blocks', 'Backfill window too large or rate-limited', 'Reduce --block-window to 500 and monitor dashboard metrics.'], + ['Synthetic events missing', 'synthetic field not persisted in schema', 'Add boolean column in schema.graphql and rerun migrations.'], + ['Invalid API key', 'CLI auth target incorrect', 'Ensure you pass the Alchemy subgraphs endpoint to graph auth.'] + ]} +/> From ff1a055a63a47be7d178a954df4b74fb2bcfbd2c Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 20:04:47 -0400 Subject: [PATCH 07/25] Document RPC gas and consensus --- content/evm/_meta.js | 5 + content/evm/rpc-consensus-overview.mdx | 61 ++++++++++++ content/evm/rpc-gas-accounting.mdx | 86 +++++++++++++++++ content/evm/rpc-gas-reference.mdx | 57 +++++++++++ content/evm/rpc-panic-faq.mdx | 86 +++++++++++++++++ content/evm/rpc-regression-playbook.mdx | 121 ++++++++++++++++++++++++ content/node/technical-reference.mdx | 1 + 7 files changed, 417 insertions(+) create mode 100644 content/evm/rpc-consensus-overview.mdx create mode 100644 content/evm/rpc-gas-accounting.mdx create mode 100644 content/evm/rpc-gas-reference.mdx create mode 100644 content/evm/rpc-panic-faq.mdx create mode 100644 content/evm/rpc-regression-playbook.mdx diff --git a/content/evm/_meta.js b/content/evm/_meta.js index f15b6329..22588bba 100644 --- a/content/evm/_meta.js +++ b/content/evm/_meta.js @@ -66,6 +66,10 @@ export default { title: 'RPC' }, reference: 'RPC Reference', + 'rpc-gas-accounting': 'Gas Accounting', + 'rpc-gas-reference': 'Gas Reference', + 'rpc-regression-playbook': 'Regression Coverage', + 'rpc-panic-faq': 'Panic Handling FAQ', 'rpc-websockets': 'WebSockets', '-- Tracing': { @@ -89,6 +93,7 @@ export default { title: 'Advanced' }, 'pointers-deep-dive': 'Pointers', + 'rpc-consensus-overview': 'Consensus Deep Dive', transactions: 'Transactions', 'ibc-protocol': 'IBC on EVM', 'cosmwasm-precompiles': 'CosmWasm Precompiles', diff --git a/content/evm/rpc-consensus-overview.mdx b/content/evm/rpc-consensus-overview.mdx new file mode 100644 index 00000000..815a635f --- /dev/null +++ b/content/evm/rpc-consensus-overview.mdx @@ -0,0 +1,61 @@ +--- +title: 'Consensus Deep Dive' +description: 'Operator-oriented reference for Sei consensus, mempool, and vote extension status.' +keywords: ['consensus', 'mempool', 'vote extensions', 'sei tendermint'] +--- + +import { Callout } from 'nextra/components'; +import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; + +# Consensus Deep Dive + +Sei uses a Tendermint-based consensus engine tuned for high-throughput EVM workloads. This guide summarises the latest behavioural changes so validator operators and infrastructure teams can keep nodes healthy. + +Covers `sei-tendermint@02c9462f1` (duplicate transaction cache sizing) and the current vote-extension posture. + +## Key Concepts + + + +## Duplicate Transaction Cache (Sep 2025) + +`sei-tendermint@02c9462f1` fixes the maximum size for the duplicate transaction cache. Previously, cached entries could exceed the intended limit when keys exceeded the configured length. + +- Ensure `config.toml` has an appropriate `mempool.cache_size` (default: 10,000). +- If you run large validators, consider bumping to 20,000 while monitoring memory usage. The cache now enforces the cap precisely. +- Removing transactions via RPC or after inclusion also clears them from the cache thanks to the same patch. + +## Vote Extensions + +- Vote extensions remain disabled for public Sei networks. +- `types/params.go` enforces that once enabled, they cannot be turned off (`"vote extensions cannot be disabled once enabled"`). +- Operators should watch release notes before toggling any configuration that references vote extensions. + +## Operational Best Practices + +- Keep `mempool.recheck` disabled unless you need re-validation after app updates; recheck increases consensus latency. +- Monitor `consensus/height` and `mempool/txs` Prometheus metrics. Spikes in cached entries without block inclusion may indicate upstream spam. +- Use `seid unsafe-reset-all` cautiously; it clears the cache and state. Prefer state sync or snapshots when recovering from consensus stalls. + +## Troubleshooting + + + +## Related Docs + +- `/node/technical-reference` – `config.toml` deep dive. +- `/node/troubleshooting` – crash and panic handling for validators. +- `rpc-gas-accounting` – gas flow ties into finalised blocks. diff --git a/content/evm/rpc-gas-accounting.mdx b/content/evm/rpc-gas-accounting.mdx new file mode 100644 index 00000000..9e8b02da --- /dev/null +++ b/content/evm/rpc-gas-accounting.mdx @@ -0,0 +1,86 @@ +--- +title: 'Gas Accounting Guarantees' +description: 'Understand how Sei computes gas for blocks, receipts, and estimations after v6.1.11, including validation steps for developers and operators.' +keywords: ['gas accounting', 'sei evm gas', 'eth_estimateGas', 'synthetic transactions', 'gasUsed guarantees'] +--- + +import { Callout } from 'nextra/components'; +import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; + +# Gas Accounting Guarantees + +Sei `v6.1.11` [release notes](https://github.com/sei-protocol/sei-chain/releases/tag/v6.1.11) patched multiple gaps in gas attribution. This page captures the definitive rules for `gasUsed`, `gasLimit`, and `eth_estimateGas`, and provides validation steps to confirm your infrastructure is consuming the numbers correctly. + + + Updates in this guide reflect sei-chain@1efdec1eb (“Fix block gas used”) and supporting RPC fixes merged in the same release train. + + +## What Changed in v6.1.11 + + + +## Gas Flow At a Glance + +1. **Ante Handler (Cosmos)** validates fees and signatures. Synthetic envelopes terminate here and do not contribute to EVM gas totals. +2. **EVM Execution** debits gas for opcodes, precompile calls, and Solo/SIP-3 migrations exactly as go-ethereum would. +3. **Refund Calculation** subtracts gas refunds generated by `SELFDESTRUCT` or storage clears before persisting the receipt. +4. **Receipt Storage** writes `receipt.gasUsed` = execution gas − refund. +5. **Block Aggregation** sums all receipt `gasUsed` values and stores the result in the Tendermint block header, which RPC surfaces through `eth_getBlock*`. + +## Verifying Your Node or Indexer + +| Step | Command | Expected Output | +| :--- | :--------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | +| 1 | `seid query block 169750823 --output json` | `block.last_commit` matches the RPC block hash and `gas_used` field > 0 | +| 2 | `curl -s http://localhost:8545 -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["0xa1c7bbc", true]}'` | `gasUsed` equals the sum of each transaction receipt after step 3 | +| 3 | `curl -s http://localhost:8545 -d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":[""]}'` | `gasUsed` never returns `0x0` for EVM transactions in post-`v6.1.11` blocks | +| 4 | `curl -s http://localhost:8545 -d '{"jsonrpc":"2.0","id":1,"method":"eth_estimateGas","params":[{"to":"0x...pointer","data":"0x..."}]}'` | Estimation succeeds without manual gas padding for pointer/Solo operations | + +## Configuration Touchpoints + +Use the RPC config defaults from `evmrpc/config.go` as a baseline: + + + +## Regression Checklist + +Run this suite after upgrades or when deploying new indexer infra: + +- Fetch 10 random receipts from the latest block and ensure `gasUsed` is non-zero and consistent with on-chain execution. +- Confirm `eth_getBlockByNumber(..., true)` shows `gasUsed` equal to the arithmetic sum of the receipts from the same call. +- Estimate gas for: + - A pointer migration using `precompile-pointer:addNativePointer`. + - A Solo claim (`precompile-solo:claim`). + - A standard ERC-20 transfer. + Expect each to execute with a margin < 2% from actual runtime gas. +- Replay `eth_getLogs` across `max_blocks_for_log` and ensure no `panic` error surfaces (see Regression Coverage guide). + +## Troubleshooting + + sum(receipts)', 'Legacy block prior to `v6.1.11` or synthetic transaction included.', 'Accept mismatch for legacy heights; filter out `synthetic: true` logs when aggregating.'] + ]} +/> + +## Related Material + +- `rpc-gas-reference` – Quick reference for configuration and formulae. +- `rpc-regression-playbook` – Full QA checklist for RPC consumers. +- `tracing-playbook` – Debugging tools for tracing anomalies. diff --git a/content/evm/rpc-gas-reference.mdx b/content/evm/rpc-gas-reference.mdx new file mode 100644 index 00000000..de7afdba --- /dev/null +++ b/content/evm/rpc-gas-reference.mdx @@ -0,0 +1,57 @@ +--- +title: 'Gas Reference' +description: 'Quick lookup for Sei RPC gas parameters, formulas, and release-specific behaviour.' +keywords: ['gas reference', 'rpc limits', 'gas formulas', 'sei chain config'] +--- + +import { KeyValueTable, Callout } from '../../mdx-components'; + +# Gas Reference + +Use this page as the condensed cheat-sheet for gas-related settings and formulas on Sei. Every entry references the release where the behaviour was introduced so you can map it to your node fleet. + +## Core Parameters + + + +## Gas Formulas + +| Scenario | Formula | Notes | +| :------------------ | :------------------------------------ | :------------------------------------------------------------------------- | +| Transaction receipt | `gasUsed = totalGasConsumed – refund` | Refund is capped at 20% of `gasLimit` in line with go-ethereum rules. | +| Block total | `block.gasUsed = Σ receipt.gasUsed` | Accurate for blocks produced after `v6.1.11`. Earlier heights may diverge. | +| Estimation buffer | `suggestedGas = estimate × 1.05` | Apply a 5% safety margin for user-facing UIs. | + +## Release Notes + +- [`v6.1.11`](https://github.com/sei-protocol/sei-chain/releases/tag/v6.1.11) + - Block `gasUsed` aligned with Tendermint execution (`sei-chain@1efdec1eb`). + - Pointer and Solo precompile flows warmed in `eth_estimateGas`. + - Synthetic logs tagged with `synthetic: true`. +- [`v1.15.7-sei-7`](https://github.com/sei-protocol/go-ethereum/releases/tag/v1.15.7-sei-7) + - Tracer length guard prevents malformed frames from panicking. +- [`v1.15.7-sei-6`](https://github.com/sei-protocol/go-ethereum/releases/tag/v1.15.7-sei-6) + - RPC now returns `{ error: { data: { trace } } }` when custom tracers panic. + +## Operational Tips + +- Keep `max_blocks_for_log` conservative on shared endpoints; let power users run their own archive node if they need large spans. +- After upgrades, clear any CDN or proxy caches so clients immediately pick up corrected `gasUsed` values. +- Use Grafana or Prometheus exporters to monitor `rpc_gas_used` and `rpc_trace_pending` metrics if you run the RPC server from `sei-chain`. + +Always restart both the Tendermint process and the embedded RPC server once you upgrade to a release that changes gas accounting. Partial restarts can keep stale caches alive. + +## See Also + +- `rpc-gas-accounting` – Full explanation of execution flow and validation steps. +- `rpc-regression-playbook` – Suggested QA coverage for RPC consumers. +- `tracing-playbook` – Panic handling for tracers and debug sessions. diff --git a/content/evm/rpc-panic-faq.mdx b/content/evm/rpc-panic-faq.mdx new file mode 100644 index 00000000..17acdbd0 --- /dev/null +++ b/content/evm/rpc-panic-faq.mdx @@ -0,0 +1,86 @@ +--- +title: 'Panic Handling FAQ' +description: 'How Sei surfaces panics in RPC responses and how operators should react.' +keywords: ['panic handling', 'rpc errors', 'tracer panics', 'node crashes'] +--- + +import { Callout } from 'nextra/components'; +import { TroubleshootingTable } from '../../mdx-components'; + +# Panic Handling FAQ + +Sei users occasionally hit runtime panics—either from custom tracers, malformed transactions, or internal bugs. Recent releases focus on surfacing those panics cleanly instead of terminating the node. This FAQ explains how they look on the wire and what you should do when you see them. + +This guidance is based on `sei-chain@1a1805cff`, `sei-chain@4fac51f02`, `sei-chain@bc47cc90f`, `sei-chain@70c633940`, and go-ethereum `v1.15.7-sei-6`/`-7`. + +## Client-Side Symptoms + +- **RPC error payloads** now return `200 OK` with a JSON structure similar to: + +```json copy +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": -32000, + "message": "panic: runtime error: invalid memory address or nil pointer dereference", + "data": { + "trace": "github.com/...", + "path": "debug_traceTransaction", + "txHash": "0x1234..." + } + } +} +``` + +- `sei_trace*ExcludeTraceFail` endpoints strip transactions that would have triggered the panic. +- When a panic originates from a tracer frame exceeding expected length, the node returns `frame length exceeds limit`. + +## Operator Checklist + +1. **Capture Logs**: tail the node logs for the timestamp of the panic. Confirm whether it is a tracer panic or a node-side issue. +2. **Identify Endpoint**: note which method was called (`debug_traceTransaction`, `eth_getLogs`, etc.) and the parameters. +3. **Reproduce Safely**: rerun the call against a staging node. If reproducible, capture stack trace and report. +4. **Assess Impact**: + - If the panic stems from a custom tracer, patch the tracer. + - If the panic is node-side and reproducible, open an issue with the Sei team, including the stack trace. + +## Known Panic Sources + +| Source | Fixed In | Mitigation | +| :----------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------- | +| Missing panic guard in RPC goroutines | `sei-chain@1a1805cff` | Upgrade and restart the node. | +| Metrics exporter panics | `sei-chain@4fac51f02` | Ensure the binary contains this fix. | +| `debug_trace*` returning panic string only | `sei-chain@bc47cc90f` | RPC now wraps the panic into proper error payloads; client must parse `error.data.trace`. | +| Panic in tracer frame length | go-ethereum `v1.15.7-sei-7` | Update tracer binaries; adds explicit length checks. | + +## Troubleshooting + + + +## When to Escalate + +- Node process exits even after applying the listed fixes. +- Panic occurs on `eth_getLogs` or other critical endpoints across multiple nodes. +- Error payload lacks `data.trace` despite being on the patched version. + +Collect: + +- RPC request/response pair +- Full stack trace from logs +- Node version (`seid version --long`) and go-ethereum tag + +Then open a ticket with the Sei core team. + +## Related Guides + +- `rpc-regression-playbook` – Suggested QA suite after upgrading nodes. +- `tracing-playbook` – Techniques for debugging custom tracers. +- `node/troubleshooting` – Node-level crash handling. diff --git a/content/evm/rpc-regression-playbook.mdx b/content/evm/rpc-regression-playbook.mdx new file mode 100644 index 00000000..2ca88eb8 --- /dev/null +++ b/content/evm/rpc-regression-playbook.mdx @@ -0,0 +1,121 @@ +--- +title: 'RPC Regression Coverage' +description: 'Suggested test suite for validating Sei RPC behaviour after upgrades.' +keywords: ['rpc regression', 'testing', 'json-rpc', 'trace testing'] +--- + +import { Callout, Tabs, Tab } from 'nextra/components'; +import { TroubleshootingTable } from '../../mdx-components'; + +# RPC Regression Coverage + +Run these checks whenever you upgrade to a new Sei release or patch your RPC nodes. Each section references recent upstream patches so you know which behaviour to expect. + +## Gas & Receipts + +Targets `sei-chain@1efdec1eb` and later. + +- Confirm the latest block’s `gasUsed` equals the sum of all receipt `gasUsed` values. +- Sample 20 receipts and ensure none return `0x0` gas for non-legacy heights. +- Verify `eth_estimateGas` succeeds for: + - ERC-20 transfer + - Pointer association (`precompile-pointer:addNativePointer`) + - Solo claim (`precompile-solo:claim`) + +## Logs & Filters + +- Query `eth_getLogs` across a window equal to `max_blocks_for_log` and confirm the response succeeds without panics. +- Issue overlapping log filters to ensure the node enforces `max_log_no_block` and returns partial results rather than failing. +- Validate `eth_newFilter` followed by `eth_getFilterLogs` after replaying 100 blocks. + +## Websocket Subscriptions + +- Maintain `newHeads`, `logs`, and `newPendingTransactions` subscriptions simultaneously. Watch for disconnects once you reach `max_subscriptions_new_head`. +- Ensure heartbeats arrive within the interval defined by your client’s timeout. + +## Tracing & Panic Handling + +- Execute `debug_traceTransaction` on a known failing transaction; expect `{ error: { message, data.trace } }` (go-ethereum `v1.15.7-sei-6`). +- Run custom tracers with intentionally malformed frames to verify the length guard (go-ethereum `v1.15.7-sei-7`). +- Stress test the tracer pool with `max_concurrent_trace_calls + 2` parallel requests and verify excess requests queue or fail gracefully. + +## Sample Scripts + + + + +```javascript copy +import { JsonRpcProvider } from 'ethers'; + +const provider = new JsonRpcProvider('http://localhost:8545'); + +async function verifyBlockGas(blockTag = 'latest') { + const block = await provider.send('eth_getBlockByNumber', [blockTag, true]); + const receipts = await Promise.all(block.transactions.map((tx) => provider.send('eth_getTransactionReceipt', [tx.hash]))); + + const sum = receipts.reduce((acc, r) => acc + BigInt(r.gasUsed), 0n); + if (sum !== BigInt(block.gasUsed)) { + throw new Error(`Mismatch: block=${block.gasUsed} receipts=${sum.toString(16)}`); + } +} + +verifyBlockGas().then(() => console.log('Gas totals match.')); +``` + + + + +```go copy +package main + +import ( + "context" + "log" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" +) + +func main() { + client, err := ethclient.Dial("ws://localhost:8546") + if err != nil { + log.Fatal(err) + } + defer client.Close() + + headers := make(chan *types.Header) + sub, err := client.SubscribeNewHead(context.Background(), headers) + if err != nil { + log.Fatal(err) + } + + for i := 0; i < 100; i++ { + select { + case err := <-sub.Err(): + log.Fatalf("subscription dropped: %v", err) + case header := <-headers: + log.Printf("new head: %s", header.Hash()) + } + } +} +``` + + + + +## Troubleshooting + + + +## Related Resources + +- `rpc-gas-accounting` +- `rpc-gas-reference` +- `rpc-panic-faq` +- `rpc-websockets` diff --git a/content/node/technical-reference.mdx b/content/node/technical-reference.mdx index 3f8eb13b..859b5fae 100644 --- a/content/node/technical-reference.mdx +++ b/content/node/technical-reference.mdx @@ -116,6 +116,7 @@ snapshot-keep-recent = 2 [mempool] size = 5000 max-txs-bytes = 1073741824 +# Duplicate transaction cache. Requires `sei-tendermint@02c9462f1` or newer to enforce the cap precisely. cache-size = 10000 # State store configuration From b22259594123edde4d48203034e1123f1487cf04 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 22:53:29 -0400 Subject: [PATCH 08/25] restore default callouts --- app/globals.css | 131 ------------------------------------------------ 1 file changed, 131 deletions(-) diff --git a/app/globals.css b/app/globals.css index d29feee1..0346d128 100644 --- a/app/globals.css +++ b/app/globals.css @@ -253,134 +253,3 @@ aside, .nextra-nav-container a.nx-me-auto { margin-inline-end: 0 !important; } - -/* Custom Callout Styling */ -.nextra-callout { - position: relative !important; - border-radius: 0.5rem !important; - border: 1px solid !important; - padding: 0.875rem 1rem !important; - font-size: 0.875rem !important; - line-height: 1.6 !important; - margin: 1.25rem 0 !important; -} - -.nextra-callout > *:first-child { - margin-top: 0 !important; -} - -.nextra-callout > *:last-child { - margin-bottom: 0 !important; -} - -/* Hide default Nextra icons */ -.nextra-callout svg { - display: none !important; -} - -/* Add type label badge */ -.nextra-callout::before { - content: ''; - position: absolute; - top: 0.875rem; - left: 1rem; - padding: 0.125rem 0.5rem; - font-size: 0.625rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.05em; - border-radius: 0.25rem; - margin-bottom: 0.5rem; -} - -/* Add padding to content to account for label */ -.nextra-callout > div { - padding-left: 4.5rem !important; - min-height: 1.5rem !important; -} - -/* Info callout */ -.nextra-callout.nx-border-blue-200 { - border-color: rgb(59 130 246 / 0.3) !important; - background: rgb(239 246 255 / 0.4) !important; - color: rgb(30 64 175) !important; -} - -.nextra-callout.nx-border-blue-200::before { - content: 'INFO'; - background: rgb(59 130 246) !important; - color: white !important; -} - -.dark .nextra-callout.nx-border-blue-200 { - border-color: rgb(59 130 246 / 0.3) !important; - background: rgb(30 58 138 / 0.1) !important; - color: rgb(219 234 254) !important; -} - -.dark .nextra-callout.nx-border-blue-200::before { - background: rgb(37 99 235) !important; - color: rgb(219 234 254) !important; -} - -/* Warning callout */ -.nextra-callout.nx-border-orange-200 { - border-color: rgb(245 158 11 / 0.3) !important; - background: rgb(254 243 199 / 0.4) !important; - color: rgb(146 64 14) !important; -} - -.nextra-callout.nx-border-orange-200::before { - content: 'WARNING'; - background: rgb(245 158 11) !important; - color: white !important; -} - -.dark .nextra-callout.nx-border-orange-200 { - border-color: rgb(245 158 11 / 0.3) !important; - background: rgb(120 53 15 / 0.1) !important; - color: rgb(254 243 199) !important; -} - -.dark .nextra-callout.nx-border-orange-200::before { - background: rgb(217 119 6) !important; - color: rgb(254 243 199) !important; -} - -/* Error callout */ -.nextra-callout.nx-border-red-200 { - border-color: rgb(239 68 68 / 0.3) !important; - background: rgb(254 242 242 / 0.4) !important; - color: rgb(153 27 27) !important; -} - -.nextra-callout.nx-border-red-200::before { - content: 'ERROR'; - background: rgb(239 68 68) !important; - color: white !important; -} - -.dark .nextra-callout.nx-border-red-200 { - border-color: rgb(239 68 68 / 0.3) !important; - background: rgb(127 29 29 / 0.1) !important; - color: rgb(254 226 226) !important; -} - -.dark .nextra-callout.nx-border-red-200::before { - background: rgb(220 38 38) !important; - color: rgb(254 226 226) !important; -} - -/* Style inline code within callouts */ -.nextra-callout code { - background: rgb(0 0 0 / 0.08) !important; - color: inherit !important; - padding: 0.125rem 0.375rem !important; - border-radius: 0.25rem !important; - font-size: 0.8125rem !important; - font-weight: 500 !important; -} - -.dark .nextra-callout code { - background: rgb(255 255 255 / 0.1) !important; -} From 59031568072e7d743ac97d9fc2cf1d2ce1960380 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 22:53:39 -0400 Subject: [PATCH 09/25] streamline not-found --- app/not-found.tsx | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/app/not-found.tsx b/app/not-found.tsx index 1cfa9af4..b97c59f4 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,26 +1,13 @@ -'use client'; -import Link from 'next/link'; -import Head from 'next/head'; - -export default function Custom404() { +export default function NotFound() { return ( - <> - - - -
-
-

404

-
-

This page could not be found.

-
- - - Go to Home - -
- +
+

Page not found

+

+ The page you're looking for doesn't exist. Check the URL or return to the documentation home. +

+ + Back to docs + +
); } From 30355253de6b998f8bbfc9a07c0574fc24c70ff2 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 22:54:08 -0400 Subject: [PATCH 10/25] operator runbooks --- content/node/_meta.js | 40 ++++++-- content/node/node-incident-playbooks.mdx | 90 +++++++++++++++++ content/node/rpc-deployment-hardening.mdx | 95 ++++++++++++++++++ content/node/seidb-adoption-faq.mdx | 64 ++++++++++++ .../node/validator-operations-playbook.mdx | 66 +++++++++++++ content/node/validator-upgrade-runbook.mdx | 98 +++++++++++++++++++ 6 files changed, 443 insertions(+), 10 deletions(-) create mode 100644 content/node/node-incident-playbooks.mdx create mode 100644 content/node/rpc-deployment-hardening.mdx create mode 100644 content/node/seidb-adoption-faq.mdx create mode 100644 content/node/validator-operations-playbook.mdx create mode 100644 content/node/validator-upgrade-runbook.mdx diff --git a/content/node/_meta.js b/content/node/_meta.js index 61c5cbfe..047bfa92 100644 --- a/content/node/_meta.js +++ b/content/node/_meta.js @@ -2,24 +2,44 @@ export default { index: { title: 'Home' }, - '-- Node Operations': { + '-- Getting Started': { type: 'separator', - title: 'Node Operations' + title: 'Getting Started' }, 'node-operators': 'Overview', - statesync: 'Statesync', - snapshot: 'Snapshot Sync', 'node-types': 'Node Types', + + '-- Running Nodes': { + type: 'separator', + title: 'Running Nodes' + }, + statesync: 'State Sync', + snapshot: 'Snapshots', troubleshooting: 'Troubleshooting', - swagger: 'API configuration', - validators: 'Validator Operations Guide', - '-- Advanced Operations': { + '-- Validators': { + type: 'separator', + title: 'Validators' + }, + validators: 'Validator Guide', + 'validator-operations-playbook': 'Operations Playbook', + 'validator-upgrade-runbook': 'Upgrade Runbook', + + '-- RPC & Infrastructure': { + type: 'separator', + title: 'RPC & Infrastructure' + }, + 'evmrpc-config': 'EVM RPC Configuration', + 'rpc-deployment-hardening': 'RPC Deployment', + 'node-incident-playbooks': 'Incident Response', + 'seidb-adoption-faq': 'SeiDB', + + '-- Reference': { type: 'separator', - title: 'Advanced Operations' + title: 'Reference' }, - 'advanced-config-monitoring': 'Advanced Configuration & Monitoring', - 'evmrpc-config': 'EVMRPC Config Runbook', 'technical-reference': 'Technical Reference', + 'advanced-config-monitoring': 'Monitoring & Metrics', + swagger: 'API Documentation', 'ibc-relayer': 'IBC Relayers' }; diff --git a/content/node/node-incident-playbooks.mdx b/content/node/node-incident-playbooks.mdx new file mode 100644 index 00000000..1a66b29a --- /dev/null +++ b/content/node/node-incident-playbooks.mdx @@ -0,0 +1,90 @@ +--- +title: 'Node Incident Playbooks' +description: 'Response procedures for common Sei node incidents (non-validator).' +keywords: ['incident response', 'node operations', 'state sync failure', 'p2p partition'] +--- + +import { Callout } from 'nextra/components'; +import { TroubleshootingTable } from '../../mdx-components'; + +# Node Incident Playbooks + +These playbooks target sentry and full-node operators supporting Sei validators. Each scenario lists detection signals, immediate actions, and verification steps. + +## 1. P2P Partition + +**Symptoms**: Height stagnates, logs show repeated `dial timeout` or missing peers. + +**Actions**: + +1. Check network connectivity (`ping`/`traceroute`) to known peers. +2. Validate `persistent_peers` and `seeds` in `config.toml`. +3. Restart Tendermint process to re-open connections: + + ```bash copy + systemctl restart seid + ``` + +4. If behind firewalls, ensure inbound/outbound ports (`26656`) are open. + +**Verify**: `seid status` shows increasing height and peer count > 0. + +## 2. State Sync Failure + +**Symptoms**: State sync stalls or crashes with `snapshot not found`. + +**Actions**: + +1. Confirm snapshot providers are reachable. +2. Clear data directory and attempt re-sync: + + ```bash copy + systemctl stop seid + rm -rf ~/.sei/data + systemctl start seid + ``` + +3. If issue persists, switch to trusted snapshot provider or use backup snapshot. + +**Verify**: Node progresses past the snapshot height and enters normal sync mode. + +## 3. Snapshot Corruption + +**Symptoms**: Restored snapshot fails to start or panics on boot. + +**Actions**: + +1. Validate checksum of the snapshot archive. +2. Re-extract snapshot to a clean directory. +3. Consider using SeiDB’s built-in pruning to regenerate snapshot post-migration. + +**Verify**: Node completes boot sequence without panics. + +## 4. High Disk Usage + +**Symptoms**: Disk usage exceeds alert thresholds; pruning ineffective. + +**Actions**: + +1. Run `seidadmin prune` (if available) or enable state-store pruning in `app.toml`. +2. Rotate logs frequently; implement logrotate. +3. Offload old snapshots to external storage. + +**Verify**: Disk usage returns to acceptable levels; monitoring alerts clear. + +## Quick Reference + + + +## Logging & Escalation + +- Collect `journalctl -u seid --since "15 minutes ago"` for escalation tickets. +- Include `config.toml`, `app.toml`, and latest snapshot metadata when contacting core teams. +- Document incident timeline and resolution for internal postmortems. diff --git a/content/node/rpc-deployment-hardening.mdx b/content/node/rpc-deployment-hardening.mdx new file mode 100644 index 00000000..81a9c3cc --- /dev/null +++ b/content/node/rpc-deployment-hardening.mdx @@ -0,0 +1,95 @@ +--- +title: 'RPC Deployment Guide' +description: 'Blueprint for operating production-grade Sei RPC endpoints.' +keywords: ['rpc deployment', 'reverse proxy', 'rate limiting', 'Next.js docs build'] +--- + +import { Callout } from 'nextra/components'; +import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; + +# RPC Deployment Guide + +Operate RPC nodes as dedicated infrastructure separate from validators. This guide outlines the hardened setup required for Sei’s EVM JSON-RPC endpoints. + +## Architecture Overview + + + +## RPC Configuration (`config/evm.toml`) + +- Keep `http_enabled = true`, `http_port = 8545`, `ws_enabled = true` only if websockets are required. +- Set `cors_origins` and `ws_origins` explicitly; avoid `*` on public endpoints. +- Tune limits per load: + + ```toml copy + max_log_no_block = 10000 + max_blocks_for_log = 2000 + max_subscriptions_new_head = 5000 # lower on constrained hardware + max_concurrent_trace_calls = 10 + trace_timeout = "30s" + ``` + +- For high traffic, increase `max_blocks_for_log` cautiously and ensure hardware can cope. + +## Reverse Proxy Sample (Nginx) + +```nginx copy +server { + listen 443 ssl; + server_name rpc.sei.example; + + ssl_certificate /etc/letsencrypt/live/rpc/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/rpc/privkey.pem; + + location / { + proxy_pass http://127.0.0.1:8545; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 60s; + limit_req zone=rpc burst=50 nodelay; + } +} + +limit_req_zone $binary_remote_addr zone=rpc:10m rate=20r/s; +``` + +- Enforce WebSocket proxying for `eth_subscribe` if required (`proxy_set_header Upgrade`). +- Enable access logs and ship to your SIEM. + +## Deployment Workflow + +1. Pull new `sei` release and update binaries. +2. Restart RPC node, clear `.next` cache for docs if hosted alongside. +3. Run `yarn build` for docs, ensuring `_document.tsx` exists to avoid Next.js errors. +4. Redeploy static site (if using Vercel/Netlify) or serve `out/` directory behind CDN. +5. Smoke test with regression scripts (`rpc-regression-playbook`). + +## Runtime Monitoring + +- Scrape metrics: `rpc_trace_pending`, `rpc_filter_count`, `rpc_ws_subscriptions`. +- Collect logs and alert on `panic` messages or repeated 500 responses. +- Track proxy metrics (requests per second, rate-limit hits). + +## Troubleshooting + + + +## Security Checklist + +- Restrict public access to HTTP only; offer WebSocket access to partners who need streaming data. +- Maintain allowlists/denylists at proxy layer. +- Rotate TLS certificates regularly and automate renewals. +- Keep RPC nodes patched with latest OS updates. diff --git a/content/node/seidb-adoption-faq.mdx b/content/node/seidb-adoption-faq.mdx new file mode 100644 index 00000000..efa50036 --- /dev/null +++ b/content/node/seidb-adoption-faq.mdx @@ -0,0 +1,64 @@ +--- +title: 'SeiDB Adoption FAQ' +description: 'Evaluate, enable, and operate SeiDB for improved storage performance.' +keywords: ['SeiDB', 'database', 'state storage', 'migration', 'performance'] +--- + +import { Callout } from 'nextra/components'; +import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; + +# SeiDB Adoption FAQ + +SeiDB is the database layer designed to improve state storage performance on Sei. Use this FAQ to decide when and how to enable it. + +## Why SeiDB? + + + +## Prerequisites + +- Run the latest binary that includes the SeiDB integration (see repository release notes). +- Ensure disk I/O can handle compacted writes (NVMe recommended). +- Backup your existing `data/` directory before enabling. + +## Enable Steps + +1. Stop the node. +2. Update `app.toml`: + + ```toml copy + [state-store] + ss-enable = true + ss-backend = "seidb" + ss-keep-recent = 100000 + ss-prune-interval = 600 + ``` + +3. Restart the node and monitor logs for `seidb` initialization messages. + +## Migration Considerations + +- First start performs data migration. Expect higher CPU and disk usage. +- Keep a recent snapshot handy; if migration encounters issues, you can roll back. +- After migration, take a fresh snapshot for disaster recovery. + +## Monitoring + +- Track disk utilization and compaction stats from logs. +- Monitor snapshot creation time; SeiDB should reduce snapshot size compared to RocksDB. + +## Troubleshooting + + diff --git a/content/node/validator-operations-playbook.mdx b/content/node/validator-operations-playbook.mdx new file mode 100644 index 00000000..18330714 --- /dev/null +++ b/content/node/validator-operations-playbook.mdx @@ -0,0 +1,66 @@ +--- +title: 'Validator Operations Playbook' +description: 'Daily operations, tuning, and troubleshooting for Sei validators.' +keywords: ['validator', 'operations', 'mempool', 'monitoring', 'consensus'] +--- + +import { Callout } from 'nextra/components'; +import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; + +# Validator Operations Playbook + +This runbook collects the key operational tasks for maintaining a production Sei validator, with emphasis on the latest mempool and consensus changes (`sei-tendermint@02c9462f1`). + +## Daily Checklist + + 0 in the last 100 blocks.'], + ['Oracle participation', 'If applicable, verify oracle votes landed within the window.'] + ]} +/> + +## Configuration Highlights + +- `mempool.cache_size = 10000` (default). With `sei-tendermint@02c9462f1`, the cache cap is enforced accurately. Raise to 20,000 on high-load validators. +- Keep `mempool.broadcast` enabled to propagate transactions quickly. +- `consensus.create_empty_blocks = true` (default) ensures liveness even under low load. Avoid disabling unless you understand the implications. + +## Monitoring Metrics + +Track these Prometheus metrics: + +- `consensus_height`, `consensus_round` – detect stalls. +- `consensus_validator_power` – verify voting power changes. +- `mempool_size`, `mempool_cache_size` – watch for saturation. +- `rpc_trace_pending` – ensure tracer load stays below `max_concurrent_trace_calls`. + +## Incident Response + +Always snapshot your validator before modifying configuration or restarting under duress. + +1. **Consensus halt** + + - Confirm majority of validators are on the same binary. + - Check logs for `nil vote extension` or duplicate tx warnings. + - Coordinate restart if required; use state sync if node falls far behind. + +2. **Mempool overflow** + + - Increase `mempool.cache_size` gradually (requires `sei-tendermint@02c9462f1`). + - Prune invalid transactions by restarting with `--mempool.recheck=true` temporarily. + +3. **RPC saturation** + - Scale out dedicated RPC nodes; validator should keep RPC closed to the public when possible. + +## Troubleshooting + + diff --git a/content/node/validator-upgrade-runbook.mdx b/content/node/validator-upgrade-runbook.mdx new file mode 100644 index 00000000..9b9193de --- /dev/null +++ b/content/node/validator-upgrade-runbook.mdx @@ -0,0 +1,98 @@ +--- +title: 'Validator Upgrade Runbook' +description: 'Step-by-step process for upgrading Sei validators with zero surprises.' +keywords: ['sei upgrade', 'validator upgrade', 'rolling restart', 'gasUsed verification'] +--- + +import { Callout } from 'nextra/components'; +import { KeyValueTable } from '../../mdx-components'; + +# Validator Upgrade Runbook + +This runbook targets production validators moving between Sei releases (e.g., `v6.1.10` → `v6.1.11`). Follow the steps sequentially to ensure consensus safety and to confirm new behaviours—such as the `gasUsed` corrections from `sei-chain@1efdec1eb`—are live. + +## 1. Pre-flight + + + +Ensure `systemd` unit files or process managers are ready; upgrades should be performed during low network activity and coordinated with fellow validators when possible. + +## 2. Fetch & Verify Binary + +1. Download the release tarball or build from source at the tagged commit. +2. Verify checksum: + + ```bash copy + sha256sum seid-v6.1.11-linux-amd64.tar.gz + ``` + +3. Extract and replace the binary: + + ```bash copy + tar -xzf seid-v6.1.11-linux-amd64.tar.gz + sudo mv seid /usr/local/bin/seid + ``` + +4. Confirm version: + + ```bash copy + seid version --long + ``` + + Expect `name: sei-chain`, `version: v6.1.11`, and commit hash matching the release (`1efdec1eb...`). + +## 3. Rolling Restart + +Never restart all validators concurrently. Stagger restarts to preserve liveness. + +1. Stop the validator process (`systemctl stop seid` or equivalent). +2. Apply configuration changes if required (e.g., raise `mempool.cache_size` alongside `sei-tendermint@02c9462f1`). +3. Start the process and tail logs: + + ```bash copy + journalctl -u seid -f --since "5 minutes ago" + ``` + +4. Wait for the node to catch up to network height (check `SyncInfo.catching_up`). +5. Repeat for sentries and remaining infrastructure. + +## 4. Post-Upgrade Validation + +Run these checks immediately after the validator resumes signing: + +- **Consensus height** increases and matches peers. +- **GasUsed correctness**: + + ```bash copy + curl -s http://localhost:8545 \ + -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["latest", true]}' \ + | jq '.result.gasUsed' + ``` + + Confirm the value equals the sum of constituent receipts. + +- **Duplicate-tx cache**: If increasing `mempool.cache_size`, run `seid debug mempool-stats` (or inspect metrics) to ensure the cache size aligns with the new limit. +- **Signer health**: Inspect logs for `Committed state` and absence of `panic` messages. + +## 5. Rollback Plan + +If anomalies arise: + +1. Stop the upgraded binary. +2. Restore the previous binary from backup (`/usr/local/bin/seid-prev`). +3. Restore the snapshot taken in the pre-flight stage if state corruption is suspected. +4. Rejoin consensus and communicate the rollback to peers. + +## 6. Document the Upgrade + +- Record block height of the upgrade, commit hash, and any configuration changes in your ops log. +- Archive the binary and checksums for audit purposes. +- Update your monitoring dashboards to reflect new metrics if applicable. From 56a240ece303caaa8f69986343871141fddd7e28 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Tue, 30 Sep 2025 22:54:22 -0400 Subject: [PATCH 11/25] rpc authoring docs --- content/evm/_meta.js | 2 + content/evm/changelog.mdx | 9 +++ content/evm/rpc-gas-reference.mdx | 3 +- content/evm/rpc-regression-playbook.mdx | 10 +-- content/evm/rpc-synthetic-transactions.mdx | 69 +++++++++++++++++++ content/evm/rpc-tracer-guide.mdx | 79 ++++++++++++++++++++++ 6 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 content/evm/rpc-synthetic-transactions.mdx create mode 100644 content/evm/rpc-tracer-guide.mdx diff --git a/content/evm/_meta.js b/content/evm/_meta.js index 22588bba..813b05a3 100644 --- a/content/evm/_meta.js +++ b/content/evm/_meta.js @@ -94,6 +94,8 @@ export default { }, 'pointers-deep-dive': 'Pointers', 'rpc-consensus-overview': 'Consensus Deep Dive', + 'rpc-synthetic-transactions': 'Synthetic Transactions', + 'rpc-tracer-guide': 'Tracer Authoring', transactions: 'Transactions', 'ibc-protocol': 'IBC on EVM', 'cosmwasm-precompiles': 'CosmWasm Precompiles', diff --git a/content/evm/changelog.mdx b/content/evm/changelog.mdx index 4ba8b018..9961e26f 100644 --- a/content/evm/changelog.mdx +++ b/content/evm/changelog.mdx @@ -30,3 +30,12 @@ Want to be notified of new releases? **Watch** the [sei-protocol/sei-chain](http ## Latest Changes + +## Release-to-Doc Mapping (v6.1.11+) + +| Release | Code Reference | Documentation Touchpoints | +| :------------------------- | :----------------------------- | :------------------------------------------------------------------------------------- | +| `v6.1.11` | `sei-chain@1efdec1eb` | `rpc-gas-accounting`, `rpc-gas-reference`, `rpc-synthetic-transactions` | +| `v1.15.7-sei-6` | go-ethereum panic wrapper | `rpc-panic-faq`, `rpc-regression-playbook`, `rpc-tracer-guide` | +| `v1.15.7-sei-7` | go-ethereum frame length guard | `rpc-tracer-guide`, `rpc-regression-playbook` | +| `sei-tendermint@02c9462f1` | Duplicate tx cache fix | `rpc-consensus-overview`, `validator-operations-playbook`, `/node/technical-reference` | diff --git a/content/evm/rpc-gas-reference.mdx b/content/evm/rpc-gas-reference.mdx index de7afdba..ddc86c03 100644 --- a/content/evm/rpc-gas-reference.mdx +++ b/content/evm/rpc-gas-reference.mdx @@ -4,7 +4,8 @@ description: 'Quick lookup for Sei RPC gas parameters, formulas, and release-spe keywords: ['gas reference', 'rpc limits', 'gas formulas', 'sei chain config'] --- -import { KeyValueTable, Callout } from '../../mdx-components'; +import { Callout } from 'nextra/components'; +import { KeyValueTable } from '../../mdx-components'; # Gas Reference diff --git a/content/evm/rpc-regression-playbook.mdx b/content/evm/rpc-regression-playbook.mdx index 2ca88eb8..72edb6ce 100644 --- a/content/evm/rpc-regression-playbook.mdx +++ b/content/evm/rpc-regression-playbook.mdx @@ -4,7 +4,7 @@ description: 'Suggested test suite for validating Sei RPC behaviour after upgrad keywords: ['rpc regression', 'testing', 'json-rpc', 'trace testing'] --- -import { Callout, Tabs, Tab } from 'nextra/components'; +import { Callout, Tabs } from 'nextra/components'; import { TroubleshootingTable } from '../../mdx-components'; # RPC Regression Coverage @@ -42,7 +42,7 @@ Run these checks whenever you upgrade to a new Sei release or patch your RPC nod ## Sample Scripts - +
```javascript copy import { JsonRpcProvider } from 'ethers'; @@ -62,8 +62,8 @@ async function verifyBlockGas(blockTag = 'latest') { verifyBlockGas().then(() => console.log('Gas totals match.')); ``` - - +
+
```go copy package main @@ -100,7 +100,7 @@ func main() { } ``` - +
## Troubleshooting diff --git a/content/evm/rpc-synthetic-transactions.mdx b/content/evm/rpc-synthetic-transactions.mdx new file mode 100644 index 00000000..313c7e2c --- /dev/null +++ b/content/evm/rpc-synthetic-transactions.mdx @@ -0,0 +1,69 @@ +--- +title: 'Synthetic Transactions on Sei' +description: 'Understanding synthetic envelopes, gasless flows, and how to index them.' +keywords: ['synthetic transaction', 'sei_associate', 'gasless', 'rpc indexing'] +--- + +import { Callout } from 'nextra/components'; +import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; + +# Synthetic Transactions on Sei + +Sei supports **synthetic** transactions—Cosmos-originated operations that are wrapped and surfaced through the EVM RPC layer. These include association flows, governance events, and distribution payouts. This guide explains how they appear through JSON-RPC and how to index them correctly after `v6.1.11`. + +## Key Properties + + + +## Association Flow (`sei_associate`) + +```json copy +{ + "jsonrpc": "2.0", + "id": 1, + "method": "sei_associate", + "params": [ + { + "custom_message": "Link my account", + "r": "0x...", + "s": "0x...", + "v": "0x1c" + } + ] +} +``` + +- Sends a synthetic transaction without gas fees; verification happens in the Cosmos ante handler. +- Resulting logs appear under `sei_seiAssociation` with `synthetic: true`. +- Indexers should persist the association tuple `(seiAddress, evmAddress)` and treat the event as finalized once included in a block. + +## Indexing Strategy + +1. Use `sei_getLogs` to fetch both EVM and synthetic logs. +2. Filter by `{ synthetic: true }` to isolate Cosmos-originated events. +3. Store synthetic logs in a parallel table if you need to keep them separate from EVM logs. +4. Cross-reference with the Cosmos module (e.g., Distribution) for additional metadata when necessary. + +## Interaction With Gas Accounting + +- Synthetic envelopes do not alter receipt `gasUsed` (see `rpc-gas-accounting`). +- When computing block-level gas metrics, exclude synthetic logs entirely; they are informational signals only. +- Estimating gas for synthetic flows is unnecessary—`sei_associate` and similar methods are processed outside the EVM gas model. + +## Troubleshooting + + diff --git a/content/evm/rpc-tracer-guide.mdx b/content/evm/rpc-tracer-guide.mdx new file mode 100644 index 00000000..7a63bf27 --- /dev/null +++ b/content/evm/rpc-tracer-guide.mdx @@ -0,0 +1,79 @@ +--- +title: 'Custom Tracer Authoring Guide' +description: 'Best practices for building JavaScript tracers against Sei’s go-ethereum fork.' +keywords: ['debug_traceTransaction', 'debug_traceCall', 'custom tracer', 'go-ethereum tracer', 'panic handling'] +--- + +import { Callout } from 'nextra/components'; +import { TroubleshootingTable } from '../../mdx-components'; + +# Custom Tracer Authoring Guide + +Sei exposes the standard `debug_trace*` RPC suite and accepts the same JavaScript tracer scripts as upstream go-ethereum. This guide captures the runtime expectations introduced in `v1.15.7-sei-6` and `v1.15.7-sei-7`, plus Sei-specific conventions for panic handling. + +## Runtime Expectations + +Sei ships go-ethereum `v1.15.7-sei-7` (`eth/tracers/api.go`, `eth/tracers/native/call.go`). Ensure your tracer works with that build. + +- **Frames limited**: the tracer must not emit frames larger than the native guard added in `v1.15.7-sei-7`. Chunk large payloads or return summaries. +- **Panic surfacing**: when your tracer panics, the node returns `{ error: { message, data.trace } }` instead of dropping the connection (v1.15.7-sei-6). +- **Timeouts**: respect `trace_timeout` (default 30s). Long-running tracers are terminated. +- **Concurrency**: at most `max_concurrent_trace_calls` (default 10) run in parallel. Plan for queuing. +- **Exclude failing traces**: Operators may call `sei_traceBlockByNumberExcludeTraceFail`. Your tracer should fail fast, letting healthy traces continue. + +## Authoring Patterns + +```javascript copy +const tracer = { + result: { + steps: [], + gasUsed: 0, + errors: [] + }, + step(log, db) { + try { + this.result.steps.push({ + pc: log.getPC(), + op: log.op.toString(), + gasCost: log.getCost() + }); + this.result.gasUsed += log.getCost(); + } catch (err) { + this.result.errors.push(String(err)); + throw err; // bubble up so Sei surfaces it in error.data.trace + } + }, + fault(log, db) { + this.result.errors.push(`fault: ${log.getError()}`); + }, + result() { + return this.result; + } +}; + +module.exports = tracer; +``` + +### Recommendations + +- Wrap logic in `try/catch` to append diagnostic context before re-throwing. +- Keep returned objects shallow; avoid large arrays or deeply nested state. +- Include a version identifier in the result to track script revisions. +- Use `db.getBalance`, `db.getCode`, etc., sparingly; each call hits execution state. + +## Validation Checklist + +- **Panic recovery**: intentionally throw inside `step` and confirm RPC returns `error.data.trace`. +- **Frame size**: log the serialized response size. Keep under 1 MB to stay below default limits. +- **Concurrency**: run `max_concurrent_trace_calls + 1` concurrent traces; ensure one request waits rather than panics. +- **Timeout**: run a tracer that spins > `trace_timeout`. The request should report a timeout error. + +## Troubleshooting + + From e4ad43f7febaf66f67a0d41f9fbc5e41e3eb4640 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 16:08:58 -0400 Subject: [PATCH 12/25] tighten operator styling --- .../node/validator-operations-playbook.mdx | 35 ++++++++++++++----- content/node/validator-upgrade-runbook.mdx | 4 +-- mdx-components.js | 14 ++++---- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/content/node/validator-operations-playbook.mdx b/content/node/validator-operations-playbook.mdx index 18330714..3c3bff90 100644 --- a/content/node/validator-operations-playbook.mdx +++ b/content/node/validator-operations-playbook.mdx @@ -24,18 +24,37 @@ This runbook collects the key operational tasks for maintaining a production Sei ## Configuration Highlights -- `mempool.cache_size = 10000` (default). With `sei-tendermint@02c9462f1`, the cache cap is enforced accurately. Raise to 20,000 on high-load validators. -- Keep `mempool.broadcast` enabled to propagate transactions quickly. -- `consensus.create_empty_blocks = true` (default) ensures liveness even under low load. Avoid disabling unless you understand the implications. + ## Monitoring Metrics -Track these Prometheus metrics: + 2 minutes.'], + ['`consensus_validator_power`', 'Monitor stake adjustments and make sure jailed status is cleared after unjails.'], + ['`mempool_size` / `mempool_cache_size`', 'Alert when size approaches cache cap; combine with duplicate-tx warnings from Tendermint logs.'], + ['`rpc_trace_pending`', 'Track tracer load if the validator exposes RPC to trusted partners; ensure the value stays under `max_concurrent_trace_calls`.'] + ]} +/> + +## Slashing Guardrails -- `consensus_height`, `consensus_round` – detect stalls. -- `consensus_validator_power` – verify voting power changes. -- `mempool_size`, `mempool_cache_size` – watch for saturation. -- `rpc_trace_pending` – ensure tracer load stays below `max_concurrent_trace_calls`. +Penalty windows on Sei follow the Cosmos 5% missed-signing threshold. Keep the following checks in your daily workflow to avoid downtime or double-sign slashing. + +` and alert when `missed_blocks_counter` increases faster than expected.'], + ['Double-sign watchdog', 'Confirm sentry nodes run duplicate vote detection or host a light client to halt on conflicting signatures.'], + ['Key custody', 'Keep HSM or KMS access logs; disable fallback keys once rotations finish to prevent unintended parallel signing.'] + ]} +/> ## Incident Response diff --git a/content/node/validator-upgrade-runbook.mdx b/content/node/validator-upgrade-runbook.mdx index 9b9193de..8aefdd5b 100644 --- a/content/node/validator-upgrade-runbook.mdx +++ b/content/node/validator-upgrade-runbook.mdx @@ -17,9 +17,9 @@ This runbook targets production validators moving between Sei releases (e.g., `v rows={[ ['Sync status', '`seid status | jq .SyncInfo.catching_up` returns `false`.'], ['Height checkpoint', '`seid status | jq .SyncInfo.latest_block_height` → record for post-upgrade comparison.'], - ['Snapshot', '`seidadmin snapshot create --output /var/sei/snapshots/YYYYMMDD` (or provider-specific tooling).'], + ['Snapshot', '`seidadmin snapshot create --output /var/sei/snapshots/YYYYMMDD` (or provider-specific tooling). Store hashes off-host.'], ['Peer health', 'Verify sentry/full-node connectivity; capture `~/.sei/config/peers` in case of rollback.'], - ['Gas baseline', '`curl -s localhost:8545 ... eth_getBlockByNumber latest true` and store `gasUsed`.'] + ['Gas baseline', '`curl -s localhost:8545 ... eth_getBlockByNumber latest true` and store `gasUsed` for post-upgrade validation.'] ]} /> diff --git a/mdx-components.js b/mdx-components.js index ea636068..8c6c84f4 100644 --- a/mdx-components.js +++ b/mdx-components.js @@ -4,15 +4,13 @@ const themeComponents = getThemeComponents(); export function KeyValueTable({ rows = [] }) { return ( -
- +
+
- {rows.map(([k, v], i) => ( - - - + {rows.map(([label, value], i) => ( + + + ))} From 2699f4e384dca7d9981853fb7391cc4e0c633a24 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 17:44:58 -0400 Subject: [PATCH 13/25] improve table spacing --- mdx-components.js | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/mdx-components.js b/mdx-components.js index 8c6c84f4..3d7fa174 100644 --- a/mdx-components.js +++ b/mdx-components.js @@ -2,15 +2,35 @@ import { useMDXComponents as getThemeComponents } from 'nextra-theme-docs'; const themeComponents = getThemeComponents(); +function renderInlineCode(content) { + if (typeof content !== 'string') { + return content; + } + + return content.split(/(`[^`]+`)/g).map((part, idx) => { + if (part.startsWith('`') && part.endsWith('`')) { + return ( + + {part.slice(1, -1)} + + ); + } + + return {part}; + }); +} + export function KeyValueTable({ rows = [] }) { return ( -
+
- {k} - {v}
{label}{value}
{rows.map(([label, value], i) => ( - - + + ))} @@ -21,7 +41,7 @@ export function KeyValueTable({ rows = [] }) { export function CardGrid({ items = [] }) { return ( -
+
{items.map((it, i) => ( +
{label}{value} + {renderInlineCode(label)} + {renderInlineCode(value)}
@@ -65,7 +85,7 @@ export function FunctionList({ items = [] }) { export function TroubleshootingTable({ rows = [] }) { return ( -
+
@@ -80,8 +100,8 @@ export function TroubleshootingTable({ rows = [] }) { - - + + ))} @@ -92,7 +112,7 @@ export function TroubleshootingTable({ rows = [] }) { export function VersionTimeline({ releases = [] }) { return ( -
+
{error} {cause}{fix}{renderInlineCode(cause)}{renderInlineCode(fix)}
From ceeb47a1cbd7b0f2702069cc0c3eb094af25b5e6 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 17:56:56 -0400 Subject: [PATCH 14/25] refine evm ui blocks --- app/not-found.tsx | 8 +- content/evm/rpc-consensus-overview.mdx | 73 +++++++++++--- content/evm/rpc-gas-accounting.mdx | 38 +++++--- content/evm/rpc-gas-reference.mdx | 13 ++- content/evm/rpc-panic-faq.mdx | 22 +++++ content/evm/rpc-regression-playbook.mdx | 11 +++ content/evm/rpc-synthetic-transactions.mdx | 33 +++++-- content/evm/rpc-tracer-guide.mdx | 38 +++++--- content/evm/rpc-websockets.mdx | 108 ++++++--------------- 9 files changed, 207 insertions(+), 137 deletions(-) diff --git a/app/not-found.tsx b/app/not-found.tsx index b97c59f4..1ec09fed 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,3 +1,7 @@ +'use client'; + +import Link from 'next/link'; + export default function NotFound() { return ( ); } diff --git a/content/evm/rpc-consensus-overview.mdx b/content/evm/rpc-consensus-overview.mdx index 815a635f..cba8a4ec 100644 --- a/content/evm/rpc-consensus-overview.mdx +++ b/content/evm/rpc-consensus-overview.mdx @@ -24,25 +24,50 @@ Sei uses a Tendermint-based consensus engine tuned for high-throughput EVM workl ]} /> -## Duplicate Transaction Cache (Sep 2025) +`sei-tendermint@02c9462f1` corrected duplicate transaction cache accounting, and the public networks continue to run with vote extensions disabled. -`sei-tendermint@02c9462f1` fixes the maximum size for the duplicate transaction cache. Previously, cached entries could exceed the intended limit when keys exceeded the configured length. +## Duplicate Transaction Cache -- Ensure `config.toml` has an appropriate `mempool.cache_size` (default: 10,000). -- If you run large validators, consider bumping to 20,000 while monitoring memory usage. The cache now enforces the cap precisely. -- Removing transactions via RPC or after inclusion also clears them from the cache thanks to the same patch. + 90% for > 2 minutes.'] + ]} +/> ## Vote Extensions -- Vote extensions remain disabled for public Sei networks. -- `types/params.go` enforces that once enabled, they cannot be turned off (`"vote extensions cannot be disabled once enabled"`). -- Operators should watch release notes before toggling any configuration that references vote extensions. + ## Operational Best Practices -- Keep `mempool.recheck` disabled unless you need re-validation after app updates; recheck increases consensus latency. -- Monitor `consensus/height` and `mempool/txs` Prometheus metrics. Spikes in cached entries without block inclusion may indicate upstream spam. -- Use `seid unsafe-reset-all` cautiously; it clears the cache and state. Prefer state sync or snapshots when recovering from consensus stalls. + ## Troubleshooting @@ -54,8 +79,24 @@ Sei uses a Tendermint-based consensus engine tuned for high-throughput EVM workl ]} /> -## Related Docs - -- `/node/technical-reference` – `config.toml` deep dive. -- `/node/troubleshooting` – crash and panic handling for validators. -- `rpc-gas-accounting` – gas flow ties into finalised blocks. +## Related Material + + diff --git a/content/evm/rpc-gas-accounting.mdx b/content/evm/rpc-gas-accounting.mdx index 9e8b02da..26806496 100644 --- a/content/evm/rpc-gas-accounting.mdx +++ b/content/evm/rpc-gas-accounting.mdx @@ -28,20 +28,36 @@ Sei `v6.1.11` [release notes](https://github.com/sei-protocol/sei-chain/releases ## Gas Flow At a Glance -1. **Ante Handler (Cosmos)** validates fees and signatures. Synthetic envelopes terminate here and do not contribute to EVM gas totals. -2. **EVM Execution** debits gas for opcodes, precompile calls, and Solo/SIP-3 migrations exactly as go-ethereum would. -3. **Refund Calculation** subtracts gas refunds generated by `SELFDESTRUCT` or storage clears before persisting the receipt. -4. **Receipt Storage** writes `receipt.gasUsed` = execution gas − refund. -5. **Block Aggregation** sums all receipt `gasUsed` values and stores the result in the Tendermint block header, which RPC surfaces through `eth_getBlock*`. + ## Verifying Your Node or Indexer -| Step | Command | Expected Output | -| :--- | :--------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | -| 1 | `seid query block 169750823 --output json` | `block.last_commit` matches the RPC block hash and `gas_used` field > 0 | -| 2 | `curl -s http://localhost:8545 -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["0xa1c7bbc", true]}'` | `gasUsed` equals the sum of each transaction receipt after step 3 | -| 3 | `curl -s http://localhost:8545 -d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":[""]}'` | `gasUsed` never returns `0x0` for EVM transactions in post-`v6.1.11` blocks | -| 4 | `curl -s http://localhost:8545 -d '{"jsonrpc":"2.0","id":1,"method":"eth_estimateGas","params":[{"to":"0x...pointer","data":"0x..."}]}'` | Estimation succeeds without manual gas padding for pointer/Solo operations | +` — ensure `gas_used` > 0 and matches RPC hash.'], + ['Receipt reconciliation', '`eth_getBlockByNumber(..., true)` — sum receipts client-side and confirm equality.'], + ['Receipt non-zero check', '`eth_getTransactionReceipt` — post-`v6.1.11` EVM transactions never return `gasUsed = 0x0`.'], + ['Estimator parity', '`eth_estimateGas` on pointer/Solo flows — succeeds without heavy manual padding.'] + ]} +/> ## Configuration Touchpoints diff --git a/content/evm/rpc-gas-reference.mdx b/content/evm/rpc-gas-reference.mdx index ddc86c03..7b8406c1 100644 --- a/content/evm/rpc-gas-reference.mdx +++ b/content/evm/rpc-gas-reference.mdx @@ -11,16 +11,15 @@ import { KeyValueTable } from '../../mdx-components'; Use this page as the condensed cheat-sheet for gas-related settings and formulas on Sei. Every entry references the release where the behaviour was introduced so you can map it to your node fleet. -## Core Parameters +## Quick Lookup diff --git a/content/evm/rpc-panic-faq.mdx b/content/evm/rpc-panic-faq.mdx index 17acdbd0..90a81d16 100644 --- a/content/evm/rpc-panic-faq.mdx +++ b/content/evm/rpc-panic-faq.mdx @@ -13,6 +13,28 @@ Sei users occasionally hit runtime panics—either from custom tracers, malforme This guidance is based on `sei-chain@1a1805cff`, `sei-chain@4fac51f02`, `sei-chain@bc47cc90f`, `sei-chain@70c633940`, and go-ethereum `v1.15.7-sei-6`/`-7`. +## Panic Surface Area + + + ## Client-Side Symptoms - **RPC error payloads** now return `200 OK` with a JSON structure similar to: diff --git a/content/evm/rpc-regression-playbook.mdx b/content/evm/rpc-regression-playbook.mdx index 72edb6ce..7e679eb9 100644 --- a/content/evm/rpc-regression-playbook.mdx +++ b/content/evm/rpc-regression-playbook.mdx @@ -11,6 +11,17 @@ import { TroubleshootingTable } from '../../mdx-components'; Run these checks whenever you upgrade to a new Sei release or patch your RPC nodes. Each section references recent upstream patches so you know which behaviour to expect. +## Regression Checklist + + + ## Gas & Receipts Targets `sei-chain@1efdec1eb` and later. diff --git a/content/evm/rpc-synthetic-transactions.mdx b/content/evm/rpc-synthetic-transactions.mdx index 313c7e2c..e948da94 100644 --- a/content/evm/rpc-synthetic-transactions.mdx +++ b/content/evm/rpc-synthetic-transactions.mdx @@ -15,11 +15,11 @@ Sei supports **synthetic** transactions—Cosmos-originated operations that are @@ -47,10 +47,25 @@ Sei supports **synthetic** transactions—Cosmos-originated operations that are ## Indexing Strategy -1. Use `sei_getLogs` to fetch both EVM and synthetic logs. -2. Filter by `{ synthetic: true }` to isolate Cosmos-originated events. -3. Store synthetic logs in a parallel table if you need to keep them separate from EVM logs. -4. Cross-reference with the Cosmos module (e.g., Distribution) for additional metadata when necessary. + ## Interaction With Gas Accounting diff --git a/content/evm/rpc-tracer-guide.mdx b/content/evm/rpc-tracer-guide.mdx index 7a63bf27..f5185f32 100644 --- a/content/evm/rpc-tracer-guide.mdx +++ b/content/evm/rpc-tracer-guide.mdx @@ -13,13 +13,25 @@ Sei exposes the standard `debug_trace*` RPC suite and accepts the same JavaScrip ## Runtime Expectations -Sei ships go-ethereum `v1.15.7-sei-7` (`eth/tracers/api.go`, `eth/tracers/native/call.go`). Ensure your tracer works with that build. - -- **Frames limited**: the tracer must not emit frames larger than the native guard added in `v1.15.7-sei-7`. Chunk large payloads or return summaries. -- **Panic surfacing**: when your tracer panics, the node returns `{ error: { message, data.trace } }` instead of dropping the connection (v1.15.7-sei-6). -- **Timeouts**: respect `trace_timeout` (default 30s). Long-running tracers are terminated. -- **Concurrency**: at most `max_concurrent_trace_calls` (default 10) run in parallel. Plan for queuing. -- **Exclude failing traces**: Operators may call `sei_traceBlockByNumberExcludeTraceFail`. Your tracer should fail fast, letting healthy traces continue. + ## Authoring Patterns @@ -63,10 +75,14 @@ module.exports = tracer; ## Validation Checklist -- **Panic recovery**: intentionally throw inside `step` and confirm RPC returns `error.data.trace`. -- **Frame size**: log the serialized response size. Keep under 1 MB to stay below default limits. -- **Concurrency**: run `max_concurrent_trace_calls + 1` concurrent traces; ensure one request waits rather than panics. -- **Timeout**: run a tracer that spins > `trace_timeout`. The request should report a timeout error. + ## Troubleshooting diff --git a/content/evm/rpc-websockets.mdx b/content/evm/rpc-websockets.mdx index f09520ae..0e4af9bb 100644 --- a/content/evm/rpc-websockets.mdx +++ b/content/evm/rpc-websockets.mdx @@ -21,91 +21,37 @@ Real-time event streams via WebSocket subscriptions (`eth_subscribe`) for `newHe Nodes enforce `max_subscriptions_new_head` globally. When the limit is reached, new `eth_subscribe('newHeads')` calls return `no new subscription can be created`. -## Supported Subscription Types +## Subscription Types -### newHeads - -Streams new block headers as they're produced (~400ms on Sei). - -```typescript copy -// Function: eth_subscribe('newHeads') -import WebSocket from 'ws'; - -const ws = new WebSocket('wss://evm-rpc.sei-apis.com'); - -ws.on('open', () => { - ws.send( - JSON.stringify({ - jsonrpc: '2.0', - id: 1, - method: 'eth_subscribe', - params: ['newHeads'] - }) - ); -}); - -ws.on('message', (data) => { - const msg = JSON.parse(data.toString()); - if (msg.method === 'eth_subscription') { - console.log('New block:', msg.params.result.number); - } -}); -``` - -### logs - -Streams logs matching filter criteria (address, topics). - -```typescript copy -// Function: eth_subscribe('logs', filter) -ws.send( - JSON.stringify({ - jsonrpc: '2.0', - id: 2, - method: 'eth_subscribe', - params: [ - 'logs', - { - address: '0xYourContract...', - topics: ['0xYourEventSignature...'] - } - ] - }) -); -``` - -### newPendingTransactions - -Streams transaction hashes as they enter the mempool. - -```typescript copy -// Function: eth_subscribe('newPendingTransactions') -ws.send( - JSON.stringify({ - jsonrpc: '2.0', - id: 3, - method: 'eth_subscribe', - params: ['newPendingTransactions'] - }) -); -``` + ## Connection Management -**Heartbeats:** - -- Send ping frames every 30–60 seconds -- Missing pongs indicate stale connection; reconnect immediately - -**Backoff on reconnect:** - -- Use exponential backoff with jitter (start 1s, max 30s) -- After reconnect, fetch latest block via HTTP before resubscribing to establish checkpoint - -**Subscription cleanup:** - -- Always call `eth_unsubscribe` before closing connection -- Node auto-cleans subscriptions on disconnect, but explicit cleanup is best practice + ## Replay & Gap Handling From 1f1c6daa2310cccdebcb2ddccc59c6821bfe6709 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 18:10:16 -0400 Subject: [PATCH 15/25] link consensus docs --- app/not-found.tsx | 8 +-- content/evm/rpc-consensus-overview.mdx | 17 +++-- content/evm/rpc-regression-playbook.mdx | 87 ++++++++++++++----------- content/learn/twin-turbo-consensus.mdx | 8 +++ mdx-components.js | 4 +- 5 files changed, 73 insertions(+), 51 deletions(-) diff --git a/app/not-found.tsx b/app/not-found.tsx index 1ec09fed..b97c59f4 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,7 +1,3 @@ -'use client'; - -import Link from 'next/link'; - export default function NotFound() { return (
@@ -9,9 +5,9 @@ export default function NotFound() {

The page you're looking for doesn't exist. Check the URL or return to the documentation home.

- + Back to docs - +
); } diff --git a/content/evm/rpc-consensus-overview.mdx b/content/evm/rpc-consensus-overview.mdx index cba8a4ec..8e2c7109 100644 --- a/content/evm/rpc-consensus-overview.mdx +++ b/content/evm/rpc-consensus-overview.mdx @@ -53,17 +53,17 @@ Sei uses a Tendermint-based consensus engine tuned for high-throughput EVM workl items={[ { title: 'Recheck discipline', - description: 'Leave `mempool.recheck` = false for production. Re-enabling increases latency; flip on briefly only after app upgrades to purge invalid txs.', - href: '/node/technical-reference' + description: 'Leave `mempool.recheck` = false in production. Toggle briefly only after app upgrades to purge invalid txs.', + href: '/node/technical-reference#consensus' }, { title: 'Metric coverage', - description: 'Alert on `consensus_height` flatlines and `mempool_cache_size` plateaus. Pair with log scrapes for duplicate tx warnings.', + description: 'Alert on `consensus_height` stalls and `mempool_cache_size` plateaus; pair with duplicate-tx warnings in logs.', href: '/node/troubleshooting' }, { title: 'Recovery playbook', - description: 'Prefer snapshots or state sync when desync occurs. `seid unsafe-reset-all` should be a last resort and requires fresh peers.', + description: 'Use snapshots/state sync first. `seid unsafe-reset-all` should be last resort with refreshed peer sets.', href: '/node/node-incident-playbooks' } ]} @@ -97,6 +97,15 @@ Sei uses a Tendermint-based consensus engine tuned for high-throughput EVM workl title: 'RPC Gas Accounting', description: 'How block-level gas totals interact with consensus updates post-`v6.1.11`.', href: '/evm/rpc-gas-accounting' + }, + { + title: 'Twin Turbo Consensus', + description: 'Protocol-level explainer of Sei’s pipelined Tendermint flow for sub-second finality.', + href: '/learn/twin-turbo-consensus' } ]} /> + + + Looking for the protocol architecture view? Read Twin Turbo Consensus. That explainer now links back here for operator-specific configuration notes. + diff --git a/content/evm/rpc-regression-playbook.mdx b/content/evm/rpc-regression-playbook.mdx index 7e679eb9..59640750 100644 --- a/content/evm/rpc-regression-playbook.mdx +++ b/content/evm/rpc-regression-playbook.mdx @@ -53,65 +53,74 @@ Run these checks whenever you upgrade to a new Sei release or patch your RPC nod ## Sample Scripts -
- + ```javascript copy import { JsonRpcProvider } from 'ethers'; -const provider = new JsonRpcProvider('http://localhost:8545'); +const provider = new JsonRpcProvider('https://sei.yournode.example', { +name: 'sei', +chainId: 1329 +}); async function verifyBlockGas(blockTag = 'latest') { - const block = await provider.send('eth_getBlockByNumber', [blockTag, true]); - const receipts = await Promise.all(block.transactions.map((tx) => provider.send('eth_getTransactionReceipt', [tx.hash]))); +const block = await provider.getBlock(blockTag, true); +const receiptGas = await Promise.all( +block.transactions.map((hash) => provider.getTransactionReceipt(hash)) +); - const sum = receipts.reduce((acc, r) => acc + BigInt(r.gasUsed), 0n); - if (sum !== BigInt(block.gasUsed)) { - throw new Error(`Mismatch: block=${block.gasUsed} receipts=${sum.toString(16)}`); - } +const sum = receiptGas.reduce((acc, receipt) => acc + receipt.gasUsed, 0n); + +if (block.gasUsed !== sum) { +throw new Error(`Mismatch: block ${block.gasUsed} vs receipts ${sum}`); +} } verifyBlockGas().then(() => console.log('Gas totals match.')); -``` - -
-
+```` + + ```go copy package main import ( - "context" - "log" + "context" + "fmt" + "log" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethclient" ) func main() { - client, err := ethclient.Dial("ws://localhost:8546") - if err != nil { - log.Fatal(err) - } - defer client.Close() - - headers := make(chan *types.Header) - sub, err := client.SubscribeNewHead(context.Background(), headers) - if err != nil { - log.Fatal(err) - } - - for i := 0; i < 100; i++ { - select { - case err := <-sub.Err(): - log.Fatalf("subscription dropped: %v", err) - case header := <-headers: - log.Printf("new head: %s", header.Hash()) - } - } + client, err := ethclient.Dial("https://sei.yournode.example") + if err != nil { + log.Fatal(err) + } + + block, err := client.BlockByNumber(context.Background(), nil) + if err != nil { + log.Fatal(err) + } + + receipts, err := client.BlockReceipts(context.Background(), block.Hash()) + if err != nil { + log.Fatal(err) + } + + var sum uint64 + for _, receipt := range receipts { + sum += receipt.GasUsed + } + + if block.GasUsed() != sum { + log.Fatalf("Mismatch: block %d vs receipts %d", block.GasUsed(), sum) + } + + fmt.Println("Gas totals match.") } -``` +```` -
+
## Troubleshooting diff --git a/content/learn/twin-turbo-consensus.mdx b/content/learn/twin-turbo-consensus.mdx index 0a72fc72..5ae13cfc 100644 --- a/content/learn/twin-turbo-consensus.mdx +++ b/content/learn/twin-turbo-consensus.mdx @@ -1,13 +1,21 @@ +import { Callout } from 'nextra/components'; + --- + title: "Twin Turbo Consensus: Sei's High-Speed Blockchain Consensus" description: "Explore how Sei's Twin Turbo consensus mechanism achieves higher transaction throughput by separating block building from consensus, with detailed explanations of the protocol's design and benefits." keywords: ["twin turbo consensus", "high-speed blockchain", "transaction throughput", "blockchain performance", "validator coordination", "consensus mechanism"] + --- # Twin Turbo Consensus ## Introduction + + Need operator tuning guidance? See Consensus Deep Dive for cache sizing, vote-extension posture, and troubleshooting. This page focuses on the architectural flow. + + Sei's consensus mechanism, often referred to as Twin Turbo Consensus, represents a suite of optimizations designed to achieve exceptionally low block finality times, targeting approximately 400 milliseconds. This rapid finality is not achieved through a completely novel consensus algorithm but rather through significant enhancements to the underlying Tendermint Byzantine Fault Tolerant (BFT) consensus engine, aggressive configuration tuning, and tight integration with Sei's parallel execution layer and SeiDB storage system. The goal is to provide near-instant transaction confirmation, enabling a new class of high-performance decentralized applications, particularly those built for the EVM. ## Core Concept: Pipelined & Parallelized Consensus diff --git a/mdx-components.js b/mdx-components.js index 3d7fa174..30578950 100644 --- a/mdx-components.js +++ b/mdx-components.js @@ -48,9 +48,9 @@ export function CardGrid({ items = [] }) { href={it.href} className='group rounded-lg border border-neutral-200 bg-white/90 p-4 shadow-sm transition hover:-translate-y-0.5 hover:border-neutral-300 hover:shadow-md dark:border-neutral-800 dark:bg-neutral-900/70 dark:hover:border-neutral-700'>
- {it.title} + {renderInlineCode(it.title)}
-
{it.description}
+
{renderInlineCode(it.description)}
))} From 19dc429aad9eb58f52b8d480fd8cc593d796eea2 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 18:18:19 -0400 Subject: [PATCH 16/25] expand consensus deep dive --- content/evm/rpc-consensus-overview.mdx | 54 +++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/content/evm/rpc-consensus-overview.mdx b/content/evm/rpc-consensus-overview.mdx index 8e2c7109..8cf03e76 100644 --- a/content/evm/rpc-consensus-overview.mdx +++ b/content/evm/rpc-consensus-overview.mdx @@ -5,7 +5,7 @@ keywords: ['consensus', 'mempool', 'vote extensions', 'sei tendermint'] --- import { Callout } from 'nextra/components'; -import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; +import { CardGrid, KeyValueTable, TroubleshootingTable } from '../../mdx-components'; # Consensus Deep Dive @@ -75,7 +75,57 @@ Sei uses a Tendermint-based consensus engine tuned for high-throughput EVM workl rows={[ ['Duplicate tx errors after upgrade', 'Cache size too small for current traffic.', 'Increase `mempool.cache_size` in `config.toml` and restart the node.'], ['Unexpected vote extension logs', 'Experimental configuration toggled vote extensions.', 'Revert the change; once enabled the protocol expects extension payloads from validators.'], - ['Consensus halt around upgrade height', 'Nodes running mixed binaries or stale state.', 'Ensure all validators run the same release and execute a coordinated upgrade with state sync ready.'] + ['Consensus halt around upgrade height', 'Nodes running mixed binaries or stale state.', 'Ensure all validators run the same release, apply the same config, and coordinate a state-sync fallback.'], + ['Height stuck with high round numbers', 'Proposer starvation or peers throttled.', 'Check `consensus_round` metric, rebalance peer set, and ensure fast proposers have adequate upstream bandwidth.'], + ['Block time drifts >1s', 'Timeout overrides reverted to defaults or hardware saturated.', 'Audit `timeout_commit`/`timeout_propose` values and host load; reapply Sei defaults and scale hardware if needed.'] + ]} +/> + +## Pipeline Overview + + + +## Configuration Reference + + + +## Telemetry & SLOs + +0.6 s for 5 minutes.'], + ['Round health', '`consensus_round` should oscillate between 0–2; sustained >3 indicates proposal issues.'], + ['Duplicate cache utilisation', '`seimempool_cache_used` / `seimempool_cache_capacity` < 0.9 outside bursts.'], + ['Peer availability', '`p2p_peer_count` ≥ expected sentries; drops correlate with proposer timeouts.'], + ['EVM finality', 'Cross-check `eth_blockNumber` vs `seid status` height; drift >1 block signals RPC lag.'] + ]} +/> + +## Upgrade Validation Checklist + + From 0328ddda267393e56117759b8a58976597a7141b Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 20:28:19 -0400 Subject: [PATCH 17/25] add prioritizer doc --- content/evm/_meta.js | 1 + content/evm/changelog.mdx | 13 +-- content/evm/rpc-prioritizer.mdx | 185 ++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 content/evm/rpc-prioritizer.mdx diff --git a/content/evm/_meta.js b/content/evm/_meta.js index 813b05a3..654ec9b9 100644 --- a/content/evm/_meta.js +++ b/content/evm/_meta.js @@ -68,6 +68,7 @@ export default { reference: 'RPC Reference', 'rpc-gas-accounting': 'Gas Accounting', 'rpc-gas-reference': 'Gas Reference', + 'rpc-prioritizer': 'Tx Prioritizer', 'rpc-regression-playbook': 'Regression Coverage', 'rpc-panic-faq': 'Panic Handling FAQ', 'rpc-websockets': 'WebSockets', diff --git a/content/evm/changelog.mdx b/content/evm/changelog.mdx index 9961e26f..379b08b9 100644 --- a/content/evm/changelog.mdx +++ b/content/evm/changelog.mdx @@ -33,9 +33,10 @@ Want to be notified of new releases? **Watch** the [sei-protocol/sei-chain](http ## Release-to-Doc Mapping (v6.1.11+) -| Release | Code Reference | Documentation Touchpoints | -| :------------------------- | :----------------------------- | :------------------------------------------------------------------------------------- | -| `v6.1.11` | `sei-chain@1efdec1eb` | `rpc-gas-accounting`, `rpc-gas-reference`, `rpc-synthetic-transactions` | -| `v1.15.7-sei-6` | go-ethereum panic wrapper | `rpc-panic-faq`, `rpc-regression-playbook`, `rpc-tracer-guide` | -| `v1.15.7-sei-7` | go-ethereum frame length guard | `rpc-tracer-guide`, `rpc-regression-playbook` | -| `sei-tendermint@02c9462f1` | Duplicate tx cache fix | `rpc-consensus-overview`, `validator-operations-playbook`, `/node/technical-reference` | +| Release | Code Reference | Documentation Touchpoints | +| :------------------------- | :----------------------------- | :-------------------------------------------------------------------------------------- | +| `v6.1.11` | `sei-chain@1efdec1eb` | `rpc-gas-accounting`, `rpc-gas-reference`, `rpc-synthetic-transactions` | +| `v1.15.7-sei-6` | go-ethereum panic wrapper | `rpc-panic-faq`, `rpc-regression-playbook`, `rpc-tracer-guide` | +| `v1.15.7-sei-7` | go-ethereum frame length guard | `rpc-tracer-guide`, `rpc-regression-playbook` | +| `sei-tendermint@02c9462f1` | Duplicate tx cache fix | `rpc-consensus-overview`, `validator-operations-playbook`, `/node/technical-reference` | +| `v6.2.0` | Tx prioritizer & ABCI hint | `rpc-prioritizer`, `rpc-consensus-overview`, `node-incident-playbooks#mempool-priority` | diff --git a/content/evm/rpc-prioritizer.mdx b/content/evm/rpc-prioritizer.mdx new file mode 100644 index 00000000..b8a389c5 --- /dev/null +++ b/content/evm/rpc-prioritizer.mdx @@ -0,0 +1,185 @@ +--- +title: 'Tx Prioritizer' +description: 'How Sei ranks transactions pre-CheckTx, how the ABCI hint works, and what operators monitor.' +keywords: ['tx prioritizer', 'mempool', 'sei tendermint', 'priority hints'] +--- + +import { CardGrid, KeyValueTable, TroubleshootingTable } from '../../mdx-components'; + +# Transaction Prioritizer (v6.2.0+) + +Sei `v6.2.0` (commit [`6a5c937f`](https://github.com/sei-protocol/sei-chain/commit/6a5c937fa9737b1e3aa2288b9349d5a9f51c9d79)) ships a dedicated transaction prioritizer. Rather than scattering priority tweaks across Ante handlers, the app now answers a focused ABCI call—`GetTxPriorityHint`—whenever Tendermint’s mempool nears capacity. This page nails down the priority tiers, the knobs exposed in `sei-chain` and `sei-tendermint@c6c5a8f3`, and the checks that prove the system is working. + +
+
+
Surface
+
`abci.GetTxPriorityHint`
+

Fires after utilisation exceeds `drop-utilisation-threshold` and returns the hint Tendermint uses to admit or reject the tx.

+ + View `internal/mempool/mempool.go` + +
+
+
Surface
+
`SeiTxPrioritizer`
+

Stateless Cosmos/EVM prioritizer that inspects the tx, validates fees, and emits an `int64` hint without touching state.

+ + View `app/prioritizer.go` + +
+
+
Tiering
+

+ OraclePriority > EVMAssociatePriority > fee-derived Cosmos/EVM priority (capped at MaxPriority) keeps mission-critical flows at the front. +

+ + View `app/prioritizer.go` + +
+
+
Version guard
+

Enabled by default on `v6.2.0+`; legacy binaries ignore the hook, so mixed-version networks still progress.

+ + View `app/app.go` + +
+
+ +## How the Hint Is Calculated + +
+
+
`OraclePriority`
+

+ Oracle votes (MsgAggregateExchangeRateVote) ride with math.MaxInt64 - 100, guaranteeing feeders clear the mempool immediately. +

+ + View `app/prioritizer.go` + +
+
+
`EVMAssociatePriority`
+

+ Unassociated evm_associate calls bypass congestion so UX wallets can link addresses without fee gymnastics. +

+ + View `app/prioritizer.go` + +
+
+
Gas price normalisation
+

+ Regular EVM traffic uses priority = gasPrice / priority_normalizer and clamps to MaxPriority so outliers can’t blow up the scale. +

+ + View `app/prioritizer.go` + +
+
+
Cosmos fee fallback
+

+ SDK txs reuse GetTxPriority, so whitelisted fee denoms and gas estimates map into the same priority lane. +

+ + View `app/prioritizer.go` + +
+
+ +The prioritizer is intentionally pure: it decodes the tx, validates sane fee caps, probes association status, and returns an `int64`. Panics are caught and logged, and the mempool falls back to priority `0` so attackers cannot halt CheckTx. + +## From Hint to Eviction + +When Tendermint’s mempool utilisation crosses `drop-utilisation-threshold`, the reactor calls `GetTxPriorityHint` **before** deciding whether to drop low-priority transactions. A new reservoir sampler tracks recent hints so the node compares incoming priority against a moving cutoff. + + + +```toml copy +[mempool] +size = 5000 +drop-utilisation-threshold = 0.80 # start hinting once 80% full +drop-priority-threshold = 0.20 # shed bottom 20% priority +drop-priority-reservoir-size = 8192 # tune for memory vs accuracy +``` + +With these values, once the mempool is ≥80% utilised, any new tx with priority below the sampled 20th percentile is rejected with `priority not high enough for mempool`. + +## Operator Checklist + +
+
+
Confirm hints flow
+

`seid debug mempool-stats` should show `priority_cutoff` moving under sustained load; if it’s flat, hints aren’t being sampled.

+
+
+
Grafana panel
+

Alert on `CheckTxMetDropUtilisationThreshold` and `CheckTxDroppedByPriorityHint` to spot accidental throttling.

+
+
+
Normalizer sanity
+

`sei q evm params priority-normalizer` must stay positive; lock it down in Terraform/Ansible defaults.

+
+
+
RPC parity
+

Compare `eth_pendingTransactions` with `seid debug mempool-stats`; a growing gap usually means clients are retrying after drops.

+
+
+ +### CLI validation + +```bash copy +# Inspect priority reservoir stats +seid debug mempool-stats | jq '.priority' + +# Query EVM priority normalizer (decides gasPrice → hint scaling) +seid query evm params | jq -r '.params.priority_normalizer' + +# Force an associate tx hint +seid tx evm associate --from wallet --evm-address --gas-prices 1usei --gas 120000 --simulate +``` + +## Developer Considerations + +- **SDK integration:** Clients may receive `priority not high enough for mempool` when bursting the chain. Surface this to end users with context (e.g., “bump gas price or retry later”). +- **Fee markets:** EIP-1559 style tips (`gasTipCap`) remain valid; the prioritizer only checks tip ≥ 0 and `gasFeeCap` ≥ base fee & minimum fee. +- **Panic-proof:** The prioritizer recovers from panics and logs `tx prioritizer panicked. Falling back on no priority`. Instrument alerting on this string. +- **Testing:** Unit suite (`app/prioritizer_test.go`) covers oracle, zero-fee, and multi-denom cases. Extend tests when adding new high-priority msg types. + +## Troubleshooting + + + +## Related Material + + From 4b9a2e21e36af5da15a2eff9fef14b7b100af5d6 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 20:28:49 -0400 Subject: [PATCH 18/25] tune consensus deep dive --- content/evm/rpc-consensus-overview.mdx | 157 ++++++++++--------------- 1 file changed, 64 insertions(+), 93 deletions(-) diff --git a/content/evm/rpc-consensus-overview.mdx b/content/evm/rpc-consensus-overview.mdx index 8cf03e76..65ca0408 100644 --- a/content/evm/rpc-consensus-overview.mdx +++ b/content/evm/rpc-consensus-overview.mdx @@ -4,130 +4,105 @@ description: 'Operator-oriented reference for Sei consensus, mempool, and vote e keywords: ['consensus', 'mempool', 'vote extensions', 'sei tendermint'] --- -import { Callout } from 'nextra/components'; import { CardGrid, KeyValueTable, TroubleshootingTable } from '../../mdx-components'; # Consensus Deep Dive -Sei uses a Tendermint-based consensus engine tuned for high-throughput EVM workloads. This guide summarises the latest behavioural changes so validator operators and infrastructure teams can keep nodes healthy. +Sei rides Tendermint BFT, but the chain’s sub-second finality comes from a carefully engineered pipeline that overlaps proposal, execution, and commit work. This page is the operator-focused playbook that connects the code paths (`sei-tendermint@02c9462f1`), config surface, and on-call runbooks that keep the pipeline healthy. -Covers `sei-tendermint@02c9462f1` (duplicate transaction cache sizing) and the current vote-extension posture. +## What’s Unique About Sei’s Tendermint -## Key Concepts +- **Heavily pipelined heights.** Proposal assembly, OCC metadata generation, and duplicate-cache pruning all happen before height `H` even begins (`app/abci/prepare.go`). Validators already have warmed state when the proposer gossips the block. +- **Parallel execution during voting.** As soon as the proposal hits, `DeliverTxBatch` fans transactions into the optimistic concurrency controller (OCC) while Tendermint runs through `EnterPrevote` → `EnterPrecommit` (`consensus/state.go`). Execution and voting finish almost together. +- **Deterministic duplicate cache.** The fix in `sei-tendermint@02c9462f1` enforces `mempool.cache_size` strictly; stale entries are evicted whenever a tx is included or explicitly removed (`mempool/tx_cache.go`). That’s what stops high-throughput validators from repeatedly reprocessing spam. +- **Priority-aware eviction.** On `v6.2.0+`, `GetTxPriorityHint` feeds a reservoir sampler so low-priority spam is rejected once utilisation crosses the configured threshold (`internal/mempool/mempool.go`). +- **Vote extensions disabled on public networks.** The plumbing exists, but `types/params.go` prevents toggling back off once enabled. Treat the feature flag as a one-way door reserved for future releases. - - -`sei-tendermint@02c9462f1` corrected duplicate transaction cache accounting, and the public networks continue to run with vote extensions disabled. +## Height H: Timeline With Code Anchors -## Duplicate Transaction Cache +| Stage | What Happens | Code Path / Signals | +| :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------ | +| Preflight (`H-1` still finalising) | `GenerateEstimatedWritesets` maps conflicting keys, SeiDB caches reads, duplicate cache prunes stale entries, new tx hints sampled for reservoir. | `app/abci/prepare.go`, logs `prepare height=`, `mempool/reservoir.go` | +| Proposal build (`EnterPropose`) | Proposer calls `CreateProposalBlock` to pack txs + OCC metadata. | `consensus/state.go:enterPropose`, `Consensus` logs `created proposal` | +| Broadcast & prevote | Proposal gossips, validators push `Prevote` while launching `DeliverTxBatch`. | `consensus/state.go:addVote`, metric `consensus_round` | +| Parallel execution | OCC runs `ProcessTXsWithOCC` workers, buffering writes in `CacheMultiStore`. | `app/abci/deliver_tx.go`, log `batch executed` | +| Precommit & finalise | When 2/3 precommits arrive, buffered writes flush to SeiDB, Tendermint schedules height `H+1`. | `consensus/state.go:finalizeCommit`, metrics `consensus_block_interval_seconds` | - 90% for > 2 minutes.'] - ]} -/> +> Tip: `seid debug consensus-state` visualises these transitions live; pair it with `journalctl -u seid | grep consensus` during incident drills. -## Vote Extensions +## Operator Knobs Worth Guarding -## Operational Best Practices +### Canonical config snippet - +```toml copy +[consensus] +timeout_commit = "400ms" +timeout_propose = "200ms" +timeout_propose_delta = "100ms" +create_empty_blocks = true +create_empty_blocks_interval = "0s" -## Troubleshooting +[mempool] +cache_size = 10000 +``` -1s', 'Timeout overrides reverted to defaults or hardware saturated.', 'Audit `timeout_commit`/`timeout_propose` values and host load; reapply Sei defaults and scale hardware if needed.'] - ]} -/> +Templatise these defaults (Ansible, Terraform, Helm) so they don’t regress during infra churn. If you enable priority-based drops, record the tuned values alongside this config and link back to the [Tx Prioritizer](/evm/rpc-prioritizer) guide. -## Pipeline Overview +## Observability That Proves the Pipeline Works 0.6 s for 5 minutes.'], + ['Round health', '`consensus_round` should oscillate 0–2. Sustained >3 ⇒ proposer issues or gossip lag.'], + ['Duplicate cache utilisation', '`seimempool_cache_used / capacity` < 0.9 once warmed. High values precede duplicate rejections.'], + ['EVM parity', 'Track `eth_blockNumber` vs `seid status`. Drift >1 block means RPC or indexer is lagging.'] ]} /> -## Configuration Reference +**Quick CLI checks** - +```bash copy +# Live consensus snapshot +seid debug consensus-state -## Telemetry & SLOs +# Confirm block cadence (aggregate diff should hover around 0.4s) +seid status | jq -r '.SyncInfo.latest_block_time' | tail -n 20 | \ + ruby -e 'times=STDIN.read.split.map { Time.parse(_1) }; puts times.each_cons(2).map { |a,b| b-a }' -0.6 s for 5 minutes.'], - ['Round health', '`consensus_round` should oscillate between 0–2; sustained >3 indicates proposal issues.'], - ['Duplicate cache utilisation', '`seimempool_cache_used` / `seimempool_cache_capacity` < 0.9 outside bursts.'], - ['Peer availability', '`p2p_peer_count` ≥ expected sentries; drops correlate with proposer timeouts.'], - ['EVM finality', 'Cross-check `eth_blockNumber` vs `seid status` height; drift >1 block signals RPC lag.'] + ['Duplicate tx errors after upgrade', 'Cache size too small for current traffic.', 'Increase `mempool.cache_size` in `config.toml` and restart during a low-load window.'], + ['`priority not high enough` floods logs', 'Hint thresholds too strict for current fee environment.', 'Revisit `drop-utilisation-threshold` / `drop-priority-threshold` following the Tx Prioritizer checklist.'], + ['Unexpected vote extension logs', 'Experimental flag toggled vote extensions.', 'Revert the change; once enabled the protocol expects extension payloads.'], + ['Height stuck with high round numbers', 'Proposer starvation or peers throttled.', 'Check `consensus_round`, rebalance peer set, ensure sentries have bandwidth; consider raising `timeout_propose_delta` slightly.'], + ['Block time drifts >1 s', 'Timeout overrides reverted or hardware saturated.', 'Audit config, reapply Sei defaults, scale CPU/IO where OCC workers run.'], + ['Different binaries gossiping', 'Mixed `sei-tendermint` versions post-upgrade.', 'Verify `seid version --long` across validators; coordinate rollback or full redeploy.'] ]} /> ## Upgrade Validation Checklist - +1. **Consensus state** – `seid debug consensus-state` shows proposers rotating and rounds moving rapidly (no long stalls on a single height). +2. **Cadence sample** – pull 20 recent block times and confirm deltas hover around `0.4s`. +3. **Cache enforcement** – after load resumes, `seid debug mempool-stats` reports the configured cache cap (no overflow past `mempool.cache_size`). +4. **RPC parity** – `eth_getBlockByNumber(..., true)` height matches Tendermint and receipts sum to block `gasUsed`. +5. **Observability soak** – Grafana alerts for latency, rounds, and cache stay green for 30 minutes; store the dashboard snapshot alongside the change ticket. ## Related Material @@ -155,7 +130,3 @@ Sei uses a Tendermint-based consensus engine tuned for high-throughput EVM workl } ]} /> - - - Looking for the protocol architecture view? Read Twin Turbo Consensus. That explainer now links back here for operator-specific configuration notes. - From 963aa49452ab5b84cc444c975b02acf160550324 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 20:39:22 -0400 Subject: [PATCH 19/25] fix sidebar entry --- content/evm/_meta.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/evm/_meta.js b/content/evm/_meta.js index 9f983893..4bb50768 100644 --- a/content/evm/_meta.js +++ b/content/evm/_meta.js @@ -16,7 +16,6 @@ export default { 'installing-seid-cli': 'Installing seid CLI', 'querying-the-evm': 'Querying the EVM', 'transactions-with-seid': 'Transactions with seid', - 'best-practices': 'Best Practices', '-- Frontend Development': { type: 'separator', @@ -78,7 +77,7 @@ export default { 'rpc-gas-accounting': 'Gas Accounting', 'rpc-gas-reference': 'Gas Reference', 'rpc-prioritizer': 'Tx Prioritizer', - 'rpc-regression-playbook': 'Regression Coverage', + 'rpc-regression-playbook': 'Regression Coverage', 'rpc-panic-faq': 'Panic Handling FAQ', 'rpc-websockets': 'WebSockets', From a882df8400363ed13269cc82b459da0ec462776c Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 20:49:52 -0400 Subject: [PATCH 20/25] reorder evm sidebar --- content/evm/_meta.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/content/evm/_meta.js b/content/evm/_meta.js index 4bb50768..6e1f4381 100644 --- a/content/evm/_meta.js +++ b/content/evm/_meta.js @@ -49,6 +49,13 @@ export default { 'debugging-contracts': 'Debugging', 'evm-verify-contracts': 'Verify Contracts', + '-- Core Concepts': { + type: 'separator', + title: 'Core Concepts' + }, + transactions: 'Transactions', + 'rpc-consensus-overview': 'Consensus & Mempool', + '-- Precompiles': { type: 'separator', title: 'Precompiles' @@ -97,17 +104,15 @@ export default { 'indexing-best-practices': 'Best Practices', 'indexer-providers': 'Indexer Providers', - '-- Advanced': { + '-- Interop & Advanced': { type: 'separator', - title: 'Advanced' + title: 'Interop & Advanced' }, 'pointers-deep-dive': 'Pointers', - 'rpc-consensus-overview': 'Consensus Deep Dive', - 'rpc-synthetic-transactions': 'Synthetic Transactions', - 'rpc-tracer-guide': 'Tracer Authoring', - transactions: 'Transactions', 'ibc-protocol': 'IBC on EVM', 'cosmwasm-precompiles': 'CosmWasm Precompiles', + 'rpc-synthetic-transactions': 'Synthetic Transactions', + 'rpc-tracer-guide': 'Tracer Authoring', '-- Ecosystem': { type: 'separator', From 5a77c0f438e55e108bcbc6f9002cfca21b2013e2 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 20:59:55 -0400 Subject: [PATCH 21/25] streamline sidebar sections --- content/evm/_meta.js | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/content/evm/_meta.js b/content/evm/_meta.js index 6e1f4381..a23e97e1 100644 --- a/content/evm/_meta.js +++ b/content/evm/_meta.js @@ -8,6 +8,7 @@ export default { }, networks: 'Network Information', 'differences-with-ethereum': 'Divergence from Ethereum', + tokens: 'View Tokens', '-- seid CLI': { type: 'separator', @@ -55,6 +56,7 @@ export default { }, transactions: 'Transactions', 'rpc-consensus-overview': 'Consensus & Mempool', + reference: 'RPC Reference', '-- Precompiles': { type: 'separator', @@ -76,43 +78,36 @@ export default { 'precompile-json': 'JSON', 'precompile-P256': 'P256', - '-- RPC': { + '-- Monitoring & Indexing': { type: 'separator', - title: 'RPC' + title: 'Monitoring & Indexing' + }, + 'indexing-best-practices': 'Indexing Best Practices', + 'indexer-providers': 'Indexer Providers', + 'tracing-overview': 'Tracing Overview', + 'tracing-playbook': 'Tracing Playbook', + 'tracing-javascript-tracers': 'Tracer Authoring', + 'tracing-troubleshooting': 'Tracing Troubleshooting', + + '-- RPC Diagnostics': { + type: 'separator', + title: 'RPC Diagnostics' }, - reference: 'RPC Reference', 'rpc-gas-accounting': 'Gas Accounting', 'rpc-gas-reference': 'Gas Reference', 'rpc-prioritizer': 'Tx Prioritizer', 'rpc-regression-playbook': 'Regression Coverage', 'rpc-panic-faq': 'Panic Handling FAQ', 'rpc-websockets': 'WebSockets', + 'rpc-synthetic-transactions': 'Synthetic Transactions', - '-- Tracing': { - type: 'separator', - title: 'Tracing' - }, - 'tracing-overview': 'Overview', - 'tracing-playbook': 'Playbook', - 'tracing-javascript-tracers': 'JavaScript Tracers', - 'tracing-troubleshooting': 'Troubleshooting', - - '-- Indexing': { - type: 'separator', - title: 'Indexing' - }, - 'indexing-best-practices': 'Best Practices', - 'indexer-providers': 'Indexer Providers', - - '-- Interop & Advanced': { + '-- Interoperability': { type: 'separator', - title: 'Interop & Advanced' + title: 'Interoperability' }, - 'pointers-deep-dive': 'Pointers', + 'pointers-deep-dive': 'Pointers Deep Dive', 'ibc-protocol': 'IBC on EVM', 'cosmwasm-precompiles': 'CosmWasm Precompiles', - 'rpc-synthetic-transactions': 'Synthetic Transactions', - 'rpc-tracer-guide': 'Tracer Authoring', '-- Ecosystem': { type: 'separator', @@ -129,6 +124,5 @@ export default { type: 'separator', title: 'Meta' }, - tokens: 'View Tokens', changelog: 'Changelog' }; From b5d6ba6408f661aad096a4842151c710263b2c6b Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 21:04:44 -0400 Subject: [PATCH 22/25] neutralize prioritizer versioning --- content/evm/rpc-consensus-overview.mdx | 2 +- content/evm/rpc-prioritizer.mdx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/content/evm/rpc-consensus-overview.mdx b/content/evm/rpc-consensus-overview.mdx index 65ca0408..35a00d09 100644 --- a/content/evm/rpc-consensus-overview.mdx +++ b/content/evm/rpc-consensus-overview.mdx @@ -15,7 +15,7 @@ Sei rides Tendermint BFT, but the chain’s sub-second finality comes from a car - **Heavily pipelined heights.** Proposal assembly, OCC metadata generation, and duplicate-cache pruning all happen before height `H` even begins (`app/abci/prepare.go`). Validators already have warmed state when the proposer gossips the block. - **Parallel execution during voting.** As soon as the proposal hits, `DeliverTxBatch` fans transactions into the optimistic concurrency controller (OCC) while Tendermint runs through `EnterPrevote` → `EnterPrecommit` (`consensus/state.go`). Execution and voting finish almost together. - **Deterministic duplicate cache.** The fix in `sei-tendermint@02c9462f1` enforces `mempool.cache_size` strictly; stale entries are evicted whenever a tx is included or explicitly removed (`mempool/tx_cache.go`). That’s what stops high-throughput validators from repeatedly reprocessing spam. -- **Priority-aware eviction.** On `v6.2.0+`, `GetTxPriorityHint` feeds a reservoir sampler so low-priority spam is rejected once utilisation crosses the configured threshold (`internal/mempool/mempool.go`). +- **Priority-aware eviction.** With the new prioritizer enabled, `GetTxPriorityHint` feeds a reservoir sampler so low-priority spam is rejected once utilisation crosses the configured threshold (`internal/mempool/mempool.go`). - **Vote extensions disabled on public networks.** The plumbing exists, but `types/params.go` prevents toggling back off once enabled. Treat the feature flag as a one-way door reserved for future releases. ## Height H: Timeline With Code Anchors diff --git a/content/evm/rpc-prioritizer.mdx b/content/evm/rpc-prioritizer.mdx index b8a389c5..c39f9d41 100644 --- a/content/evm/rpc-prioritizer.mdx +++ b/content/evm/rpc-prioritizer.mdx @@ -6,9 +6,9 @@ keywords: ['tx prioritizer', 'mempool', 'sei tendermint', 'priority hints'] import { CardGrid, KeyValueTable, TroubleshootingTable } from '../../mdx-components'; -# Transaction Prioritizer (v6.2.0+) +# Transaction Prioritizer -Sei `v6.2.0` (commit [`6a5c937f`](https://github.com/sei-protocol/sei-chain/commit/6a5c937fa9737b1e3aa2288b9349d5a9f51c9d79)) ships a dedicated transaction prioritizer. Rather than scattering priority tweaks across Ante handlers, the app now answers a focused ABCI call—`GetTxPriorityHint`—whenever Tendermint’s mempool nears capacity. This page nails down the priority tiers, the knobs exposed in `sei-chain` and `sei-tendermint@c6c5a8f3`, and the checks that prove the system is working. +Sei now ships a dedicated transaction prioritizer. Rather than scattering priority tweaks across Ante handlers, the app answers a focused ABCI call—`GetTxPriorityHint`—whenever Tendermint’s mempool nears capacity. This page nails down the priority tiers, the knobs exposed in `sei-chain` and `sei-tendermint@c6c5a8f3`, and the checks that prove the system is working.
@@ -38,7 +38,7 @@ Sei `v6.2.0` (commit [`6a5c937f`](https://github.com/sei-protocol/sei-chain/comm
Version guard
-

Enabled by default on `v6.2.0+`; legacy binaries ignore the hook, so mixed-version networks still progress.

+

Ships enabled in current releases; older binaries ignore the hook, so mixed-version networks continue to make progress.

View `app/app.go` @@ -158,7 +158,7 @@ seid tx evm associate --from wallet --evm-address --gas-prices 1usei --ga rows={[ ['`priority not high enough for mempool` on legitimate txs', 'Reservoir cutoff too aggressive or priority normalizer mis-set.', 'Lower `drop-priority-threshold`, raise normalizer, or temporarily disable threshold while load stabilises.'], ['Hints always 0', '`priority_normalizer` zeroed or prioritizer panic fallback triggered.', 'Verify `sei query evm params`; check logs for `tx prioritizer panicked`.'], - ['Oracle votes delayed', 'Oracle msgs lost special tier (regression).', 'Confirm `OraclePriority` constant, ensure app build is ≥ `v6.2.0`.'], + ['Oracle votes delayed', 'Oracle msgs lost special tier (regression).', 'Confirm `OraclePriority` constant and verify all nodes run a prioritizer-enabled release.'], ['Assoc tx rejected with “already associated”', 'Wallet already linked so prioritizer returns error.', 'Surface clearer UX; instruct signer to send standard tx instead.'] ]} /> From c009ef74909f69b972eff38eaaab2a063f63b8aa Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Wed, 1 Oct 2025 21:54:52 -0400 Subject: [PATCH 23/25] refresh pointer and tracer docs --- content/evm/pointers-deep-dive.mdx | 92 +++++++++++++++++++++++++++++- content/evm/rpc-tracer-guide.mdx | 8 +-- 2 files changed, 94 insertions(+), 6 deletions(-) diff --git a/content/evm/pointers-deep-dive.mdx b/content/evm/pointers-deep-dive.mdx index c2b4dbf5..5b0bf30e 100644 --- a/content/evm/pointers-deep-dive.mdx +++ b/content/evm/pointers-deep-dive.mdx @@ -5,14 +5,38 @@ keywords: ['pointer', 'pointerview', 'cw20', 'erc1155', 'erc721', 'interop'] --- import { Callout } from 'nextra/components'; +import { CardGrid, KeyValueTable, TroubleshootingTable } from '../../mdx-components'; # Pointers Deep Dive -Patterns for mapping CW assets/contracts to EVM via Pointer and querying via PointerView. +The Pointer and PointerView precompiles bridge Cosmos assets and identities into Sei’s EVM. This guide covers the write/read split, typical mapping patterns, and operational safeguards before you dive into the individual ABI docs. + +## When to use Pointer vs PointerView + + ## Mapping patterns -Use Pointer for ownership/actions; use PointerView for safe queries. +Use Pointer for ownership/actions; use PointerView for safe queries. Contracts should never rely on off-chain caches for ownership truth. ## Identifiers @@ -26,4 +50,68 @@ Patterns for mapping CW assets/contracts to EVM via Pointer and querying via Poi ## Examples +### Register a CW20 token for EVM access + +```solidity copy +pragma solidity ^0.8.20; + +interface IPointer { + function addCW20Pointer(string calldata cwToken, address evmToken) external; +} + +contract CW20Registrar { + IPointer public immutable pointer; + + constructor(address pointerAddr) { + pointer = IPointer(pointerAddr); + } + + function register(string calldata cwToken, address evmToken) external { + pointer.addCW20Pointer(cwToken, evmToken); + } +} +``` + +### Resolve ownership via PointerView + +```solidity copy +pragma solidity ^0.8.20; + +interface IPointerView { + function getCW721Pointer(string calldata cwCollection) external view returns (address); +} + +contract OwnershipLens { + IPointerView public immutable viewContract; + + constructor(address pointerView) { + viewContract = IPointerView(pointerView); + } + + function ownerContract(string calldata cwCollection) external view returns (address) { + return viewContract.getCW721Pointer(cwCollection); + } +} +``` + +## Operational checklist + + + ## Troubleshooting + + diff --git a/content/evm/rpc-tracer-guide.mdx b/content/evm/rpc-tracer-guide.mdx index f5185f32..9dec24aa 100644 --- a/content/evm/rpc-tracer-guide.mdx +++ b/content/evm/rpc-tracer-guide.mdx @@ -9,7 +9,7 @@ import { TroubleshootingTable } from '../../mdx-components'; # Custom Tracer Authoring Guide -Sei exposes the standard `debug_trace*` RPC suite and accepts the same JavaScript tracer scripts as upstream go-ethereum. This guide captures the runtime expectations introduced in `v1.15.7-sei-6` and `v1.15.7-sei-7`, plus Sei-specific conventions for panic handling. +Sei exposes the standard `debug_trace*` RPC suite and accepts the same JavaScript tracer scripts as upstream go-ethereum, with a few Sei-specific safety guards and panic surfacing improvements. ## Runtime Expectations @@ -17,12 +17,12 @@ Sei exposes the standard `debug_trace*` RPC suite and accepts the same JavaScrip items={[ { title: 'Frame limits', - description: 'Keep serialized payloads below the guard introduced in `v1.15.7-sei-7`. Chunk large results or stream externally.', + description: 'Tracer payloads should stay comfortably under the enforced frame guard. Chunk large results or stream externally.', href: '#authoring-patterns' }, { title: 'Panic surfacing', - description: 'Errors thrown inside tracers bubble up via `error.data.trace` (since `v1.15.7-sei-6`). Capture them for debugging.', + description: 'Errors thrown inside tracers bubble up via `error.data.trace`. Capture and persist them for debugging.', href: '#troubleshooting' }, { @@ -90,6 +90,6 @@ module.exports = tracer; rows={[ ['panic: runtime error: index out of range', 'Tracer script accesses an array out of bounds.', 'Add guards, catch the error, or ensure the data structure matches opcodes observed.'], ['frame length exceeds limit', 'Tracer returns payload larger than go-ethereum guard.', 'Stream results to storage or return a summarized payload.'], - ['Missing error.data.trace', 'Node still on go-ethereum < `v1.15.7-sei-6` or panic occurs outside tracer scope.', 'Upgrade binaries; ensure panic happens inside tracer so the wrapper can catch it.'] + ['Missing error.data.trace', 'Node running a build without panic surfacing, or panic occurs outside tracer scope.', 'Upgrade to a tracer-enabled release; ensure errors originate inside the tracer so the wrapper can capture them.'] ]} /> From 2387e95508d5fba380496693eca7a26e885d8cb9 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Thu, 2 Oct 2025 17:04:48 -0400 Subject: [PATCH 24/25] polish pointer runbooks --- content/evm/indexer-providers/alchemy.md | 2 +- content/evm/indexer-providers/the-graph.mdx | 11 +++++------ content/evm/optimizing-for-parallelization.mdx | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/content/evm/indexer-providers/alchemy.md b/content/evm/indexer-providers/alchemy.md index 655b48c8..1dd0b3d3 100644 --- a/content/evm/indexer-providers/alchemy.md +++ b/content/evm/indexer-providers/alchemy.md @@ -11,7 +11,7 @@ Subgraphs handle the intricacies of data ingestion, decoding, and database manag ## Why Alchemy Subgraphs? -- **High-performance.** We’ve benchmarked 5x faster data indexing and 2x better block lag than competitors — that’s why we’re trusted by Eigenlayer, GMX, and more. +- **High-performance.** We’ve benchmarked 5x faster data indexing and 2x better block lag than competitors - that’s why we’re trusted by Eigenlayer, GMX, and more. - **No decentralized headaches.** We make payment easy (just a credit card, no staking tokens) and have guaranteed uptime compared to decentralized options. - **Developer tooling.** Access best in class developer tools like error logs, subgraph versioning, indexing performance metrics, and more. - **Easy migration.** If you have existing subgraphs, it takes just 5 minutes to switch over using the same command line tools you’re familiar with. diff --git a/content/evm/indexer-providers/the-graph.mdx b/content/evm/indexer-providers/the-graph.mdx index c02e0334..c9ba2122 100644 --- a/content/evm/indexer-providers/the-graph.mdx +++ b/content/evm/indexer-providers/the-graph.mdx @@ -1,8 +1,9 @@ --- -title: "The Graph" -description: "Build and deploy subgraphs on The Graph to efficiently index and query data from Sei smart contracts, with step-by-step instructions from initialization to production deployment." -keywords: ["the graph", "subgraphs", "blockchain indexing", "graphql", "dapp data"] +title: 'The Graph' +description: 'Build and deploy subgraphs on The Graph to efficiently index and query data from Sei smart contracts, with step-by-step instructions from initialization to production deployment.' +keywords: ['the graph', 'subgraphs', 'blockchain indexing', 'graphql', 'dapp data'] --- + import { ImageWithCaption } from '../../../src/components'; import imgApiKeys from '../../../public/assets/ecosystem/resources/the-graph/api_keys.png'; @@ -195,9 +196,7 @@ Passing this into the query URL returns this result: // ... ``` - - Trivia: Looking at the top sales on [CryptoPunks website](https://cryptopunks.app/cryptopunks/topsales) it looks like the top sale is Punk #5822, not #9998. Why? Because they censor the flash-loan sale that happened. - +Trivia: Looking at the top sales on [CryptoPunks website](https://cryptopunks.app/cryptopunks/topsales) it looks like the top sale is Punk #5822, not #9998. Why? Because they censor the flash-loan sale that happened. ### Sample code diff --git a/content/evm/optimizing-for-parallelization.mdx b/content/evm/optimizing-for-parallelization.mdx index af5394bf..aff30b37 100644 --- a/content/evm/optimizing-for-parallelization.mdx +++ b/content/evm/optimizing-for-parallelization.mdx @@ -99,7 +99,7 @@ These reduce gas and often reduce storage touches, which also improves paralleli ## Leverage Sei Precompiles -Sei provides precompiled contracts for common functionality—cheaper and simpler than custom Solidity equivalents: +Sei provides precompiled contracts for common functionality-cheaper and simpler than custom Solidity equivalents: - [JSON](/evm/precompiles/json) (`0x1003`) - [P256](/evm/precompiles/P256) (`0x1011`) From 2b11d749f81311c3d8812590349f3590d966d462 Mon Sep 17 00:00:00 2001 From: seiyan-writer Date: Thu, 2 Oct 2025 17:04:26 -0400 Subject: [PATCH 25/25] clean doc styling --- app/globals.css | 107 +++++++++ content/evm/_meta.js | 3 +- content/evm/ai-tooling/cambrian-agent-kit.mdx | 4 +- content/evm/bridging/layerzero.mdx | 16 +- content/evm/bridging/thirdweb.mdx | 3 +- content/evm/building-a-frontend.mdx | 2 +- content/evm/changelog.mdx | 4 +- content/evm/differences-with-ethereum.mdx | 18 +- content/evm/ecosystem-contracts.mdx | 2 +- content/evm/evm-foundry.mdx | 2 +- content/evm/evm-hardhat.mdx | 2 +- content/evm/evm-verify-contracts.mdx | 6 +- content/evm/evm-wizard.mdx | 9 +- content/evm/ibc-protocol.mdx | 15 +- content/evm/index.mdx | 2 +- content/evm/indexer-providers/alchemy.mdx | 47 +--- content/evm/indexing-best-practices.mdx | 25 +-- content/evm/ledger-ethers.mdx | 15 +- content/evm/pointers-deep-dive.mdx | 206 ++++++++++++------ content/evm/precompile-distribution.mdx | 2 +- content/evm/precompile-example-usage.mdx | 12 - content/evm/precompile-oracle.mdx | 10 +- content/evm/precompile-pointer.mdx | 60 +++-- content/evm/precompile-pointerview.mdx | 61 ++++-- content/evm/precompile-solo.mdx | 26 +-- content/evm/precompile-version-matrix.mdx | 2 +- content/evm/reference.mdx | 4 +- content/evm/rpc-consensus-overview.mdx | 44 +--- content/evm/rpc-gas-accounting.mdx | 52 ++--- content/evm/rpc-gas-reference.mdx | 30 +-- content/evm/rpc-panic-faq.mdx | 24 +- content/evm/rpc-prioritizer.mdx | 164 ++++---------- content/evm/rpc-synthetic-transactions.mdx | 38 +--- content/evm/rpc-tracer-guide.mdx | 22 +- content/evm/rpc-websockets.mdx | 22 +- content/evm/sei-global-wallet.mdx | 4 +- content/evm/solidity-resources.mdx | 2 +- content/evm/tracing-javascript-tracers.mdx | 2 +- content/evm/tracing-playbook.mdx | 2 +- content/evm/tracing-troubleshooting.mdx | 6 +- content/evm/usdc-on-sei.md | 4 +- content/learn/dev-chains.mdx | 4 +- content/learn/explorers.mdx | 4 +- content/learn/parallelization-engine.mdx | 6 +- content/node/index.mdx | 6 +- content/node/snapshot.mdx | 6 +- content/node/technical-reference.mdx | 4 +- .../node/validator-operations-playbook.mdx | 2 +- content/node/validator-upgrade-runbook.mdx | 2 +- 49 files changed, 544 insertions(+), 571 deletions(-) diff --git a/app/globals.css b/app/globals.css index 0346d128..729098e4 100644 --- a/app/globals.css +++ b/app/globals.css @@ -253,3 +253,110 @@ aside, .nextra-nav-container a.nx-me-auto { margin-inline-end: 0 !important; } + +/* Callout component overrides - targeting Nextra's actual structure */ +.nextra-callout { + margin: 1.75rem 0 !important; + padding: 16px 20px !important; + border-radius: 12px !important; + border-width: 1.5px !important; + box-shadow: + 0 20px 44px -26px rgba(15, 23, 42, 0.16), + 0 0 0 1px rgba(255, 255, 255, 0.03) inset !important; + background: transparent !important; +} + +.dark .nextra-callout { + box-shadow: + 0 24px 54px -28px rgba(3, 7, 18, 0.6), + 0 0 0 1px rgba(255, 255, 255, 0.05) inset !important; + background: transparent !important; +} + +.nextra-callout :where(p, ul, ol) { + margin-top: 0 !important; + margin-bottom: 0.75rem !important; +} + +.nextra-callout :where(p:last-child, ul:last-child, ol:last-child) { + margin-bottom: 0 !important; +} + +.nextra-callout code { + background: var(--callout-code-bg) !important; + border-radius: 6px !important; + padding: 0.16rem 0.48rem !important; + font-size: 0.84em !important; + border: 1px solid rgba(255, 255, 255, 0.06) !important; +} + +/* Info callout (blue) */ +.nextra-callout.x\\:border-blue-200 { + --callout-code-bg: rgba(59, 130, 246, 0.1); + border-color: rgba(59, 130, 246, 0.35) !important; + color: rgba(30, 58, 138, 0.88) !important; +} + +.dark .nextra-callout.x\\:border-blue-200 { + --callout-code-bg: rgba(147, 197, 253, 0.12); + border-color: rgba(147, 197, 253, 0.5) !important; + color: rgba(224, 242, 254, 0.96) !important; +} + +.dark .nextra-callout.x\\:border-blue-200 svg { + color: rgba(147, 197, 253, 0.92) !important; +} + +/* Warning callout (yellow) */ +.nextra-callout.x\\:border-yellow-100, +.nextra-callout.x\\:border-yellow-200 { + --callout-code-bg: rgba(234, 179, 8, 0.1); + border-color: rgba(234, 179, 8, 0.42) !important; + color: rgba(113, 63, 18, 0.85) !important; +} + +.dark .nextra-callout.x\\:border-yellow-100, +.dark .nextra-callout.x\\:border-yellow-200 { + --callout-code-bg: rgba(253, 224, 71, 0.12); + border-color: rgba(253, 224, 71, 0.55) !important; + color: rgba(254, 249, 195, 0.96) !important; +} + +.dark .nextra-callout.x\\:border-yellow-100 svg, +.dark .nextra-callout.x\\:border-yellow-200 svg { + color: rgba(253, 224, 71, 0.92) !important; +} + +/* Error callout (red) */ +.nextra-callout.x\\:border-red-200 { + --callout-code-bg: rgba(239, 68, 68, 0.1); + border-color: rgba(239, 68, 68, 0.35) !important; + color: rgba(127, 29, 29, 0.88) !important; +} + +.dark .nextra-callout.x\\:border-red-200 { + --callout-code-bg: rgba(252, 165, 165, 0.12); + border-color: rgba(252, 165, 165, 0.5) !important; + color: rgba(254, 226, 226, 0.96) !important; +} + +.dark .nextra-callout.x\\:border-red-200 svg { + color: rgba(252, 165, 165, 0.92) !important; +} + +/* Default/orange callout */ +.nextra-callout.x\\:border-orange-100 { + --callout-code-bg: rgba(234, 88, 12, 0.1); + border-color: rgba(234, 88, 12, 0.35) !important; + color: rgba(124, 45, 18, 0.85) !important; +} + +.dark .nextra-callout.x\\:border-orange-100 { + --callout-code-bg: rgba(253, 186, 116, 0.12); + border-color: rgba(253, 186, 116, 0.45) !important; + color: rgba(254, 237, 213, 0.96) !important; +} + +.dark .nextra-callout.x\\:border-orange-100 svg { + color: rgba(253, 186, 116, 0.92) !important; +} diff --git a/content/evm/_meta.js b/content/evm/_meta.js index a23e97e1..7cca0ee6 100644 --- a/content/evm/_meta.js +++ b/content/evm/_meta.js @@ -86,7 +86,8 @@ export default { 'indexer-providers': 'Indexer Providers', 'tracing-overview': 'Tracing Overview', 'tracing-playbook': 'Tracing Playbook', - 'tracing-javascript-tracers': 'Tracer Authoring', + 'tracing-javascript-tracers': 'JavaScript Tracers', + 'rpc-tracer-guide': 'Custom Tracer Guide', 'tracing-troubleshooting': 'Tracing Troubleshooting', '-- RPC Diagnostics': { diff --git a/content/evm/ai-tooling/cambrian-agent-kit.mdx b/content/evm/ai-tooling/cambrian-agent-kit.mdx index f4ad9118..b4d26098 100644 --- a/content/evm/ai-tooling/cambrian-agent-kit.mdx +++ b/content/evm/ai-tooling/cambrian-agent-kit.mdx @@ -11,7 +11,7 @@ import { IconInfoCircle, IconAlertTriangle, IconShield, IconCode, IconDatabase } ## Overview -Cambrian Agent Kit is a developer SDK for building powerful, autonomous AI agents and agentic chatbots on the SEI blockchain. It lets you interact with DeFi protocols (Takara, Silo, Citrex, Symphony), manage SEI tokens and NFTs, and seamlessly integrate AI workflows. In this tutorial, you'll learn how to set up the kit, run your first agent, and use it for real DeFi use cases—customized for your needs. To understand more and deep dive into how it works under the hood, please refer to the [Cambrian Agent Kit Documentation](https://deepwiki.com/CambrianAgents/sei-agent-kit/1-overview). +Cambrian Agent Kit is a developer SDK for building powerful, autonomous AI agents and agentic chatbots on the SEI blockchain. It lets you interact with DeFi protocols (Takara, Silo, Citrex, Symphony), manage SEI tokens and NFTs, and seamlessly integrate AI workflows. In this tutorial, you'll learn how to set up the kit, run your first agent, and use it for real DeFi use cases-customized for your needs. To understand more and deep dive into how it works under the hood, please refer to the [Cambrian Agent Kit Documentation](https://deepwiki.com/CambrianAgents/sei-agent-kit/1-overview). ## Supported Features @@ -189,7 +189,7 @@ async function checkBalanceWithErrorHandling() { // Validate balance before proceeding if (parseFloat(balance) < 0.01) { - console.warn('⚠️ Low SEI balance. You may need more SEI for gas fees.'); + console.warn('Low SEI balance. You may need more SEI for gas fees.'); } } catch (error) { console.error('Error checking balance:', error.message); diff --git a/content/evm/bridging/layerzero.mdx b/content/evm/bridging/layerzero.mdx index d945fcc0..3ede30d3 100644 --- a/content/evm/bridging/layerzero.mdx +++ b/content/evm/bridging/layerzero.mdx @@ -620,7 +620,7 @@ You've issued an omnichain token and bridged it from Sei Mainnet to Optimism. Cu ### Common Issues and Solutions - + **Quote Send Reverts** If your `quoteSend` call reverts, it usually means that your LayerZero wiring hasn't been fully configured or there's no default pathway for the chains you're trying to bridge. Here's how to diagnose and fix it: @@ -659,7 +659,7 @@ Once you've updated your config, retry your `quoteSend` flow. It should now retu - + **Insufficient Gas** If the transaction fails on the destination chain, try to increase the gas like this: @@ -695,11 +695,11 @@ let options = Options.newOptions().addExecutorLzReceiveOption(150000, 0).toBytes You've successfully: -- ✅ Created an Omnichain Fungible Token project -- ✅ Configured networks and LayerZero pathways -- ✅ Deployed contracts to multiple chains -- ✅ Connected the deployments via LayerZero wiring -- ✅ Created tasks for cross-chain token transfers -- ✅ Learned how LayerZero enables omnichain applications +- Created an Omnichain Fungible Token project +- Configured networks and LayerZero pathways +- Deployed contracts to multiple chains +- Connected the deployments via LayerZero wiring +- Created tasks for cross-chain token transfers +- Learned how LayerZero enables omnichain applications Your tokens can now move freely between SEI and any connected chain, maintaining a unified supply and enabling true cross-chain functionality! diff --git a/content/evm/bridging/thirdweb.mdx b/content/evm/bridging/thirdweb.mdx index efecd016..bdb65ab2 100644 --- a/content/evm/bridging/thirdweb.mdx +++ b/content/evm/bridging/thirdweb.mdx @@ -8,11 +8,12 @@ keywords: - bridging - any --- + # Enable Onramps, Swapping & Bridging on Any EVM Chain [**Payments**](https://thirdweb.com/payments?utm_source=sei&utm_medium=documentation&utm_campaign=chain_docs) is a comprehensive Web3 payment solution to help you monetize any app or game. -With Payments, your users can **onramp, bridge, and swap** on any EVM chain — with any EVM token or fiat — thanks to its **automatic cross-chain routing**. +With Payments, your users can **onramp, bridge, and swap** on any EVM chain - with any EVM token or fiat - thanks to its **automatic cross-chain routing**. Plus, developers can **earn from day one** using the **fee-sharing mechanism** and **easy implementation**. diff --git a/content/evm/building-a-frontend.mdx b/content/evm/building-a-frontend.mdx index b9cee5eb..5fc6c811 100644 --- a/content/evm/building-a-frontend.mdx +++ b/content/evm/building-a-frontend.mdx @@ -24,7 +24,7 @@ Developing the frontend of a dApp on Sei EVM involves connecting to wallets, int We'll implement the same functionality using each library so you can compare their approaches and choose the one that best fits your development style. - + **Deploy to Testnet First** diff --git a/content/evm/changelog.mdx b/content/evm/changelog.mdx index 379b08b9..90fed64e 100644 --- a/content/evm/changelog.mdx +++ b/content/evm/changelog.mdx @@ -19,7 +19,7 @@ Stay up-to-date with the latest changes, improvements, and new features in Sei. This changelog is automatically synced from the [sei-chain repository](https://github.com/sei-protocol/sei-chain/blob/main/CHANGELOG.md). For the most up-to-date information, you can also view the changelog directly on GitHub. - + **Stay Updated** @@ -31,7 +31,7 @@ Want to be notified of new releases? **Watch** the [sei-protocol/sei-chain](http -## Release-to-Doc Mapping (v6.1.11+) +## Release-to-Doc Mapping | Release | Code Reference | Documentation Touchpoints | | :------------------------- | :----------------------------- | :-------------------------------------------------------------------------------------- | diff --git a/content/evm/differences-with-ethereum.mdx b/content/evm/differences-with-ethereum.mdx index 8894de7b..25e976eb 100644 --- a/content/evm/differences-with-ethereum.mdx +++ b/content/evm/differences-with-ethereum.mdx @@ -90,9 +90,7 @@ Sei's EVM and Ethereum itself: - Sei assets can not only exist as EVM (i.e., ERC20/721/1155), but also as CW (i.e., CW20, CW721) or as "native" (Sei, IBC, Tokenfactory). - User accounts on Sei have [two addresses](/learn/accounts) derived from the same public key (Cosmos Bech32 and EVM-compatible 0x…) - - Interoperability between EVM and Cosmos-SDK modules is governed and navigated via [precompiles](./precompiles/example-usage) and [pointer contracts](/learn/pointers) - +Interoperability between EVM and Cosmos-SDK modules is governed and navigated via [precompiles](./precompiles/example-usage) and [pointer contracts](/learn/pointers) ## Sei EVM Deployment @@ -278,10 +276,12 @@ Sei’s EVM fully supports the common token standards: Existing OpenZeppelin contracts and tools work unchanged. -## Testing & Migration Checklist +## Migration Notes + +When deploying existing Ethereum contracts to Sei: -- ✅ Re‑deploy your Solidity code to Sei testnet; most contracts need no changes. -- ✅ If you used SELFDESTRUCT, refactor to a soft‑close pattern. -- ✅ Remove EIP‑1559 fee UI complexity; use a single `gasPrice` input in frontends. -- ✅ If you rely on on‑chain “randomness,” integrate an oracle/VRF. -- ✅ Size your `gasLimit` with a modest buffer (parallel execution can slightly vary estimates). +- Re-deploy your Solidity code to Sei testnet; most contracts require no changes. +- If you used `SELFDESTRUCT`, refactor to a soft-close pattern. +- Simplify fee UI: use a single `gasPrice` input instead of EIP-1559 complexity. +- For on-chain randomness, integrate an oracle or VRF solution. +- Set `gasLimit` with a modest buffer; parallel execution can cause slight estimate variance. diff --git a/content/evm/ecosystem-contracts.mdx b/content/evm/ecosystem-contracts.mdx index 47b382ca..01b1d28c 100644 --- a/content/evm/ecosystem-contracts.mdx +++ b/content/evm/ecosystem-contracts.mdx @@ -38,7 +38,7 @@ This registry is actively maintained and expanded. We're constantly adding new p - **View Contract Details**: Click on the Seitrace link to see detailed information about the contract - **Bookmark This Page**: Keep this registry handy for quick access to the latest contract addresses and updates. - + **Add Your Project** diff --git a/content/evm/evm-foundry.mdx b/content/evm/evm-foundry.mdx index fc108dfe..f46dabe4 100644 --- a/content/evm/evm-foundry.mdx +++ b/content/evm/evm-foundry.mdx @@ -11,7 +11,7 @@ import { Callout } from 'nextra/components'; This tutorial will guide you through setting up Foundry for Sei EVM development and using OpenZeppelin contracts to build secure, standardized smart contracts. We'll cover environment setup, contract creation, deployment, and show how to leverage OpenZeppelin's pre-built components with the powerful Foundry toolkit. - + **Deploy to Testnet First** diff --git a/content/evm/evm-hardhat.mdx b/content/evm/evm-hardhat.mdx index 6f8f3268..eb401a7f 100644 --- a/content/evm/evm-hardhat.mdx +++ b/content/evm/evm-hardhat.mdx @@ -11,7 +11,7 @@ import { Callout } from 'nextra/components'; This tutorial will guide you through setting up Hardhat for Sei EVM development and using OpenZeppelin contracts to build secure, standardized smart contracts. We'll cover environment setup, contract creation, deployment, and show how to leverage OpenZeppelin's pre-built components. - + **Deploy to Testnet First** It is highly recommended that you deploy to testnet (atlantic-2) first and verify everything works as expected before committing to mainnet. Doing so helps you catch bugs early, avoid unnecessary gas costs, and keep your users safe. diff --git a/content/evm/evm-verify-contracts.mdx b/content/evm/evm-verify-contracts.mdx index ffa7886a..654bfb4a 100644 --- a/content/evm/evm-verify-contracts.mdx +++ b/content/evm/evm-verify-contracts.mdx @@ -104,7 +104,7 @@ Access this function at: https://seitrace.com/contract-verification 7. **Enter the Solidity Contract Code:** You may need to flatten your Solidity code if it utilizes a library or inherits dependencies from another contract. We recommend [Hardhat](https://hardhat.org/) or the [POA Solidity Flattener](https://github.com/poanetwork/solidity-flattener). 8. **Add Contract Libraries:** Enter the name and `0x` address for any required libraries called in the `.sol` file. You can add multiple contracts with the “+” button. 9. **Click** the `Verify and Publish` button. -10. If all goes well, you will see a check‑mark ✅ next to **Code** in the code tab, and an additional tab called **Read Contract**. The contract name will now appear in Seitrace with any transactions related to your contract. +10. If successful, you will see a verification indicator next to **Code** in the code tab, and an additional tab called **Read Contract**. The contract name will now appear in Seitrace with any transactions related to your contract. ### Solidity (Standard JSON input) @@ -124,7 +124,7 @@ Click **Verify & Publish** and wait for the response. 4. **Contract Code:** Copy and paste the contract code. 5. **Click** the `Verify and Publish` button. -If all goes well, you will see a check‑mark ✅ next to Code in the code tab, and an additional tab called **Read Contract**. +If successful, you will see a verification indicator next to Code in the code tab, and an additional tab called **Read Contract**. The contract name will now appear in Seitrace with any transactions related to your contract. @@ -139,7 +139,7 @@ If you receive the `There was an error compiling your contract` message this mea Unfortunately, there are many reasons this may be the case. Here are a few things to try: 1. Double check the compiler version is correct. - Check all version digits — for example `0.5.1` is different from `0.5.10` + Check all version digits - for example `0.5.1` is different from `0.5.10` 2. Check that an extra space has not been added to the end of the contract. When pasting in, an extra space may be added. Delete this and attempt to recompile. 3. Copy, paste, and verify your source code in [Remix](https://remix.ethereum.org/). You may find some exceptions here. diff --git a/content/evm/evm-wizard.mdx b/content/evm/evm-wizard.mdx index faa4aecd..6114d331 100644 --- a/content/evm/evm-wizard.mdx +++ b/content/evm/evm-wizard.mdx @@ -1,8 +1,9 @@ --- -title: "Using the OpenZeppelin Contract Wizard with Sei EVM" -description: "Learn how to use the OpenZeppelin Contract Wizard to create secure, standard-compliant smart contracts for Sei EVM, with step-by-step guidance and best practices." -keywords: ["OpenZeppelin wizard", "smart contract generator", "ERC standards", "Sei EVM", "contract development"] +title: 'Using the OpenZeppelin Contract Wizard with Sei EVM' +description: 'Learn how to use the OpenZeppelin Contract Wizard to create secure, standard-compliant smart contracts for Sei EVM, with step-by-step guidance and best practices.' +keywords: ['OpenZeppelin wizard', 'smart contract generator', 'ERC standards', 'Sei EVM', 'contract development'] --- + # Using the EVM OpenZeppelin Contract Wizard The OpenZeppelin Contract Wizard is a powerful interactive tool that simplifies the creation of secure, standard-compliant smart contracts for Sei EVM. It provides a user-friendly interface for generating production-ready contract code based on OpenZeppelin's battle-tested libraries. Explore and create your custom smart contracts using the embedded wizard below. @@ -103,7 +104,7 @@ The OpenZeppelin Contract Wizard is a powerful interactive tool that simplifies - **Understand Each Option**: Hover over the question icons (?) for detailed explanations - **Review Dependencies**: Note which OpenZeppelin libraries your contract imports - **Consider Gas Costs**: More features generally mean higher deployment and operation costs -- **Customize Post-Generation**: The wizard provides a starting point—most projects will require additional customization +- **Customize Post-Generation**: The wizard provides a starting point-most projects will require additional customization ## Learn More diff --git a/content/evm/ibc-protocol.mdx b/content/evm/ibc-protocol.mdx index fa525ec3..3f94e8c0 100644 --- a/content/evm/ibc-protocol.mdx +++ b/content/evm/ibc-protocol.mdx @@ -1,8 +1,9 @@ --- -title: "EVM-Enhanced IBC Implementation" +title: 'EVM-Enhanced IBC Implementation' description: "Discover Sei's innovative approach to blockchain interoperability through its EVM-enhanced IBC implementation, allowing direct, trustless communication between EVM smart contracts and the IBC protocol without intermediate bridges." -keywords: ["evm ibc", "blockchain interoperability", "ibc protocol", "cross-chain communication", "sei precompiles"] +keywords: ['evm ibc', 'blockchain interoperability', 'ibc protocol', 'cross-chain communication', 'sei precompiles'] --- + # EVM-Enhanced IBC Implementation ## Understanding the Foundation: IBC Protocol @@ -11,7 +12,7 @@ The Inter-Blockchain Communication (IBC) protocol represents a fundamental advancement in blockchain interoperability, enabling direct, trustless communication between independent blockchains. Much like TCP/IP created a standard way for computers to communicate across the internet, IBC establishes a -universal protocol for blockchains to interact—regardless of their underlying +universal protocol for blockchains to interact-regardless of their underlying architecture. ### The Protocol Stack @@ -22,7 +23,7 @@ into two distinct layers: - **Transport Layer:** This layer manages the secure movement of data between chains. Just as TCP/IP is indifferent to whether it's carrying an email or a web page, this layer is - agnostic to the type of data transferred—focusing solely on ensuring reliable, + agnostic to the type of data transferred-focusing solely on ensuring reliable, secure delivery. - **Application Layer:** @@ -38,7 +39,7 @@ cross-chain communication: - **Light Clients:** Act as trustless observers, verifying the state of another blockchain without - storing its full copy—much like a trusted friend verifying local events from + storing its full copy-much like a trusted friend verifying local events from afar. - **Connections:** @@ -60,7 +61,7 @@ cross-chain communication: Traditional bridges often rely on multiple steps, manual interventions, or additional trust assumptions. In contrast, Sei's architecture enables EVM -clients and smart contracts to interact with IBC directly—eliminating friction +clients and smart contracts to interact with IBC directly-eliminating friction points such as wrapped tokens or manual claiming. This uniquely positions Sei as an easily accessible solution for teams seeking a fast, reliable and affordable gateway between any existing IBC and EVM ecosystems. @@ -253,7 +254,7 @@ a transaction arrives, it: 3. Makes tokens immediately available in the correct format For instance, when tokens are directed to an EVM address, a pointer contract -updates balances in real time—eliminating manual claims or conversions. +updates balances in real time-eliminating manual claims or conversions. --- diff --git a/content/evm/index.mdx b/content/evm/index.mdx index 3fefbf56..05188f0e 100644 --- a/content/evm/index.mdx +++ b/content/evm/index.mdx @@ -224,7 +224,7 @@ import { RpcSelector, AddSeiInlineButton } from '../../src/components';

- Choose from official Sei Foundation endpoints or community-maintained alternatives. All endpoints support standard EVM JSON-RPC methods. Public endpoints have rate limits—for production apps, consider using a dedicated RPC provider or running your own node. + Choose from official Sei Foundation endpoints or community-maintained alternatives. All endpoints support standard EVM JSON-RPC methods. Public endpoints have rate limits-for production apps, consider using a dedicated RPC provider or running your own node.

diff --git a/content/evm/indexer-providers/alchemy.mdx b/content/evm/indexer-providers/alchemy.mdx index f0070c52..11e6e72c 100644 --- a/content/evm/indexer-providers/alchemy.mdx +++ b/content/evm/indexer-providers/alchemy.mdx @@ -3,7 +3,7 @@ title: 'Alchemy Subgraphs' --- import { Callout } from 'nextra/components'; -import { CardGrid, TroubleshootingTable } from '../../../mdx-components'; +import { TroubleshootingTable } from '../../../mdx-components'; # Alchemy Subgraphs @@ -68,48 +68,15 @@ Sei blocks do not revert, but Alchemy maintains a virtual reorg buffer for consi ## Build Pipeline Snapshot -
-
- deployment loop -

Sei subgraph lifecycle

-
-
-
-

1. Generate schema & mappings

-

Run `graph codegen` locally; ensure synthetic fields are added to the schema.

-
-
-

2. Build & validate

-

Use `graph build` with a local IPFS daemon (or the bundled mock) to catch ABI mismatches fast.

-
-
-

3. Deploy to Alchemy

-

`graph deploy --node https://subgraphs.sei.alchemy.com/api` and watch the dashboard until lag shrinks under 2 blocks.

-
-
-
+1. **Generate schema & mappings** - Run `graph codegen` locally and ensure synthetic fields are declared in `schema.graphql` before publishing. +2. **Build & validate** - Use `graph build` with a local IPFS daemon (or bundled mock) to catch ABI mismatches fast. +3. **Deploy to Alchemy** - `graph deploy --node https://subgraphs.sei.alchemy.com/api` and monitor the dashboard until lag shrinks under 2 blocks. ## Implementation References - +- [Schema checklist](https://thegraph.com/docs/en/developing/creating-a-subgraph/) - ensure every synthetic field is represented in `schema.graphql` before running codegen. +- [Sei indexing best practices](/evm/indexing-best-practices) - align window sizes and backoff strategy with Sei RPC guidance. +- [Alchemy subgraph dashboard](https://dashboard.alchemy.com/) - monitor buffered vs finalized heights, error counts, and throughput. ## Troubleshooting diff --git a/content/evm/indexing-best-practices.mdx b/content/evm/indexing-best-practices.mdx index 6de39dd3..c49b1a87 100644 --- a/content/evm/indexing-best-practices.mdx +++ b/content/evm/indexing-best-practices.mdx @@ -5,7 +5,6 @@ keywords: ['indexing', 'logs', 'reorgs', 'backfill', 'best practices'] --- import { Callout } from 'nextra/components'; -import { CardGrid } from '../../mdx-components'; # Indexing Best Practices @@ -36,7 +35,7 @@ Sei produces instant finality; however, nodes may restart mid-ingest. Implement ## Rate Limits & Retries - Throttle requests to one window per 200 ms unless working with a dedicated archive endpoint. -- Respect HTTP `429` responses—exponential backoff starting at 1s, max 30s. +- Respect HTTP `429` responses-exponential backoff starting at 1s, max 30s. - Retry `EOF` and transient network errors up to 5 attempts; avoid retrying contract-level reverts. - Encode idempotency by hashing blockNumber + logIndex to prevent duplicate writes when retries occur. @@ -77,22 +76,6 @@ Sei produces instant finality; however, nodes may restart mid-ingest. Implement ## Reference Implementations - +- [@sei-js/evm indexing scripts](https://github.com/sei-protocol/sei-js/tree/main/packages/indexing) - production-ready ingestion loop with retries, checkpointing, and synthetic log metadata. +- [Tracing playbook](/evm/tracing/playbook) - understand trace limits and panic handling when debugging ingestion anomalies. +- [Pointers deep dive](/evm/pointers-deep-dive) - learn how pointer metadata influences synthetic events surfaced during indexing. diff --git a/content/evm/ledger-ethers.mdx b/content/evm/ledger-ethers.mdx index aa3cb663..29caba5e 100644 --- a/content/evm/ledger-ethers.mdx +++ b/content/evm/ledger-ethers.mdx @@ -1,8 +1,9 @@ --- -title: "Using Ledger Wallet with Ethers on Sei" -description: "Learn how to integrate Ledger hardware wallets with the ethers.js library for secure Sei EVM transactions, including wallet setup, connection, and transaction signing." -keywords: ["ledger wallet", "ethers.js", "hardware wallet", "blockchain security", "sei transactions"] +title: 'Using Ledger Wallet with Ethers on Sei' +description: 'Learn how to integrate Ledger hardware wallets with the ethers.js library for secure Sei EVM transactions, including wallet setup, connection, and transaction signing.' +keywords: ['ledger wallet', 'ethers.js', 'hardware wallet', 'blockchain security', 'sei transactions'] --- + # Using Ledger wallet with ethers on Sei ## Prerequisites & Setup @@ -63,7 +64,7 @@ const signer = new LedgerSigner(TransportNodeHid, provider); **Key points before you code:** -- **Precompiles** are optimized, built-in contracts—no external contract deployment needed. +- **Precompiles** are optimized, built-in contracts-no external contract deployment needed. - You must supply the **`value`** field in overrides to attach SEI. - Every transaction pauses to let you **approve** on Ledger. @@ -137,7 +138,7 @@ runGovernanceDemo(); - The staking precompile is simply another in-chain contract that handles delegation logic. - You must set both `value` (SEI to lock) **and** `from` (your EVM address). -- Gas limits on precompiles can be very low—start small and raise if you hit errors. +- Gas limits on precompiles can be very low-start small and raise if you hit errors. ```tsx copy import { STAKING_PRECOMPILE_ADDRESS, STAKING_PRECOMPILE_ABI } from '@sei-js/evm'; @@ -192,9 +193,9 @@ runStakingDemo(); - If you see "blind signing required" errors, double-check the app settings on the device itself. - **Reading Events & Logs:** - You can attach an `on("receipt", …)` or use `provider.getTransactionReceipt(tx.hash)` to inspect events emitted by + You can attach an `on("receipt", …)` or use `provider.getTransactionReceipt(tx.hash)` to inspect events emitted by the precompile for richer UX. - **Security Reminder:** - Always verify the **contract address** and **ABI** match official Sei docs. Using a wrong address can lead to lost + Always verify the **contract address** and **ABI** match official Sei docs. Using a wrong address can lead to lost funds. diff --git a/content/evm/pointers-deep-dive.mdx b/content/evm/pointers-deep-dive.mdx index 5b0bf30e..01e9dde4 100644 --- a/content/evm/pointers-deep-dive.mdx +++ b/content/evm/pointers-deep-dive.mdx @@ -1,107 +1,181 @@ --- title: 'Pointers Deep Dive' description: 'Design patterns for bridging identities and assets between CW and EVM using Pointer & PointerView.' -keywords: ['pointer', 'pointerview', 'cw20', 'erc1155', 'erc721', 'interop'] +keywords: + - pointer + - pointerview + - cw20 + - erc1155 + - erc721 + - interop --- import { Callout } from 'nextra/components'; -import { CardGrid, KeyValueTable, TroubleshootingTable } from '../../mdx-components'; +import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; # Pointers Deep Dive -The Pointer and PointerView precompiles bridge Cosmos assets and identities into Sei’s EVM. This guide covers the write/read split, typical mapping patterns, and operational safeguards before you dive into the individual ABI docs. - -## When to use Pointer vs PointerView - - +The Pointer (write) and PointerView (read) precompiles are the backbone of identity and asset interoperability on Sei. This guide dives into how the mapping layer is structured, how migrations use it, and how to operate Pointer safely in production. + +## Architecture Overview + +Pointer is the stateful gateway: it writes mappings that link CW addresses and native denoms to canonical EVM contracts. PointerView exposes the same data read-only so applications, wallets, and indexers can resolve ownership with zero mutation risk. + +| Component | Responsibility | Source code anchor | +| :---------------------- | :-------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | +| Pointer (`0x…100B`) | Registers mappings via `add*Pointer` functions, emits events, charges fees. | [`precompiles/pointer/abi.json`](https://github.com/sei-protocol/sei-chain/blob/main/precompiles/pointer/abi.json) | +| PointerView (`0x…100A`) | Returns pointer address, version, and existence flag. | [`precompiles/pointerview/abi.json`](https://github.com/sei-protocol/sei-chain/blob/main/precompiles/pointerview/abi.json) | +| Solo precompile | Consumes Pointer data during asset migrations. | [`precompiles/solo/abi.json`](https://github.com/sei-protocol/sei-chain/blob/main/precompiles/solo/abi.json) | +| Addr precompile | Verifies association prerequisites before pointer writes. | [`precompiles/addr/abi.json`](https://github.com/sei-protocol/sei-chain/blob/main/precompiles/addr/abi.json) | + +Pointer emits `PointerRegistered`, `PointerUpdated`, and `PointerRemoved` logs. PointerView does not emit events; treat it as the canonical source of truth when reconciling state. -## Mapping patterns +## Lifecycle of a Pointer Mapping -Use Pointer for ownership/actions; use PointerView for safe queries. Contracts should never rely on off-chain caches for ownership truth. +1. **Pre-flight checks** + - Confirm the CW contract implements the expected interface (CW20 `transfer`, CW721 `owner_of`, etc.). + - Ensure the initiating wallet is associated (`sei_associate` or Addr precompile). + - Validate you have governance approval for new mappings (fees + policy). +2. **Write** + - Call the relevant `add*Pointer` entrypoint with the CW contract address or denom identifier. + - Pay the required fee in `usei`; transactions revert if fees are missing. +3. **Event emission** + - Pointer emits a log tagged `synthetic=true` (post `v6.1.11`). Indexers capture the mapping metadata. +4. **Verification** + - Query PointerView to confirm `(address, version, exists)` match the expected deployment. + - Persist the pointer address in application configuration or registry contracts. +5. **Usage** + - Downstream EVM contracts interact with the pointer contract as if it were the canonical ERC implementation. + - Front-ends and wallets use PointerView to show asset provenance. -## Identifiers +## Identifiers and Metadata -## Examples +## Mapping Recipes -### Register a CW20 token for EVM access +### CW20 → ERC-20 -```solidity copy -pragma solidity ^0.8.20; +1. Register pointer via governance-controlled multisig or module. +2. Wait for the transaction to finalize and capture the emitted pointer address. +3. Update front-ends to read balances from the new pointer contract, not the legacy CW endpoint. +4. (Optional) Mint/burn functionality can proxy through the pointer if the CW contract exposes hooks. + +### CW721 → ERC-721 Metadata + +1. Register pointer with `addCW721Pointer`. +2. Deploy a metadata proxy that queries PointerView for the original CW collection when constructing token URIs. +3. Ensure indexers subscribe to both ERC-721 events and CW721 events for full provenance. + +### CW1155 → ERC-1155 Batches + +1. Batch registration can reuse the same pointer contract for multiple token IDs; pointer stores the CW contract mapping. +2. When migrating, ensure the pointer contract mirrors CW1155 semantics for `safeBatchTransferFrom`. +3. Test large batch transfers under load-they rely heavily on gas estimations that include pointer lookup warmups. + +### Native Denom → ERC-20 Wrapper + +1. Call `addNativePointer('usei')` (or other denom) after verifying supply controls. +2. The resulting pointer contract holds canonical mint/burn logic tied to the Cosmos bank module. +3. Use PointerView lookups before referencing the address in DeFi protocols to avoid stale registries. +## Integration Patterns + +### Pointer Write Module + +```solidity copy interface IPointer { - function addCW20Pointer(string calldata cwToken, address evmToken) external; + function addNativePointer(string calldata denom) external payable returns (address); } -contract CW20Registrar { - IPointer public immutable pointer; +contract PointerAuthorizer { + IPointer immutable pointer; + address immutable treasury; - constructor(address pointerAddr) { - pointer = IPointer(pointerAddr); + constructor(address pointerAddress, address treasuryAddr) { + pointer = IPointer(pointerAddress); + treasury = treasuryAddr; } - function register(string calldata cwToken, address evmToken) external { - pointer.addCW20Pointer(cwToken, evmToken); + function registerNative(string calldata denom) external payable onlyGov { + require(msg.value >= 10 ** 15, "fee required"); + address deployed = pointer.addNativePointer{value: msg.value}(denom); + (bool ok, ) = treasury.call{value: msg.value}(""); + require(ok, "treasury ack failed"); + emit NativePointerRegistered(denom, deployed); } } ``` -### Resolve ownership via PointerView +### PointerView Consumer ```solidity copy -pragma solidity ^0.8.20; - interface IPointerView { - function getCW721Pointer(string calldata cwCollection) external view returns (address); + function getCW721Pointer(string calldata cwAddr) + external + view + returns (address pointer, uint16 version, bool exists); } -contract OwnershipLens { - IPointerView public immutable viewContract; +contract CollectionResolver { + IPointerView immutable pointerView; - constructor(address pointerView) { - viewContract = IPointerView(pointerView); + constructor(address pointerViewAddr) { + pointerView = IPointerView(pointerViewAddr); } - function ownerContract(string calldata cwCollection) external view returns (address) { - return viewContract.getCW721Pointer(cwCollection); + function resolveCW721(string calldata cwCollection) external view returns (address) { + (address pointer, , bool exists) = pointerView.getCW721Pointer(cwCollection); + require(exists, "pointer missing"); + return pointer; } } ``` -## Operational checklist +## Migration Runbooks + +### Solo-Assisted Balance Migration + +1. **Inventory** - Use PointerView to list registered native denoms and CW assets. +2. **Prepare claims** - Generate Solo payloads referencing pointer addresses and targeted EVM accounts. +3. **Dry run** - Execute claims on a staging network; ensure fees and pointer versions align. +4. **Execute** - Broadcast `claim` or `claimSpecific` transactions. +5. **Validate** - Confirm synthetic logs via indexer and PointerView pointer state. +6. **Monitor** - Track failures (`invalid payload`, `already claimed`) and reconcile. + +### Governance-Controlled Pointer Updates + +1. Submit proposal containing new pointer targets and justification. +2. Upon approval, run controlled script invoking `add*Pointer` or future `update*Pointer` flow. +3. Record transaction hashes and pointer addresses in change management system. +4. Notify downstream teams (indexers, wallets) of new version numbers. +5. Schedule post-change audit using `pointerView` queries and indexer diff reports. + +## Operations & Monitoring + +- **Access control** - Restrict pointer writes to multisigs, governance contracts, or hardened operator bots. Store the allowlist on-chain where possible. +- **Fee accounting** - Track total `usei` spent registering pointers; compare against expected policy budgets. +- **Indexing parity** - Nightly job to compare PointerView responses versus indexed events; alert on mismatches. +- **Version guard** - Scripts consuming PointerView should assert `version` values before assuming ABI compatibility. +- **Incident response** - Maintain runbooks for pointer drift, unauthorized writes, and failed migrations. Most issues are caught by reconciling PointerView against recorded events. + +## Observability Checklist @@ -109,9 +183,17 @@ contract OwnershipLens { + +## Related Material + +- [Pointer Precompile](/evm/precompile-pointer) - ABI reference for write operations. +- [PointerView Precompile](/evm/precompile-pointerview) - Read API reference. +- [Solo Precompile](/evm/precompile-solo) - Migration tooling built on pointer data. +- [Transaction Prioritizer](/evm/rpc-prioritizer) - Ensure pointer writes are prioritized alongside association transactions. diff --git a/content/evm/precompile-distribution.mdx b/content/evm/precompile-distribution.mdx index b6545f84..6657c3f4 100644 --- a/content/evm/precompile-distribution.mdx +++ b/content/evm/precompile-distribution.mdx @@ -103,7 +103,7 @@ console.log(rewards.total); - Withdraw calls revert during `staticcall`; they must be regular transactions - Commission withdrawals succeed only when `msg.sender` matches the validator operator address -- Returned amounts are raw integers—divide by `decimals` before converting to display units +- Returned amounts are raw integers-divide by `decimals` before converting to display units - v6.1.11+ emits synthetic distribution logs tagged with `synthetic=true` ## Troubleshooting diff --git a/content/evm/precompile-example-usage.mdx b/content/evm/precompile-example-usage.mdx index 15f3ec99..c0d4cc34 100644 --- a/content/evm/precompile-example-usage.mdx +++ b/content/evm/precompile-example-usage.mdx @@ -10,18 +10,6 @@ import { Callout } from 'nextra/components'; Sei precompiles are special smart contracts deployed at fixed addresses that expose native Sei functionality to EVM applications. They can be used like any standard smart contract, enabling powerful cross-chain capabilities. - -**Available Precompiles:** - -- **Staking** (`0x1005`) - Delegation and staking operations -- **Governance** (`0x1006`) - Proposal submission and voting -- **Oracle** (`0x1008`) - Price feed data access -- **JSON** (`0x1003`) - JSON data parsing -- **P256** (`0x1011`) - Verifying `P-256` elliptic curve signatures -- **Distribution** (`0x1007`) - Staking rewards and validator commissions - - - ## Quick Setup Install the required dependencies: diff --git a/content/evm/precompile-oracle.mdx b/content/evm/precompile-oracle.mdx index 0052a964..b58b0f48 100644 --- a/content/evm/precompile-oracle.mdx +++ b/content/evm/precompile-oracle.mdx @@ -91,11 +91,11 @@ console.log('SEI 1h TWAP:', seiTwap.twap); Common denominations: -- `usei` — Micro SEI -- `uusdc` — Micro USDC -- `uatom` — Micro ATOM -- `ueth` — Micro Ethereum -- `ubtc` — Micro Bitcoin +- `usei` - Micro SEI +- `uusdc` - Micro USDC +- `uatom` - Micro ATOM +- `ueth` - Micro Ethereum +- `ubtc` - Micro Bitcoin Call `getExchangeRates()` to see the full list of available price feeds. diff --git a/content/evm/precompile-pointer.mdx b/content/evm/precompile-pointer.mdx index 564d203b..a3c00109 100644 --- a/content/evm/precompile-pointer.mdx +++ b/content/evm/precompile-pointer.mdx @@ -9,9 +9,11 @@ import { FunctionList, TroubleshootingTable } from '../../mdx-components'; # Pointer Precompile -**Address:** `0x000000000000000000000000000000000000100b` +**Address:** `0x000000000000000000000000000000000000100B` -Bridge identities/assets between CW and EVM. +Pointer is the write surface that creates canonical bridges between CW contracts/native denoms and EVM contracts. It emits events for indexers and charges a fee in `usei` per registration. Pair it with PointerView for reads. + +Before calling Pointer, ensure the signer is associated (`sei_associate` or Addr precompile) and that PointerView doesn’t already report a mapping. ## Functions @@ -54,39 +56,67 @@ interface IPointerPrecompile { -Pointer creation is a state-changing transaction and requires a fee in usei; rely on PointerView for lookups when possible. +## Usage Workflow + +1. **Pre-flight** - Query PointerView to confirm the mapping is absent; verify the CW contract implements required interfaces. +2. **Authorize caller** - Pointer should be invoked by a governance module, multisig, or automation bot. Maintain an allowlist of valid callers. +3. **Set fee budget** - Pointer charges `usei`. Pull the current rate from governance or configuration and fund the caller account. +4. **Invoke Pointer** - Call the desired `add*Pointer` function with the target CW address/denom and attach the fee. Transactions revert on duplicate mappings, insufficient fees, or invalid addresses. +5. **Verify** - After the transaction confirms, fetch the pointer via PointerView and persist the address/version. Indexers must capture the `PointerRegistered` log (tagged `synthetic=true`). -## Example +## Example: Register CW20 + Native Pointer ```typescript copy import { ethers } from 'ethers'; -const POINTER = '0x000000000000000000000000000000000000100b'; +const POINTER = '0x000000000000000000000000000000000000100B'; const ABI = ['function addCW20Pointer(string cwAddr) payable returns (address)', 'function addNativePointer(string token) payable returns (address)']; const provider = new ethers.BrowserProvider(window.ethereum); await provider.send('eth_requestAccounts', []); -const pointer = new ethers.Contract(POINTER, ABI, await provider.getSigner()); +const signer = await provider.getSigner(); +const pointer = new ethers.Contract(POINTER, ABI, signer); -// Register CW20 pointer (fee charged in usei) -await pointer.addCW20Pointer('sei1cw20...', { value: ethers.parseEther('0.01') }); +const fee = ethers.parseEther('0.01'); + +// Register CW20 pointer +const cw20Tx = await pointer.addCW20Pointer('sei1cw20...', { value: fee }); +await cw20Tx.wait(); // Register native denom pointer -await pointer.addNativePointer('usei', { value: ethers.parseEther('0.01') }); +const nativeTx = await pointer.addNativePointer('usei', { value: fee }); +await nativeTx.wait(); ``` +## Operational Guidance + +- **Association prerequisite** - If the caller is not associated, Pointer calls revert. Run `sei_associate` first. +- **Fee accounting** - Track fees spent on registrations; reconcile with governance budgets. +- **Event monitoring** - Indexers must store `PointerRegistered`/`PointerUpdated` logs. Replay from the block height if events are missed. +- **Version coordination** - Pointer updates will increment the version returned by PointerView; alert downstream services when the number changes. +- **Rollback strategy** - Pointer cannot overwrite an existing mapping; to rotate, governance must deploy update tooling (planned) or remove and re-register (future extension). For now, treat initial registration as permanent. + ## Notes -- Pointer creation requires governance-approved fees; check release notes for current pricing -- Pointer contracts emit synthetic events tagged `synthetic=true` (v6.1.11+) -- Ensure CW contracts implement expected interfaces; invalid targets revert during registration +- Pointer creation requires governance-approved fees; check release notes for current pricing. +- Pointer contracts emit synthetic events tagged `synthetic=true` (`v6.1.11+`). +- Ensure CW contracts implement expected interfaces; invalid targets revert during registration. +- Solo migrations depend on up-to-date pointers; coordinate updates before running claim windows. ## Troubleshooting + +## Related Docs + +- [PointerView Precompile](/evm/precompile-pointerview) - Read API used to verify registrations. +- [Pointers Deep Dive](/evm/pointers-deep-dive) - Architecture guidance, migration runbooks, and monitoring. +- [Solo Precompile](/evm/precompile-solo) - Uses pointer data when migrating assets. +- [Addr Precompile](/evm/precompile-addr) - Association step required before pointer writes. diff --git a/content/evm/precompile-pointerview.mdx b/content/evm/precompile-pointerview.mdx index df4172d7..f78199a0 100644 --- a/content/evm/precompile-pointerview.mdx +++ b/content/evm/precompile-pointerview.mdx @@ -11,7 +11,9 @@ import { FunctionList, TroubleshootingTable } from '../../mdx-components'; **Address:** `0x000000000000000000000000000000000000100A` -Query registered pointers and their metadata. +PointerView is the read-only counterpart to Pointer. Use it to resolve canonical EVM contracts, confirm version numbers, and avoid duplicate registrations. It mirrors on-chain state without emitting events. + +PointerView returns the latest mapping even if events were missed. Always query it after pointer writes to verify success. ## Functions @@ -20,22 +22,22 @@ Query registered pointers and their metadata. { name: 'getCW20Pointer', signature: 'function getCW20Pointer(string cwAddr) view returns (address addr, uint16 version, bool exists)', - description: 'Fetch the ERC-20 pointer for a CW20 contract, plus version + existence flag.' + description: 'Fetch the ERC-20 pointer for a CW20 contract, with version and existence flag.' }, { name: 'getCW721Pointer', signature: 'function getCW721Pointer(string cwAddr) view returns (address addr, uint16 version, bool exists)', - description: 'Return ERC-721 pointer details for the CW721 contract.' + description: 'Resolve the ERC-721 pointer that mirrors the CW721 collection.' }, { name: 'getCW1155Pointer', signature: 'function getCW1155Pointer(string cwAddr) view returns (address addr, uint16 version, bool exists)', - description: 'Return ERC-1155 pointer data for the CW1155 contract.' + description: 'Return ERC-1155 pointer information for the CW1155 contract.' }, { name: 'getNativePointer', signature: 'function getNativePointer(string token) view returns (address addr, uint16 version, bool exists)', - description: 'Resolve the pointer contract for a native denom (e.g., "usei").' + description: 'Look up the pointer contract for a native denom (e.g., "usei").' } ]} /> @@ -54,7 +56,14 @@ interface IPointerViewPrecompile { -## Example +## Usage Patterns + +- **Post-registration verification** - After calling Pointer, immediately query PointerView to confirm the mapping, record the pointer address, and capture the version number in your registry. +- **Contract lookups** - Smart contracts should query PointerView before interacting with a pointer to avoid hard-coded addresses. Cache the result per block to reduce RPC calls. +- **Indexing reconciliation** - Indexers replay Pointer events but treat PointerView as the authoritative source. Nightly jobs compare stored mappings with PointerView responses. +- **Frontend display** - Wallets and explorers call PointerView to display the source CW contract or native denom associated with an EVM asset. + +## Example: Guarding Calls With PointerView ```typescript copy import { ethers } from 'ethers'; @@ -65,26 +74,46 @@ const ABI = ['function getCW20Pointer(string cwAddr) view returns (address addr, const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); const pointerView = new ethers.Contract(POINTER_VIEW, ABI, provider); -const [cw20Addr, cw20Version, cw20Exists] = await pointerView.getCW20Pointer('sei1cw20...'); -if (cw20Exists) { - console.log(`ERC-20 pointer deployed at ${cw20Addr} (version ${cw20Version})`); +export async function resolveCW20(cwAddr: string) { + const [pointer, version, exists] = await pointerView.getCW20Pointer(cwAddr); + if (!exists) { + throw new Error('Pointer not registered'); + } + return { pointer, version }; } -const [nativeAddr] = await pointerView.getNativePointer('usei'); -console.log('Native pointer for usei:', nativeAddr); +export async function resolveNative(denom: string) { + const [address, version, exists] = await pointerView.getNativePointer(denom); + return exists ? { address, version } : null; +} ``` +## Operations & Monitoring + +- Query pointer view endpoints from automation scripts before initiating migrations or pointer-dependent transactions. +- Record pointer versions and alert if they change unexpectedly; version increments indicate a re-registration or contract upgrade. +- Expose PointerView queries in health checks to confirm RPC endpoints are returning consistent data. +- When indexers miss events, use PointerView as the recovery source and backfill historical state from its responses. + ## Notes -- PointerView is read-only; use the Pointer precompile to create new mappings -- Results mirror on-chain state; cache responses client-side to avoid unnecessary RPC calls -- Use PointerView to confirm pointer ownership before invoking Pointer-dependent precompiles +- PointerView is read-only; use the Pointer precompile to create or update mappings. +- Responses mirror on-chain state and require no fee; cache results responsibly to avoid unnecessary RPC load. +- Use PointerView to confirm pointer ownership before invoking Solo, Bank, or other precompiles that rely on pointer addresses. +- `exists` returning `false` signals a missing mapping; treat `address(0)` with `exists=true` as an invalid state and escalate. ## Troubleshooting + +## Related Docs + +- [Pointer Precompile](/evm/precompile-pointer) - Write surface for registering pointers. +- [Pointers Deep Dive](/evm/pointers-deep-dive) - Architecture patterns, migrations, and operations. +- [Solo Precompile](/evm/precompile-solo) - Consumes PointerView data during asset migrations. diff --git a/content/evm/precompile-solo.mdx b/content/evm/precompile-solo.mdx index 542e0256..05df6c82 100644 --- a/content/evm/precompile-solo.mdx +++ b/content/evm/precompile-solo.mdx @@ -5,7 +5,7 @@ keywords: ['solo', 'migration', 'cw20', 'cw721', 'precompile', 'sei evm'] --- import { Callout } from 'nextra/components'; -import { CardGrid, FunctionList, TroubleshootingTable } from '../../mdx-components'; +import { FunctionList, TroubleshootingTable } from '../../mdx-components'; # Solo Precompile @@ -74,7 +74,7 @@ await solo.claimSpecific(cw20Payload, { gasLimit: 300_000 }); - Solo rejects CosmWasm entrypoints; invoke only from EVM context - Payloads are signature-bound; reuse of the same claim reverts with `already claimed` -- Review transaction receipts for synthetic logs tagged `synthetic=true` (v6.1.11+) +- Review transaction receipts for synthetic logs tagged `synthetic=true` - For asset inventories, pair Solo with Pointer/PointerView before generating payloads ## Troubleshooting @@ -89,22 +89,6 @@ await solo.claimSpecific(cw20Payload, { gasLimit: 300_000 }); ## Related Docs - +- [Pointer Deep Dive](/evm/pointers-deep-dive) - understand how pointer associations feed Solo and other migration flows. +- [Addr precompile](/evm/precompile-addr) - lookup associations before finalising claim payloads. +- [Indexing best practices](/evm/indexing-best-practices) - capture Solo synthetic logs reliably during large-scale migrations. diff --git a/content/evm/precompile-version-matrix.mdx b/content/evm/precompile-version-matrix.mdx index a5811b3d..a054dafa 100644 --- a/content/evm/precompile-version-matrix.mdx +++ b/content/evm/precompile-version-matrix.mdx @@ -12,7 +12,7 @@ import { CopyableAddress } from '../../src/components/CopyableAddress'; Track precompile availability and behavioral changes across Sei chain releases. -All precompile addresses are stable. Version changes affect behavior, gas costs, or available methods—not addresses. +All precompile addresses are stable. Version changes affect behavior, gas costs, or available methods-not addresses. ## Precompile Addresses diff --git a/content/evm/reference.mdx b/content/evm/reference.mdx index 206e66c7..205abc1e 100644 --- a/content/evm/reference.mdx +++ b/content/evm/reference.mdx @@ -66,7 +66,7 @@ Sei implements the [Ethereum JSON-RPC API](https://ethereum.org/en/developers/do ## Namespace Differences -### Gas accounting guarantees (v6.1.11+) +### Gas accounting guarantees As of [v6.1.11](https://github.com/sei-protocol/sei-chain/releases/tag/v6.1.11), `gasUsed` now always reflects the real gas charged on Sei for both blocks and receipts. Historical blocks may still show legacy values. @@ -86,7 +86,7 @@ Sei implements the [Ethereum JSON-RPC API](https://ethereum.org/en/developers/do - **Both EVM + Cosmos transactions** - Complete chain view - Includes synthetic logs from Cosmos modules (Bank, Distribution, Staking) - Transaction indices include all txs sequentially -- Adds `synthetic: true` flag to Cosmos-originated events (v6.1.11+) +- Adds `synthetic: true` flag to Cosmos-originated events Use `sei_` endpoints for indexers needing cross-VM visibility. Use `eth_` for pure EVM applications. diff --git a/content/evm/rpc-consensus-overview.mdx b/content/evm/rpc-consensus-overview.mdx index 35a00d09..d4466997 100644 --- a/content/evm/rpc-consensus-overview.mdx +++ b/content/evm/rpc-consensus-overview.mdx @@ -4,7 +4,7 @@ description: 'Operator-oriented reference for Sei consensus, mempool, and vote e keywords: ['consensus', 'mempool', 'vote extensions', 'sei tendermint'] --- -import { CardGrid, KeyValueTable, TroubleshootingTable } from '../../mdx-components'; +import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; # Consensus Deep Dive @@ -20,13 +20,13 @@ Sei rides Tendermint BFT, but the chain’s sub-second finality comes from a car ## Height H: Timeline With Code Anchors -| Stage | What Happens | Code Path / Signals | -| :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------ | -| Preflight (`H-1` still finalising) | `GenerateEstimatedWritesets` maps conflicting keys, SeiDB caches reads, duplicate cache prunes stale entries, new tx hints sampled for reservoir. | `app/abci/prepare.go`, logs `prepare height=`, `mempool/reservoir.go` | -| Proposal build (`EnterPropose`) | Proposer calls `CreateProposalBlock` to pack txs + OCC metadata. | `consensus/state.go:enterPropose`, `Consensus` logs `created proposal` | -| Broadcast & prevote | Proposal gossips, validators push `Prevote` while launching `DeliverTxBatch`. | `consensus/state.go:addVote`, metric `consensus_round` | -| Parallel execution | OCC runs `ProcessTXsWithOCC` workers, buffering writes in `CacheMultiStore`. | `app/abci/deliver_tx.go`, log `batch executed` | -| Precommit & finalise | When 2/3 precommits arrive, buffered writes flush to SeiDB, Tendermint schedules height `H+1`. | `consensus/state.go:finalizeCommit`, metrics `consensus_block_interval_seconds` | +| Stage | What Happens | Code Path / Signals | +| :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| Preflight (\`H-1\` still finalising) | \`GenerateEstimatedWritesets\` maps conflicting keys, SeiDB caches reads, duplicate cache prunes stale entries, new tx hints sampled for reservoir. | \`app/abci/prepare.go\`, logs \`prepare height=\`, \`mempool/reservoir.go\` | +| Proposal build (\`EnterPropose\`) | Proposer calls \`CreateProposalBlock\` to pack txs + OCC metadata. | \`consensus/state.go:enterPropose\`, \`Consensus\` logs \`created proposal\` | +| Broadcast & prevote | Proposal gossips, validators push \`Prevote\` while launching \`DeliverTxBatch\`. | \`consensus/state.go:addVote\`, metric \`consensus_round\` | +| Parallel execution | OCC runs \`ProcessTXsWithOCC\` workers, buffering writes in \`CacheMultiStore\`. | \`app/abci/deliver_tx.go\`, log \`batch executed\` | +| Precommit & finalise | When 2/3 precommits arrive, buffered writes flush to SeiDB, Tendermint schedules height \`H+1\`. | \`consensus/state.go:finalizeCommit\`, metrics \`consensus_block_interval_seconds\` | > Tip: `seid debug consensus-state` visualises these transitions live; pair it with `journalctl -u seid | grep consensus` during incident drills. @@ -106,27 +106,7 @@ seid debug mempool-stats ## Related Material - +- [Technical Reference](/node/technical-reference) - full node configuration matrix including `config.toml` consensus knobs. +- [Incident Playbooks](/node/node-incident-playbooks) - drills for consensus halts, snapshot issues, and P2P partitions. +- [RPC Gas Accounting](/evm/rpc-gas-accounting) - how block-level gas totals interact with the consensus updates. +- [Twin Turbo Consensus](/learn/twin-turbo-consensus) - protocol-level explainer of Sei’s pipelined Tendermint flow for sub-second finality. diff --git a/content/evm/rpc-gas-accounting.mdx b/content/evm/rpc-gas-accounting.mdx index 26806496..bdcc7619 100644 --- a/content/evm/rpc-gas-accounting.mdx +++ b/content/evm/rpc-gas-accounting.mdx @@ -19,43 +19,27 @@ Sei `v6.1.11` [release notes](https://github.com/sei-protocol/sei-chain/releases ## Gas Flow At a Glance - +1. **Ante handler**: Cosmos layer validates signatures/fees; synthetic envelopes stop here without touching EVM gas. +2. **EVM execution**: go-ethereum debits opcode gas, precompile calls, and Solo/SIP-3 migrations exactly as upstream. +3. **Refund + storage**: Apply EIP-3529 refund cap, persist `receipt.gasUsed = execution - refund`, then aggregate totals into the block header. ## Verifying Your Node or Indexer ` — ensure `gas_used` > 0 and matches RPC hash.'], - ['Receipt reconciliation', '`eth_getBlockByNumber(..., true)` — sum receipts client-side and confirm equality.'], - ['Receipt non-zero check', '`eth_getTransactionReceipt` — post-`v6.1.11` EVM transactions never return `gasUsed = 0x0`.'], - ['Estimator parity', '`eth_estimateGas` on pointer/Solo flows — succeeds without heavy manual padding.'] + ['Block sanity', '\`seid query block \` - ensure \`gas_used\` > 0 and matches RPC hash.'], + ['Receipt reconciliation', '\`eth_getBlockByNumber(..., true)\` - sum receipts client-side and confirm equality.'], + ['Receipt non-zero check', '\`eth_getTransactionReceipt\` - post-\`v6.1.11\` EVM transactions never return \`gasUsed = 0x0\`.'], + ['Estimator parity', '\`eth_estimateGas\` on pointer/Solo flows - succeeds without heavy manual padding.'] ]} /> @@ -65,10 +49,10 @@ Use the RPC config defaults from `evmrpc/config.go` as a baseline: @@ -89,9 +73,9 @@ Run this suite after upgrades or when deploying new indexer infra: sum(receipts)', 'Legacy block prior to `v6.1.11` or synthetic transaction included.', 'Accept mismatch for legacy heights; filter out `synthetic: true` logs when aggregating.'] + ['\`gasUsed = 0x0\` on fresh blocks', 'Node is still serving pre-\`v6.1.11\` receipts or RPC cache is stale.', 'Restart RPC service after upgrading to \`v6.1.11\`; invalidate any CDN cache that fronts the RPC.'], + ['\`eth_estimateGas\` fails on pointer calls', 'Pointer cache cold or Solo migration contract not yet deployed.', 'Call \`precompile-pointerview\` once before estimating, or ensure Solo migration contracts are deployed at the documented addresses.'], + ['Block \`gasUsed\` > sum(receipts)', 'Legacy block prior to \`v6.1.11\` or synthetic transaction included.', 'Accept mismatch for legacy heights; filter out \`synthetic: true\` logs when aggregating.'] ]} /> diff --git a/content/evm/rpc-gas-reference.mdx b/content/evm/rpc-gas-reference.mdx index 7b8406c1..2e838cf8 100644 --- a/content/evm/rpc-gas-reference.mdx +++ b/content/evm/rpc-gas-reference.mdx @@ -15,32 +15,34 @@ Use this page as the condensed cheat-sheet for gas-related settings and formulas ## Gas Formulas -| Scenario | Formula | Notes | -| :------------------ | :------------------------------------ | :------------------------------------------------------------------------- | -| Transaction receipt | `gasUsed = totalGasConsumed – refund` | Refund is capped at 20% of `gasLimit` in line with go-ethereum rules. | -| Block total | `block.gasUsed = Σ receipt.gasUsed` | Accurate for blocks produced after `v6.1.11`. Earlier heights may diverge. | -| Estimation buffer | `suggestedGas = estimate × 1.05` | Apply a 5% safety margin for user-facing UIs. | + ## Release Notes -- [`v6.1.11`](https://github.com/sei-protocol/sei-chain/releases/tag/v6.1.11) +- [v6.1.11](https://github.com/sei-protocol/sei-chain/releases/tag/v6.1.11) - Block `gasUsed` aligned with Tendermint execution (`sei-chain@1efdec1eb`). - Pointer and Solo precompile flows warmed in `eth_estimateGas`. - Synthetic logs tagged with `synthetic: true`. -- [`v1.15.7-sei-7`](https://github.com/sei-protocol/go-ethereum/releases/tag/v1.15.7-sei-7) +- [v1.15.7-sei-7](https://github.com/sei-protocol/go-ethereum/releases/tag/v1.15.7-sei-7) - Tracer length guard prevents malformed frames from panicking. -- [`v1.15.7-sei-6`](https://github.com/sei-protocol/go-ethereum/releases/tag/v1.15.7-sei-6) - - RPC now returns `{ error: { data: { trace } } }` when custom tracers panic. +- [v1.15.7-sei-6](https://github.com/sei-protocol/go-ethereum/releases/tag/v1.15.7-sei-6) + - RPC now returns error payload with trace data when custom tracers panic. ## Operational Tips diff --git a/content/evm/rpc-panic-faq.mdx b/content/evm/rpc-panic-faq.mdx index 90a81d16..3ada7dbb 100644 --- a/content/evm/rpc-panic-faq.mdx +++ b/content/evm/rpc-panic-faq.mdx @@ -9,31 +9,15 @@ import { TroubleshootingTable } from '../../mdx-components'; # Panic Handling FAQ -Sei users occasionally hit runtime panics—either from custom tracers, malformed transactions, or internal bugs. Recent releases focus on surfacing those panics cleanly instead of terminating the node. This FAQ explains how they look on the wire and what you should do when you see them. +Sei users occasionally hit runtime panics-either from custom tracers, malformed transactions, or internal bugs. Recent releases focus on surfacing those panics cleanly instead of terminating the node. This FAQ explains how they look on the wire and what you should do when you see them. This guidance is based on `sei-chain@1a1805cff`, `sei-chain@4fac51f02`, `sei-chain@bc47cc90f`, `sei-chain@70c633940`, and go-ethereum `v1.15.7-sei-6`/`-7`. ## Panic Surface Area - +- **Tracer runtime** - Custom JS tracers that throw now return `error.data.trace` instead of severing the connection; capture it for postmortems. +- **Go panics** - Core RPC wraps go-ethereum panics and returns a 5xx with stack info. Watch logs for `panic: recovered` entries. +- **Client resilience** - Back off with jitter on repeated panic responses and persist context for support tickets. ## Client-Side Symptoms diff --git a/content/evm/rpc-prioritizer.mdx b/content/evm/rpc-prioritizer.mdx index c39f9d41..b4b0d9ca 100644 --- a/content/evm/rpc-prioritizer.mdx +++ b/content/evm/rpc-prioritizer.mdx @@ -4,87 +4,31 @@ description: 'How Sei ranks transactions pre-CheckTx, how the ABCI hint works, a keywords: ['tx prioritizer', 'mempool', 'sei tendermint', 'priority hints'] --- -import { CardGrid, KeyValueTable, TroubleshootingTable } from '../../mdx-components'; +import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; # Transaction Prioritizer -Sei now ships a dedicated transaction prioritizer. Rather than scattering priority tweaks across Ante handlers, the app answers a focused ABCI call—`GetTxPriorityHint`—whenever Tendermint’s mempool nears capacity. This page nails down the priority tiers, the knobs exposed in `sei-chain` and `sei-tendermint@c6c5a8f3`, and the checks that prove the system is working. - -
-
-
Surface
-
`abci.GetTxPriorityHint`
-

Fires after utilisation exceeds `drop-utilisation-threshold` and returns the hint Tendermint uses to admit or reject the tx.

- - View `internal/mempool/mempool.go` - -
-
-
Surface
-
`SeiTxPrioritizer`
-

Stateless Cosmos/EVM prioritizer that inspects the tx, validates fees, and emits an `int64` hint without touching state.

- - View `app/prioritizer.go` - -
-
-
Tiering
-

- OraclePriority > EVMAssociatePriority > fee-derived Cosmos/EVM priority (capped at MaxPriority) keeps mission-critical flows at the front. -

- - View `app/prioritizer.go` - -
-
-
Version guard
-

Ships enabled in current releases; older binaries ignore the hook, so mixed-version networks continue to make progress.

- - View `app/app.go` - -
-
+Sei now ships a dedicated transaction prioritizer. Rather than scattering priority tweaks across Ante handlers, the app answers a focused ABCI call (`GetTxPriorityHint`) whenever Tendermint's mempool nears capacity. This page nails down the priority tiers, the knobs exposed in `sei-chain` and `sei-tendermint@c6c5a8f3`, and the checks that prove the system is working. + + \`EVMAssociatePriority\` > fee-derived priority keeps mission-critical flows in front.', '[\`app/prioritizer.go\`](https://github.com/sei-protocol/sei-chain/blob/6a5c937fa9737b1e3aa2288b9349d5a9f51c9d79/app/prioritizer.go#L99)'], + ['Version guard', 'Ships enabled in current releases; older binaries ignore the hook so mixed clusters still make progress.', '[\`app/app.go\`](https://github.com/sei-protocol/sei-chain/blob/6a5c937fa9737b1e3aa2288b9349d5a9f51c9d79/app/app.go#L990)'] + ]} +/> ## How the Hint Is Calculated -
-
-
`OraclePriority`
-

- Oracle votes (MsgAggregateExchangeRateVote) ride with math.MaxInt64 - 100, guaranteeing feeders clear the mempool immediately. -

- - View `app/prioritizer.go` - -
-
-
`EVMAssociatePriority`
-

- Unassociated evm_associate calls bypass congestion so UX wallets can link addresses without fee gymnastics. -

- - View `app/prioritizer.go` - -
-
-
Gas price normalisation
-

- Regular EVM traffic uses priority = gasPrice / priority_normalizer and clamps to MaxPriority so outliers can’t blow up the scale. -

- - View `app/prioritizer.go` - -
-
-
Cosmos fee fallback
-

- SDK txs reuse GetTxPriority, so whitelisted fee denoms and gas estimates map into the same priority lane. -

- - View `app/prioritizer.go` - -
-
+ The prioritizer is intentionally pure: it decodes the tx, validates sane fee caps, probes association status, and returns an `int64`. Panics are caught and logged, and the mempool falls back to priority `0` so attackers cannot halt CheckTx. @@ -94,10 +38,10 @@ When Tendermint’s mempool utilisation crosses `drop-utilisation-threshold`, th @@ -109,28 +53,14 @@ drop-priority-threshold = 0.20 # shed bottom 20% priority drop-priority-reservoir-size = 8192 # tune for memory vs accuracy ``` -With these values, once the mempool is ≥80% utilised, any new tx with priority below the sampled 20th percentile is rejected with `priority not high enough for mempool`. +With these values, once the mempool is ≥80% utilised, any new tx with priority below the sampled 20th percentile is rejected with \`priority not high enough for mempool\`. ## Operator Checklist -
-
-
Confirm hints flow
-

`seid debug mempool-stats` should show `priority_cutoff` moving under sustained load; if it’s flat, hints aren’t being sampled.

-
-
-
Grafana panel
-

Alert on `CheckTxMetDropUtilisationThreshold` and `CheckTxDroppedByPriorityHint` to spot accidental throttling.

-
-
-
Normalizer sanity
-

`sei q evm params priority-normalizer` must stay positive; lock it down in Terraform/Ansible defaults.

-
-
-
RPC parity
-

Compare `eth_pendingTransactions` with `seid debug mempool-stats`; a growing gap usually means clients are retrying after drops.

-
-
+- **Confirm hints flow**: `seid debug mempool-stats` should show `priority_cutoff` moving under sustained load; if it's flat, hints aren't being sampled. +- **Grafana panel**: Alert on `CheckTxMetDropUtilisationThreshold` and `CheckTxDroppedByPriorityHint` to spot accidental throttling. +- **Normalizer sanity**: `sei q evm params priority-normalizer` must stay positive; lock it down in Terraform/Ansible defaults. +- **RPC parity**: Compare `eth_pendingTransactions` with `seid debug mempool-stats`; a growing gap usually means clients are retrying after drops. ### CLI validation @@ -147,39 +77,25 @@ seid tx evm associate --from wallet --evm-address --gas-prices 1usei --ga ## Developer Considerations -- **SDK integration:** Clients may receive `priority not high enough for mempool` when bursting the chain. Surface this to end users with context (e.g., “bump gas price or retry later”). -- **Fee markets:** EIP-1559 style tips (`gasTipCap`) remain valid; the prioritizer only checks tip ≥ 0 and `gasFeeCap` ≥ base fee & minimum fee. -- **Panic-proof:** The prioritizer recovers from panics and logs `tx prioritizer panicked. Falling back on no priority`. Instrument alerting on this string. -- **Testing:** Unit suite (`app/prioritizer_test.go`) covers oracle, zero-fee, and multi-denom cases. Extend tests when adding new high-priority msg types. +- **SDK integration:** Clients may receive \`priority not high enough for mempool\` when bursting the chain. Surface this to end users with context (e.g., "bump gas price or retry later"). +- **Fee markets:** EIP-1559 style tips (\`gasTipCap\`) remain valid; the prioritizer only checks tip ≥ 0 and \`gasFeeCap\` ≥ base fee & minimum fee. +- **Panic-proof:** The prioritizer recovers from panics and logs \`tx prioritizer panicked. Falling back on no priority\`. Instrument alerting on this string. +- **Testing:** Unit suite (\`app/prioritizer_test.go\`) covers oracle, zero-fee, and multi-denom cases. Extend tests when adding new high-priority msg types. ## Troubleshooting ## Related Material - +- [Consensus & Mempool](/evm/rpc-consensus-overview) - ties the prioritizer into OCC execution, duplicate cache enforcement, and upgrade validation. +- [RPC Regression Coverage](/evm/rpc-regression-playbook) - extend load tests with priority-drop scenarios and trace guards. +- [Node Incident Playbooks](/node/node-incident-playbooks) - on-call runbooks for mempool back-pressure, including reservoir resets. +- [EVM Gas Accounting](/evm/rpc-gas-accounting) - shows how block-level `gasUsed` ties to priority decisions and RPC expectations. diff --git a/content/evm/rpc-synthetic-transactions.mdx b/content/evm/rpc-synthetic-transactions.mdx index e948da94..d4689754 100644 --- a/content/evm/rpc-synthetic-transactions.mdx +++ b/content/evm/rpc-synthetic-transactions.mdx @@ -9,7 +9,7 @@ import { KeyValueTable, TroubleshootingTable } from '../../mdx-components'; # Synthetic Transactions on Sei -Sei supports **synthetic** transactions—Cosmos-originated operations that are wrapped and surfaced through the EVM RPC layer. These include association flows, governance events, and distribution payouts. This guide explains how they appear through JSON-RPC and how to index them correctly after `v6.1.11`. +Sei supports **synthetic** transactions-Cosmos-originated operations that are wrapped and surfaced through the EVM RPC layer. These include association flows, governance events, and distribution payouts. This guide explains how they appear through JSON-RPC and how to index them correctly after `v6.1.11`. ## Key Properties @@ -47,38 +47,10 @@ Sei supports **synthetic** transactions—Cosmos-originated operations that are ## Indexing Strategy - +- **Dual namespace polling** - Query both `eth_getLogs` and `sei_getLogs` (or subscribe to the `sei_` channel) so synthetic envelopes and EVM events stay aligned. +- **Synthetic segregation** - Store synthetic logs in a dedicated table keyed by `{tx_hash, module}` for downstream reconciliation. +- **Cosmos cross-reference** - For enrichment, join against module-specific gRPC queries (distribution, governance) using the event payload. ## Interaction With Gas Accounting -- Synthetic envelopes do not alter receipt `gasUsed` (see `rpc-gas-accounting`). -- When computing block-level gas metrics, exclude synthetic logs entirely; they are informational signals only. -- Estimating gas for synthetic flows is unnecessary—`sei_associate` and similar methods are processed outside the EVM gas model. - -## Troubleshooting - - +- Synthetic envelopes do not alter receipt `gasUsed` (see `rpc-gas-accounting` diff --git a/content/evm/rpc-tracer-guide.mdx b/content/evm/rpc-tracer-guide.mdx index 9dec24aa..46ad4de8 100644 --- a/content/evm/rpc-tracer-guide.mdx +++ b/content/evm/rpc-tracer-guide.mdx @@ -13,25 +13,9 @@ Sei exposes the standard `debug_trace*` RPC suite and accepts the same JavaScrip ## Runtime Expectations - +- **Frame limits** - Keep serialized payloads below the enforced frame guard; chunk or stream large outputs. +- **Panic surfacing** - Errors thrown inside tracers appear in `error.data.trace`. Persist them for debugging. +- **Timeout & concurrency** - Respect `trace_timeout` (default 30s) and `max_concurrent_trace_calls` (default 10); excess calls queue. ## Authoring Patterns diff --git a/content/evm/rpc-websockets.mdx b/content/evm/rpc-websockets.mdx index 0e4af9bb..2e253544 100644 --- a/content/evm/rpc-websockets.mdx +++ b/content/evm/rpc-websockets.mdx @@ -33,25 +33,9 @@ Real-time event streams via WebSocket subscriptions (`eth_subscribe`) for `newHe ## Connection Management - +- **Heartbeat loop** - Send `eth_subscribe` keep-alives or lightweight RPC calls every ~20s to stop proxies reclaiming idle sockets. +- **Backoff strategy** - Reconnect with exponential backoff when the node drops the socket (capacity reached, idle timeout). Avoid thundering herds. +- **Replay window** - Persist the last processed block and resubscribe with a bounded `fromBlock` range to cover gaps. ## Replay & Gap Handling diff --git a/content/evm/sei-global-wallet.mdx b/content/evm/sei-global-wallet.mdx index db15b699..39fb23d0 100644 --- a/content/evm/sei-global-wallet.mdx +++ b/content/evm/sei-global-wallet.mdx @@ -10,13 +10,13 @@ import { Callout, Tabs } from 'nextra/components'; By logging in with Sei Global Wallet, you agree to our [Terms of Use](https://app.sei.io/terms) -Sei Global Wallet is a cross-application embedded crypto wallet powered by [Dynamic Global Wallets](https://docs.dynamic.xyz/global-wallets/overview). It gives users a persistent wallet experience across any integrated app within the Sei ecosystem, allowing them to authenticate and interact using familiar login methods like Google, Twitter, or Telegram—no browser extension or prior crypto knowledge required. +Sei Global Wallet is a cross-application embedded crypto wallet powered by [Dynamic Global Wallets](https://docs.dynamic.xyz/global-wallets/overview). It gives users a persistent wallet experience across any integrated app within the Sei ecosystem, allowing them to authenticate and interact using familiar login methods like Google, Twitter, or Telegram-no browser extension or prior crypto knowledge required. Additional auth methods such as existing wallet apps (Metamask, ..) are possible through Dynamic, but are not currently enabled ## What is Sei Global Wallet? -Unlike traditional browser extension wallets, Sei Global Wallet is built directly into web applications and allows users to authenticate using familiar methods like Google, Twitter, Telegram, or email—no downloads or crypto knowledge required. +Unlike traditional browser extension wallets, Sei Global Wallet is built directly into web applications and allows users to authenticate using familiar methods like Google, Twitter, Telegram, or email-no downloads or crypto knowledge required. ## Why Use Sei Global Wallet? diff --git a/content/evm/solidity-resources.mdx b/content/evm/solidity-resources.mdx index f55ef9f4..c11eb8b4 100644 --- a/content/evm/solidity-resources.mdx +++ b/content/evm/solidity-resources.mdx @@ -53,7 +53,7 @@ This page suggests a minimal set of resources for getting started with building - **[Rareskills Blog](https://rareskills.io/blog)** Features in-depth articles on various Solidity concepts and their [Book of Gas Optimization](https://www.rareskills.io/post/gas-optimization). - **[Foundry Fundamentals course by Cyfrin Updraft](https://updraft.cyfrin.io/courses/foundry)** - A comprehensive web3 development course covering Foundry—the industry-standard framework for building, deploying, and testing smart contracts. + A comprehensive web3 development course covering Foundry-the industry-standard framework for building, deploying, and testing smart contracts. - **[Smart Contract Programmer YT channel](https://www.youtube.com/@smartcontractprogrammer)** Contains many in-depth videos covering topics from ABI encoding to EVM memory management. - **[DeFi developer roadmap](https://github.com/OffcierCia/DeFi-Developer-Road-Map)** diff --git a/content/evm/tracing-javascript-tracers.mdx b/content/evm/tracing-javascript-tracers.mdx index 9dc03ffa..b669c6dd 100644 --- a/content/evm/tracing-javascript-tracers.mdx +++ b/content/evm/tracing-javascript-tracers.mdx @@ -233,7 +233,7 @@ Identify expensive operations for gas optimization: ### Memory-Efficient Patterns ```javascript -// ✅ Efficient - Use summary data +// Efficient - Use summary data { "tracer": `{ summary: { diff --git a/content/evm/tracing-playbook.mdx b/content/evm/tracing-playbook.mdx index 21408043..845fec41 100644 --- a/content/evm/tracing-playbook.mdx +++ b/content/evm/tracing-playbook.mdx @@ -29,7 +29,7 @@ Recommended tuning: - Prefer block ranges under 500 for iterative analysis; pipeline requests if you need full-day coverage. - Set `tracerConfig.timeout` inside your request to a value `<=` cluster timeout; the lower of the two values wins. - Use `disableStorage` and `disableStack` flags when you only need execution logs to reduce payload size. -- New frame length guard means stringifying massive intermediate objects will revert—log hashes or sample slices instead. +- New frame length guard means stringifying massive intermediate objects will revert-log hashes or sample slices instead. ## JS Tracers diff --git a/content/evm/tracing-troubleshooting.mdx b/content/evm/tracing-troubleshooting.mdx index 4e9e4b94..f228b0a6 100644 --- a/content/evm/tracing-troubleshooting.mdx +++ b/content/evm/tracing-troubleshooting.mdx @@ -101,7 +101,7 @@ This guide covers common issues encountered when using EVM transaction tracing o }` } -// ✅ Correct - Proper quotes +// Correct - Proper quotes { "tracer": `{ step: function(log, db) { @@ -231,7 +231,7 @@ curl -X POST -H "Content-Type: application/json" \ }` } -// ✅ Efficient - Direct assignment +// Efficient - Direct assignment { "tracer": `{ operations: {}, @@ -375,7 +375,7 @@ const storageTracer = `{ ## Best Practices Summary -### ✅ Do's +### Do's - **Start Simple**: Begin with built-in tracers before custom JavaScript - **Set Timeouts**: Always configure appropriate timeouts diff --git a/content/evm/usdc-on-sei.md b/content/evm/usdc-on-sei.md index 9a0e3aba..9fccbf65 100644 --- a/content/evm/usdc-on-sei.md +++ b/content/evm/usdc-on-sei.md @@ -8,7 +8,7 @@ USDC is a digital dollar issued by [Circle](http://developers.circle.com), also known as a stablecoin, running on many of the world's leading blockchains. Designed to represent US dollars on the internet, USDC is backed 100% by highly liquid cash and cash-equivalent assets so that it's always redeemable 1:1 for USD. -On the **Sei**, USDC can be transferred like any standard ERC-20 token — enabling fast, secure, and programmable digital dollar transactions. +On the **Sei**, USDC can be transferred like any standard ERC-20 token - enabling fast, secure, and programmable digital dollar transactions. This guide walks you through building a standalone index.js script using **Viem** and **Node.js** to check your USDC balance and send a test transfer to another address. @@ -217,5 +217,5 @@ Continue building by checking for more information in the [Circle Developer Docs - Testnet Only: Sei testnet USDC has no real value. Don’t use mainnet keys or expect real funds. - Security: Store private keys in `.env`; never commit secrets. Use best practices for key management. - Gas Fees: You’ll need a small amount of testnet SEI to cover gas. -- Lightweight ABI: Only `balanceOf` and `transfer` are used — enough for simple transfers. +- Lightweight ABI: Only `balanceOf` and `transfer` are used - enough for simple transfers. - Viem Behavior: `readContract` and `writeContract` handle reads/writes. The private key is auto-prefixed with `0x`. diff --git a/content/learn/dev-chains.mdx b/content/learn/dev-chains.mdx index e3c0c272..8080fcfe 100644 --- a/content/learn/dev-chains.mdx +++ b/content/learn/dev-chains.mdx @@ -57,9 +57,7 @@ control over all tokens and governance decisions. Please read the [Nodes Introduction](/node) section for more information on how to set up and run a local chain. - - TIP: View more chain specific information, RPC endpoints, and Explorers for the [EVM](/evm/networks). - +TIP: View more chain specific information, RPC endpoints, and Explorers for the [EVM](/evm/networks). ## Chain Registry diff --git a/content/learn/explorers.mdx b/content/learn/explorers.mdx index dbcc35e2..adbe3d2c 100644 --- a/content/learn/explorers.mdx +++ b/content/learn/explorers.mdx @@ -37,9 +37,7 @@ with the network including: with other wallets or addresses. Some may include the raw code for the contract. - - Note: In rare cases, some information may be misinterpreted. Off-chain data should be used as a guide only. - +Note: In rare cases, some information may be misinterpreted. Off-chain data should be used as a guide only. ## Sei Explorers diff --git a/content/learn/parallelization-engine.mdx b/content/learn/parallelization-engine.mdx index 87071b4f..120f1955 100644 --- a/content/learn/parallelization-engine.mdx +++ b/content/learn/parallelization-engine.mdx @@ -1,7 +1,7 @@ --- -title: "Sei Parallelization Engine: Multi-core Blockchain Execution" +title: 'Sei Parallelization Engine: Multi-core Blockchain Execution' description: "Learn how Sei's Parallelization Engine enables simultaneous transaction processing across multiple CPU cores, delivering higher throughput while maintaining safety and consistency guarantees." -keywords: ["parallelization engine", "multi-core processing", "blockchain concurrency", "transaction throughput", "optimistic execution", "dai algorithm"] +keywords: ['parallelization engine', 'multi-core processing', 'blockchain concurrency', 'transaction throughput', 'optimistic execution', 'dai algorithm'] --- import { Callout } from 'nextra/components'; @@ -191,7 +191,7 @@ The dependency analyzer might also consider secondary effects such as: The dependency generator outputs an estimated read/write set for each transaction. These sets contain the state keys expected to be accessed, with additional metadata indicating access types (read vs. write) and confidence levels for each prediction. The system uses these confidence levels during scheduling decisions to minimize the likelihood of conflicts. -The output from this phase consists of an "estimated writeset" for each transaction—essentially a prediction of its state impact—which serves as crucial input for the next execution planning phase. +The output from this phase consists of an "estimated writeset" for each transaction-essentially a prediction of its state impact-which serves as crucial input for the next execution planning phase. ### 3. Parallel Execution diff --git a/content/node/index.mdx b/content/node/index.mdx index 3fe257fe..03d17de1 100644 --- a/content/node/index.mdx +++ b/content/node/index.mdx @@ -59,9 +59,7 @@ import Link from 'next/link'; ### Hardware Requirements - - For best performance, run `seid` directly on bare metal hardware (no virtualization or containers). Running `seid` under Docker or Kubernetes for example can significantly degrade performance and cause your node to consistenly fall behind the chain tip. - +For best performance, run `seid` directly on bare metal hardware (no virtualization or containers). Running `seid` under Docker or Kubernetes for example can significantly degrade performance and cause your node to consistenly fall behind the chain tip.
@@ -444,7 +442,7 @@ pending-ttl-num-blocks = 5
Setting Up a systemd Service
- If you see an error such as `panic: recovered: runtime error: integer divide by zero` it means you can’t start nodes straight from the genesis file. Instead, sync to the block tip via [state sync](/node/statesync) or using a [snapshot](/node/snapshot). + If you see an error such as `panic: recovered: runtime error: integer divide by zero` it means you can’t start nodes straight from the genesis file. Instead, sync to the block tip via [state sync](/node/statesync) or using a [snapshot](/node/snapshot).

For production deployments, configure a systemd service to ensure your node restarts automatically:

diff --git a/content/node/snapshot.mdx b/content/node/snapshot.mdx index 67eb8e7e..c8d238ce 100644 --- a/content/node/snapshot.mdx +++ b/content/node/snapshot.mdx @@ -64,7 +64,7 @@ You can find the guide to download and use the snapshots [here](https://seistrea -## Option 1 — Polkachu (recommended for most operators) +## Option 1 - Polkachu (recommended for most operators) ### Prerequisites @@ -142,7 +142,7 @@ Optional: Remove the snapshot file to free up space: rm sei_snapshot.tar.lz4 ``` -## Option 2 — StakeMe Snapshots +## Option 2 - StakeMe Snapshots ### System Dependencies & Configuration @@ -181,7 +181,7 @@ rm $HOME/cosmos_data_sei_20250813_220001.tar.lz4 sudo journalctl -u seid -f --no-hostname -o cat ``` -## Option 3 — CryptoCrew Full Archive +## Option 3 - CryptoCrew Full Archive ### Prerequisites diff --git a/content/node/technical-reference.mdx b/content/node/technical-reference.mdx index 859b5fae..c2f0ccdc 100644 --- a/content/node/technical-reference.mdx +++ b/content/node/technical-reference.mdx @@ -23,9 +23,7 @@ troubleshooting. These commands help you control and monitor your node's operation: - - If you see an error such as `panic: recovered: runtime error: integer divide by zero` it means you can’t start nodes straight from the genesis file. Instead, sync to the block tip via [state sync](/node/statesync) or using a [snapshot](/node/snapshot). - +If you see an error such as `panic: recovered: runtime error: integer divide by zero` it means you can’t start nodes straight from the genesis file. Instead, sync to the block tip via [state sync](/node/statesync) or using a [snapshot](/node/snapshot). ```bash copy # Start the node diff --git a/content/node/validator-operations-playbook.mdx b/content/node/validator-operations-playbook.mdx index 3c3bff90..e1236859 100644 --- a/content/node/validator-operations-playbook.mdx +++ b/content/node/validator-operations-playbook.mdx @@ -26,7 +26,7 @@ This runbook collects the key operational tasks for maintaining a production Sei