diff --git a/.github/workflows/sync-evm-changelog.yml b/.github/workflows/sync-evm-changelog.yml index 07098760..94447309 100644 --- a/.github/workflows/sync-evm-changelog.yml +++ b/.github/workflows/sync-evm-changelog.yml @@ -37,13 +37,16 @@ jobs: echo "release_tag=$RELEASE_TAG" >> $GITHUB_OUTPUT - # Fetch the CHANGELOG.md from the EVM repo - curl -s "https://raw.githubusercontent.com/cosmos/evm/$RELEASE_TAG/CHANGELOG.md" > /tmp/changelog.md + # Fetch the entire CHANGELOG.md from the EVM repo main branch + # We always pull from main to get the complete changelog + curl -s "https://raw.githubusercontent.com/cosmos/evm/main/CHANGELOG.md" > /tmp/changelog.md if [ ! -s /tmp/changelog.md ]; then echo "Failed to fetch changelog or changelog is empty" exit 1 fi + + echo "Successfully fetched changelog ($(wc -l < /tmp/changelog.md) lines)" - name: Parse and convert changelog id: convert @@ -51,313 +54,231 @@ jobs: cat << 'EOF' > parse_changelog.js const fs = require('fs'); - function parseChangelog(content, releaseTag, initMode = false) { + function parseFullChangelog(content) { const lines = content.split('\n'); - let inUnreleasedSection = false; - let currentContent = []; + const versions = []; + let currentVersion = null; + let currentDate = null; let currentCategory = null; let categories = {}; - let allVersions = []; - - // For init mode, just parse UNRELEASED as the initial version - if (initMode) { - const result = parseChangelog(content, releaseTag, false); - if (result.hasContent) { - return { - allVersions: [{ - version: releaseTag, - date: new Date().toISOString().split('T')[0], - categories: result.categories - }], - hasContent: true - }; - } - return { allVersions: [], hasContent: false }; - } - - // Original single version parsing for regular updates + for (let i = 0; i < lines.length; i++) { - const line = lines[i].trim(); - - // Check for UNRELEASED section - skip this line entirely - if (line === '## UNRELEASED') { - inUnreleasedSection = true; + const line = lines[i]; + const trimmedLine = line.trim(); + + // Match version headers like "## [v0.3.1] - 2024-10-11" or "## v0.3.1 - 2024-10-11" + const versionMatch = trimmedLine.match(/^##\s+\[?(v[\d\.\-\w]+)\]?\s*(?:-\s*(.+))?$/); + + if (versionMatch) { + // Save previous version if exists + if (currentVersion && Object.keys(categories).length > 0) { + versions.push({ + version: currentVersion, + date: currentDate || new Date().toISOString().split('T')[0], + categories: { ...categories } + }); + } + + // Start new version + currentVersion = versionMatch[1]; + currentDate = versionMatch[2] || null; + + // Try to extract date if it's in format "2024-10-11" or similar + if (currentDate) { + const dateMatch = currentDate.match(/(\d{4}-\d{2}-\d{2})/); + if (dateMatch) { + currentDate = dateMatch[1]; + } + } + + categories = {}; + currentCategory = null; continue; } - - // If we hit another ## section after UNRELEASED, we're done - if (inUnreleasedSection && line.startsWith('## ') && line !== '## UNRELEASED') { - break; + + // Skip UNRELEASED section + if (trimmedLine === '## UNRELEASED') { + // Save previous version before skipping + if (currentVersion && Object.keys(categories).length > 0) { + versions.push({ + version: currentVersion, + date: currentDate || new Date().toISOString().split('T')[0], + categories: { ...categories } + }); + } + currentVersion = null; + categories = {}; + currentCategory = null; + continue; } - - // Look for category headers (### CATEGORY) - if (inUnreleasedSection && line.startsWith('### ')) { - currentCategory = line.replace('### ', '').trim(); - categories[currentCategory] = []; + + // Match category headers (### FEATURES, ### BUG FIXES, etc.) + if (currentVersion && trimmedLine.startsWith('### ')) { + currentCategory = trimmedLine.replace('### ', '').trim(); + if (!categories[currentCategory]) { + categories[currentCategory] = []; + } continue; } - - // Collect content under each category - if (inUnreleasedSection && currentCategory && line && !line.startsWith('#')) { - categories[currentCategory].push(line); + + // Collect items under categories + if (currentVersion && currentCategory && trimmedLine && !trimmedLine.startsWith('#')) { + // Skip separator lines and empty content + if (trimmedLine !== '---' && trimmedLine !== '___' && trimmedLine !== '***') { + categories[currentCategory].push(trimmedLine); + } } } - - return { - categories, - hasContent: Object.keys(categories).length > 0 - }; - } - - function convertToMintlifyUpdate(changelogData, releaseTag, initMode = false) { - if (initMode) { - const { allVersions, hasContent } = changelogData; - - if (!hasContent) { - return ''; - } - - let allUpdates = ''; - - allVersions.forEach(versionData => { - const { version, date, categories } = versionData; - let processedContent = ''; - - // Define the order we want to display categories - const categoryOrder = [ - 'FEATURES', - 'IMPROVEMENTS', - 'BUG FIXES', - 'DEPENDENCIES', - 'STATE BREAKING', - 'API-Breaking' - ]; - - // Process categories in preferred order - categoryOrder.forEach(category => { - if (categories[category] && categories[category].length > 0) { - processedContent += `## ${category.toLowerCase().replace(/[-_]/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}\n\n`; - - categories[category].forEach(item => { - if (item.trim()) { - let cleanItem = item - .replace(/^[\-\*] /, '* ') - .replace(/\\\[/g, '[') - .replace(/\\\]/g, ']'); - - processedContent += `${cleanItem}\n`; - } - }); - - processedContent += '\n'; - } - }); - - // Add any remaining categories not in our predefined order - Object.keys(categories).forEach(category => { - if (!categoryOrder.includes(category) && categories[category].length > 0) { - processedContent += `## ${category.toLowerCase().replace(/[-_]/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}\n\n`; - - categories[category].forEach(item => { - if (item.trim()) { - let cleanItem = item - .replace(/^[\-\*] /, '* ') - .replace(/\\\[/g, '[') - .replace(/\\\]/g, ']'); - - processedContent += `${cleanItem}\n`; - } - }); - - processedContent += '\n'; - } - }); - - if (processedContent.trim()) { - allUpdates += ` - ${processedContent.trim()} - - - `; - } + + // Save last version if exists + if (currentVersion && Object.keys(categories).length > 0) { + versions.push({ + version: currentVersion, + date: currentDate || new Date().toISOString().split('T')[0], + categories: { ...categories } }); - - return allUpdates; } + + return versions; + } - // Regular single update processing - const { categories, hasContent } = changelogData; + function convertToMintlify(versions) { + let mdxContent = `--- +title: "Changelog" +description: "Follows the \\\`CHANGELOG.md\\\` for \\\`cosmos/evm\\\`" +--- - if (!hasContent) { - throw new Error('No unreleased changes found in changelog'); - } +# Changelog + +This page tracks all releases and changes to the Cosmos EVM module. - let processedContent = ''; +`; // Define the order we want to display categories const categoryOrder = [ 'FEATURES', - 'IMPROVEMENTS', + 'IMPROVEMENTS', 'BUG FIXES', 'DEPENDENCIES', 'STATE BREAKING', + 'API BREAKING', 'API-Breaking' ]; - // Process categories in preferred order - categoryOrder.forEach(category => { - if (categories[category] && categories[category].length > 0) { - processedContent += `## ${category.toLowerCase().replace(/[-_]/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}\n\n`; - - categories[category].forEach(item => { - if (item.trim()) { - // Clean up the bullet points and links - let cleanItem = item - .replace(/^[\-\*] /, '* ') - .replace(/\\\[/g, '[') - .replace(/\\\]/g, ']'); - - processedContent += `${cleanItem}\n`; - } - }); - - processedContent += '\n'; - } - }); - - // Add any remaining categories not in our predefined order - Object.keys(categories).forEach(category => { - if (!categoryOrder.includes(category) && categories[category].length > 0) { - processedContent += `## ${category.toLowerCase().replace(/[-_]/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}\n\n`; - - categories[category].forEach(item => { - if (item.trim()) { - let cleanItem = item - .replace(/^[\-\*] /, '* ') - .replace(/\\\[/g, '[') - .replace(/\\\]/g, ']'); - - processedContent += `${cleanItem}\n`; - } - }); - - processedContent += '\n'; + // Process each version + versions.forEach(({ version, date, categories }) => { + let updateContent = ''; + + // Process categories in preferred order + categoryOrder.forEach(category => { + const matchingCategory = Object.keys(categories).find( + cat => cat.toUpperCase().replace(/[-_]/g, ' ') === category.replace(/[-_]/g, ' ') + ); + + if (matchingCategory && categories[matchingCategory].length > 0) { + // Format category title + const formattedCategory = category + .toLowerCase() + .replace(/[-_]/g, ' ') + .replace(/\b\w/g, l => l.toUpperCase()); + + updateContent += `## ${formattedCategory}\n\n`; + + categories[matchingCategory].forEach(item => { + if (item.trim()) { + // Clean up bullet points and ensure consistent formatting + let cleanItem = item + .replace(/^[\-\*]\s*/, '') // Remove existing bullet + .replace(/\\\[/g, '[') // Fix escaped brackets + .replace(/\\\]/g, ']'); + + updateContent += `* ${cleanItem}\n`; + } + }); + + updateContent += '\n'; + } + }); + + // Add any remaining categories not in our predefined order + Object.keys(categories).forEach(category => { + const normalizedCategory = category.toUpperCase().replace(/[-_]/g, ' '); + const isProcessed = categoryOrder.some( + ordered => ordered.replace(/[-_]/g, ' ') === normalizedCategory + ); + + if (!isProcessed && categories[category].length > 0) { + const formattedCategory = category + .toLowerCase() + .replace(/[-_]/g, ' ') + .replace(/\b\w/g, l => l.toUpperCase()); + + updateContent += `## ${formattedCategory}\n\n`; + + categories[category].forEach(item => { + if (item.trim()) { + let cleanItem = item + .replace(/^[\-\*]\s*/, '') + .replace(/\\\[/g, '[') + .replace(/\\\]/g, ']'); + + updateContent += `* ${cleanItem}\n`; + } + }); + + updateContent += '\n'; + } + }); + + // Only add the Update component if there's content + if (updateContent.trim()) { + mdxContent += ` +${updateContent.trim()} + + +`; } }); - - // Get current date for the label - const currentDate = new Date().toISOString().split('T')[0]; - - const updateComponent = ` - ${processedContent.trim()} - - - `; - - return updateComponent; + + return mdxContent; } // Main execution try { const changelogContent = fs.readFileSync('/tmp/changelog.md', 'utf8'); - const releaseTag = process.argv[2]; - const initMode = process.argv[3] === 'init'; - - const parsedData = parseChangelog(changelogContent, releaseTag, initMode); - const mintlifyUpdate = convertToMintlifyUpdate(parsedData, releaseTag, initMode); - - fs.writeFileSync('/tmp/update_component.mdx', mintlifyUpdate); + const versions = parseFullChangelog(changelogContent); + + console.log(`Parsed ${versions.length} versions from changelog`); + + if (versions.length === 0) { + console.error('No versions found in changelog'); + process.exit(1); + } + + const mintlifyContent = convertToMintlify(versions); + fs.writeFileSync('/tmp/release-notes.mdx', mintlifyContent); + console.log('Successfully converted changelog to Mintlify format'); - + } catch (error) { console.error('Error:', error.message); process.exit(1); } EOF - # Check if this is initialization mode (changelog file doesn't exist) - CHANGELOG_FILE="docs/changelog/release-notes.mdx" - if [ ! -f "$CHANGELOG_FILE" ]; then - echo "Initializing changelog with all previous versions..." - node parse_changelog.js "${{ steps.fetch-changelog.outputs.release_tag }}" init - else - echo "Updating existing changelog with new release..." - node parse_changelog.js "${{ steps.fetch-changelog.outputs.release_tag }}" - fi + node parse_changelog.js - name: Update changelog file run: | CHANGELOG_FILE="docs/changelog/release-notes.mdx" - UPDATE_CONTENT=$(cat /tmp/update_component.mdx) - - # Check if the changelog file exists - if [ ! -f "$CHANGELOG_FILE" ]; then - echo "Creating new changelog file with all historical versions" - # Create the directory if it doesn't exist - mkdir -p "$(dirname "$CHANGELOG_FILE")" - - # Create the file with proper YAML front matter using printf - printf '%s\n' '---' > "$CHANGELOG_FILE" - printf '%s\n' 'title: "Changelog"' >> "$CHANGELOG_FILE" - printf '%s\n' 'description: "Follows the \`CHANGELOG.md\` for \`cosmos/evm\`"' >> "$CHANGELOG_FILE" - printf '%s\n' '---' >> "$CHANGELOG_FILE" - printf '\n' >> "$CHANGELOG_FILE" - printf '%s\n' '# Changelog' >> "$CHANGELOG_FILE" - printf '\n' >> "$CHANGELOG_FILE" - printf '%s\n' 'This page tracks all releases and changes to the Cosmos EVM module.' >> "$CHANGELOG_FILE" - printf '\n' >> "$CHANGELOG_FILE" - - # Append the update content - cat /tmp/update_component.mdx >> "$CHANGELOG_FILE" - else - echo "Updating existing changelog with new release..." - # Find the insertion point (after the front matter and title) - # Insert the new update at the top of the changelog entries - awk -v update="$UPDATE_CONTENT" ' - BEGIN { found_header = 0; found_intro = 0; inserted = 0 } - - # Skip frontmatter - /^---$/ && NR == 1 { in_frontmatter = 1; print; next } - /^---$/ && in_frontmatter { in_frontmatter = 0; print; next } - in_frontmatter { print; next } - - # Find the main title - /^# / && !found_header { - print - found_header = 1 - next - } - - # After title, look for intro text or existing Update - found_header && !inserted { - if (/^This page tracks/ || /^$/) { - print - if (/^This page tracks/) found_intro = 1 - if (found_intro && /^$/) { - print update - inserted = 1 - } - } else if (/^ /tmp/updated_changelog.mdx - - mv /tmp/updated_changelog.mdx "$CHANGELOG_FILE" - fi + + # Create directory if it doesn't exist + mkdir -p "$(dirname "$CHANGELOG_FILE")" + + # Replace the entire file with the newly generated content + cp /tmp/release-notes.mdx "$CHANGELOG_FILE" + + echo "Changelog updated with $(grep -c ' + + Learn about account structure in Cosmos SDK + + + Understand Ethereum account model + + -## Creating Accounts[​](#creating-accounts "Direct link to Creating Accounts") +## Creating Accounts To create one account you can either create a private key, a keystore file (a private key protected by a password), or a mnemonic phrase (a string of words that can access multiple private keys). @@ -18,7 +24,7 @@ Cosmos blockchains, like the Cosmos Hub, support creating accounts with mnemonic HD keys generate addresses by taking the mnemonic phrase and combining it with a piece of information called a [derivation path](https://learnmeabitcoin.com/technical/derivation-paths). Blockchains can differ in which derivation path they support. To access all accounts from an mnemonic phrase on a blockchain, it is therefore important to use that blockchain's specific derivation path. -## Representing Accounts[​](#representing-accounts "Direct link to Representing Accounts") +## Representing Accounts The terms "account" and "address" are often used interchangeably to describe crypto wallets. In the Cosmos SDK, an account designates a pair of public key (PubKey) and private key (PrivKey). The derivation path defines what the private key, public key, and address would be. @@ -26,7 +32,7 @@ The PubKey can be derived to generate various addresses in different formats, wh The PrivKey is used to generate digital signatures to prove that an address associated with the PrivKey approved of a given message. The proof is performed by applying a cryptographic scheme to the PrivKey, known as Elliptic Curve Digital Signature Algorithm (ECDSA), to generate a PubKey that is compared with the address in the message. -## EVM Accounts[​](#evm-accounts "Direct link to EVM Accounts") +## EVM Accounts Cosmos EVM defines its own custom `Account` type to implement a HD wallet that is compatible with Ethereum type addresses. It uses Ethereum's ECDSA secp256k1 curve for keys (`eth_secp265k1`) and satisfies the [EIP84](https://github.com/ethereum/EIPs/issues/84) for full [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) paths. This cryptographic curve is not to be confused with [Bitcoin's ECDSA secp256k1](https://en.bitcoin.it/wiki/Secp256k1) curve. @@ -40,95 +46,192 @@ The custom Cosmos EVM [EthAccount](https://github.com/cosmos/evm/blob/main/types For more information on Ethereum accounts head over to the [x/evm module](/docs/documentation/cosmos-sdk/modules/vm#concepts). -### Addresses and Public Keys[​](#addresses-and-public-keys "Direct link to Addresses and Public Keys") +### Addresses and Public Keys [BIP-0173](https://github.com/satoshilabs/slips/blob/master/slip-0173.md) defines a new format for segregated witness output addresses that contains a human-readable part that identifies the Bech32 usage. There are 3 main types of HRP for the `Addresses`/`PubKeys` available by default on the Cosmos EVM: -* Addresses and Keys for **accounts**, which identify users (e.g. the sender of a `message`). They are derived using the **`eth_secp256k1`** curve. -* Addresses and Keys for **validator operators**, which identify the operators of validators. They are derived using the **`eth_secp256k1`** curve. -* Addresses and Keys for **consensus nodes**, which identify the validator nodes participating in consensus. They are derived using the **`ed25519`** curve. - -| | Address bech32 Prefix | Pubkey bech32 Prefix | Curve | Address byte length | Pubkey byte length | -| ------------------ | --------------------- | -------------------- | --------------- | ------------------- | ------------------ | -| Accounts | `cosmos` | `cosmospub` | `eth_secp256k1` | `20` | `33` (compressed) | -| Validator Operator | `cosmosvaloper` | `cosmosvaloperpub` | `eth_secp256k1` | `20` | `33` (compressed) | -| Consensus Nodes | `cosmosvalcons` | `cosmosvalconspub` | `ed25519` | `20` | `32` | - -### Address formats for clients[​](#address-formats-for-clients "Direct link to Address formats for clients") + + + **Purpose**: Identify users (e.g., transaction senders) + - **Curve**: `eth_secp256k1` + - **Address Prefix**: `cosmos` + - **Pubkey Prefix**: `cosmospub` + - **Address Length**: 20 bytes + - **Pubkey Length**: 33 bytes (compressed) + + + + **Purpose**: Identify validator operators + - **Curve**: `eth_secp256k1` + - **Address Prefix**: `cosmosvaloper` + - **Pubkey Prefix**: `cosmosvaloperpub` + - **Address Length**: 20 bytes + - **Pubkey Length**: 33 bytes (compressed) + + + + **Purpose**: Identify nodes participating in consensus + - **Curve**: `ed25519` + - **Address Prefix**: `cosmosvalcons` + - **Pubkey Prefix**: `cosmosvalconspub` + - **Address Length**: 20 bytes + - **Pubkey Length**: 32 bytes + + + +### Address formats for clients `EthAccount` can be represented in both [Bech32](https://en.bitcoin.it/wiki/Bech32) (e.g. `cosmos1...`) and hex (`0x...`) formats for Ethereum's Web3 tooling compatibility. -The Bech32 format is the default format for Cosmos-SDK queries and transactions through CLI and REST clients. The hex format on the other hand, is the Ethereum `common.Address` representation of a Cosmos `sdk.AccAddress`. - -* **Address (Bech32)**: `cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw` -* **Address ([EIP55](https://eips.ethereum.org/EIPS/eip-55) Hex)**: `0x91defC7fE5603DFA8CC9B655cF5772459BF10c6f` -* **Compressed Public Key**: `{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"AsV5oddeB+hkByIJo/4lZiVUgXTzNfBPKC73cZ4K1YD2"}` - -### Address conversion[​](#address-conversion "Direct link to Address conversion") + +The Bech32 format is the default for Cosmos-SDK queries and transactions, while the hex format is used for Ethereum compatibility. + -The `appd debug addr
` can be used to convert an address between hex and bech32 formats. For example: + -Bech32 - -``` - $ appd debug addr cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw Address: [20 87 74 109 255 45 223 158 7 130 139 67 69 211 4 9 25 175 86 82] Address (hex): 14574A6DFF2DDF9E07828B4345D3040919AF5652 Bech32 Acc: cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw Bech32 Val: cosmosvaloper1z3t55m0l9h0eupuz3dp5t5cypyv674jjn4d6nn +```text Bech32 Address +cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw ``` -Hex - -``` - $ cosmosd debug addr 14574A6DFF2DDF9E07828B4345D3040919AF5652 Address: [20 87 74 109 255 45 223 158 7 130 139 67 69 211 4 9 25 175 86 82] Address (hex): 14574A6DFF2DDF9E07828B4345D3040919AF5652 Bech32 Acc: cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw Bech32 Val: cosmosvaloper1z3t55m0l9h0eupuz3dp5t5cypyv674jjn4d6nn -``` - -### Key output[​](#key-output "Direct link to Key output") - - - The Cosmos SDK Keyring output (i.e `appd keys`) only supports addresses and public keys in Bech32 format. - - -We can use the `keys show` command of `appd` with the flag `--bech (acc|val|cons)` to obtain the addresses and keys as mentioned above, - -Accounts - +```text EIP-55 Hex Address +0x91defC7fE5603DFA8CC9B655cF5772459BF10c6f ``` - $ appd keys show dev0 --bech acc- name: dev0 type: local address: cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"AsV5oddeB+hkByIJo/4lZiVUgXTzNfBPKC73cZ4K1YD2"}' mnemonic: "" -``` - -Validator -``` - $ appd keys show dev0 --bech val- name: dev0 type: local address: cosmosvaloper1z3t55m0l9h0eupuz3dp5t5cypyv674jjn4d6nn pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"AsV5oddeB+hkByIJo/4lZiVUgXTzNfBPKC73cZ4K1YD2"}' mnemonic: "" +```json Public Key +{ + "@type": "/ethermint.crypto.v1.ethsecp256k1.PubKey", + "key": "AsV5oddeB+hkByIJo/4lZiVUgXTzNfBPKC73cZ4K1YD2" +} ``` -Consensus + -``` - $ appd keys show dev0 --bech cons- name: dev0 type: local address: cosmosvalcons1rllqa5d97n6zyjhy6cnscc7zu30zjn3f7wyj2n pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"A/fVLgIqiLykFQxum96JkSOoTemrXD0tFaFQ1B0cpB2c"}' mnemonic: "" -``` +### Address conversion -## Querying an Account[​](#querying-an-account "Direct link to Querying an Account") +The `appd debug addr
` can be used to convert an address between hex and bech32 formats: -You can query an account address using the CLI, gRPC or + -### Command Line Interface[​](#command-line-interface "Direct link to Command Line Interface") +```bash Bech32 to Hex +$ appd debug addr cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw -``` -# NOTE: the --output (-o) flag will define the output format in JSON or YAML (text)appd q auth account $(appd keys show dev0 -a) -o text'@type': /ethermint.types.v1.EthAccountbase_account:account_number: "0"address: cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jwpub_key: '@type': /ethermint.crypto.v1.ethsecp256k1.PubKey key: AsV5oddeB+hkByIJo/4lZiVUgXTzNfBPKC73cZ4K1YD2sequence: "1"code_hash: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 +Address: [20 87 74 109 255 45 223 158 7 130 139 67 69 211 4 9 25 175 86 82] +Address (hex): 14574A6DFF2DDF9E07828B4345D3040919AF5652 +Bech32 Acc: cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw +Bech32 Val: cosmosvaloper1z3t55m0l9h0eupuz3dp5t5cypyv674jjn4d6nn ``` -### Cosmos gRPC and REST[​](#cosmos-grpc-and-rest "Direct link to Cosmos gRPC and REST") +```bash Hex to Bech32 +$ appd debug addr 14574A6DFF2DDF9E07828B4345D3040919AF5652 -``` -# GET /cosmos/auth/v1beta1/accounts/{address}curl -X GET "http://localhost:10337/cosmos/auth/v1beta1/accounts/cosmos14au322k9munkmx5wrchz9q30juf5wjgz2cfqku" -H "accept: application/json" +Address: [20 87 74 109 255 45 223 158 7 130 139 67 69 211 4 9 25 175 86 82] +Address (hex): 14574A6DFF2DDF9E07828B4345D3040919AF5652 +Bech32 Acc: cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw +Bech32 Val: cosmosvaloper1z3t55m0l9h0eupuz3dp5t5cypyv674jjn4d6nn ``` -### JSON-RPC[​](#json-rpc "Direct link to JSON-RPC") + -To retrieve the Ethereum hex address using Web3, use the JSON-RPC [`eth_accounts`](/docs/api-reference/ethereum-json-rpc/methods#eth-accounts) or [`personal_listAccounts`](/docs/api-reference/ethereum-json-rpc/methods#personal-listAccounts) endpoints: +### Key output -``` -# query against a local nodecurl -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545curl -X POST --data '{"jsonrpc":"2.0","method":"personal_listAccounts","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 -``` + + The Cosmos SDK Keyring output (i.e `appd keys`) only supports addresses and public keys in Bech32 format. + + +We can use the `keys show` command with the flag `--bech ` to obtain different address formats: + + + + ```bash + $ appd keys show dev0 --bech acc + + - name: dev0 + type: local + address: cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw + pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"AsV5oddeB+hkByIJo/4lZiVUgXTzNfBPKC73cZ4K1YD2"}' + mnemonic: "" + ``` + + + + ```bash + $ appd keys show dev0 --bech val + + - name: dev0 + type: local + address: cosmosvaloper1z3t55m0l9h0eupuz3dp5t5cypyv674jjn4d6nn + pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"AsV5oddeB+hkByIJo/4lZiVUgXTzNfBPKC73cZ4K1YD2"}' + mnemonic: "" + ``` + + + + ```bash + $ appd keys show dev0 --bech cons + + - name: dev0 + type: local + address: cosmosvalcons1rllqa5d97n6zyjhy6cnscc7zu30zjn3f7wyj2n + pubkey: '{"@type":"/ethermint.crypto.v1.ethsecp256k1.PubKey","key":"A/fVLgIqiLykFQxum96JkSOoTemrXD0tFaFQ1B0cpB2c"}' + mnemonic: "" + ``` + + + +## Querying an Account + +You can query an account address using CLI, gRPC, REST, or JSON-RPC: + + + + ```bash + # NOTE: the --output (-o) flag defines the output format + appd q auth account $(appd keys show dev0 -a) -o text + + '@type': /ethermint.types.v1.EthAccount + base_account: + account_number: "0" + address: cosmos1z3t55m0l9h0eupuz3dp5t5cypyv674jj7mz2jw + pub_key: + '@type': /ethermint.crypto.v1.ethsecp256k1.PubKey + key: AsV5oddeB+hkByIJo/4lZiVUgXTzNfBPKC73cZ4K1YD2 + sequence: "1" + code_hash: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + ``` + + + + ```bash + # GET /cosmos/auth/v1beta1/accounts/{address} + curl -X GET "http://localhost:10337/cosmos/auth/v1beta1/accounts/cosmos14au322k9munkmx5wrchz9q30juf5wjgz2cfqku" \ + -H "accept: application/json" + ``` + + + + ```bash + # List accounts using eth_accounts + curl -X POST --data '{ + "jsonrpc":"2.0", + "method":"eth_accounts", + "params":[], + "id":1 + }' -H "Content-Type: application/json" http://localhost:8545 + + # Or using personal_listAccounts + curl -X POST --data '{ + "jsonrpc":"2.0", + "method":"personal_listAccounts", + "params":[], + "id":1 + }' -H "Content-Type: application/json" http://localhost:8545 + ``` + + + + +For JSON-RPC methods, see [`eth_accounts`](/docs/api-reference/ethereum-json-rpc/methods#eth-accounts) and [`personal_listAccounts`](/docs/api-reference/ethereum-json-rpc/methods#personal-listAccounts) documentation. + diff --git a/docs/documentation/concepts/chain-id.mdx b/docs/documentation/concepts/chain-id.mdx index 37bfd689..15cdba9f 100644 --- a/docs/documentation/concepts/chain-id.mdx +++ b/docs/documentation/concepts/chain-id.mdx @@ -1,38 +1,149 @@ --- title: "Chain ID" -description: "A chain ID is a unique identifier that represents a blockchain network. We use it to distinguish different blockchain networks from each other and to ensure that transactions and messages are sent to the correct network. Your Cosmos EVM blockchain should follow the format of format." +description: "Chain IDs are unique identifiers that distinguish blockchain networks from each other. Cosmos EVM uses a dual Chain ID system to maintain compatibility with both Cosmos SDK and Ethereum ecosystems." --- - You can also look up the [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) `Chain ID` by referring to [chainlist.org](https://chainlist.org/). + You can look up existing EVM Chain IDs by referring to [chainlist.org](https://chainlist.org/) to ensure your chosen ID is not already in use. -## The Chain Identifier[​](#the-chain-identifier "Direct link to The Chain Identifier") +## Dual Chain ID System -Every chain must have a unique identifier or `chain-id`. CometBFT requires each application to define its own `chain-id` in the [genesis.json fields](https://docs.cometbft.com/v1.0/references/config/genesis.json). However, to comply with both EIP-155 and Cosmos standard for chain upgrades, Cosmos EVM-compatible chains must implement a special structure for their chain identifiers. +Cosmos EVM requires **two separate chain IDs** to maintain full compatibility with both the Cosmos SDK and Ethereum ecosystems: -## Structure[​](#structure "Direct link to Structure") +### 1. Cosmos Chain ID (String) -A Cosmos EVM Chain ID contains 3 main components +The **Cosmos Chain ID** is a string identifier used by: +- CometBFT consensus engine +- IBC (Inter-Blockchain Communication) protocol +- Native Cosmos SDK transactions +- Chain upgrades and governance -* **Identifier**: Unstructured string that defines the name of the application. -* **EIP-155 Number**: Immutable [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) `CHAIN_ID` that defines the replay attack protection number. -* **Version Number**: Is the version number (always positive) that the chain is currently running. This number **MUST** be incremented every time the chain is upgraded or forked to avoid network or consensus errors. +**Format**: String with flexible naming (e.g., `"cosmosevm-1"`, `"mychain-testnet-2"`) -### Format[​](#format "Direct link to Format") +**Example**: +```json +// In genesis.json +{ + "chain_id": "cosmosevm-1" +} +``` + +### 2. EVM Chain ID (Integer) -The format for specifying a Cosmos EVM-compatible chain-id in genesis is the following: +The **EVM Chain ID** is an integer used by: +- Ethereum transactions (EIP-155 replay protection) +- MetaMask and other Ethereum wallets +- Smart contract deployments +- EVM tooling (Hardhat, Foundry, etc.) +**Format**: Positive integer (e.g., `9000`, `9001`) + +**Example**: +```go +// In app/app.go +const EVMChainID = 9000 ``` -{identifier}_{EIP155}-{version} + +## Configuration + +Both chain IDs must be configured when setting up your chain: + +### In Your Application Code + +```go +// app/app.go +const ( + CosmosChainID = "cosmosevm-1" // String for Cosmos/IBC + EVMChainID = 9000 // Integer for EVM/Ethereum +) ``` -The following table provides an example where the second row corresponds to an upgrade from the first one: +### In Genesis Configuration + +The Cosmos Chain ID is set in `genesis.json`: +```json +{ + "chain_id": "cosmosevm-1", + // ... other genesis parameters +} +``` + +The EVM Chain ID is configured in the EVM module parameters: +```go +// During chain initialization +evmtypes.DefaultChainConfig(9000) // Your EVM Chain ID +``` + +## Important Considerations + +### Chain ID Selection + + +The two chain IDs serve different purposes and **must not be confused**: +- Use the **Cosmos Chain ID** (string) for IBC connections, governance proposals, and Cosmos SDK operations +- Use the **EVM Chain ID** (integer) for MetaMask configuration, smart contract deployments, and EIP-155 transaction signing + + +### EVM Chain ID Guidelines + +When selecting your EVM Chain ID: +1. **Check availability**: Verify your chosen ID is not already in use on [chainlist.org](https://chainlist.org/) +2. **Avoid conflicts**: Don't use well-known chain IDs (1 for Ethereum mainnet, 137 for Polygon, etc.) +3. **Consider ranges**: + - Production networks often use lower numbers + - Testnets commonly use higher ranges (9000+) + - Local development can use very high numbers (31337+) + +### Chain Upgrades + + +Unlike traditional Cosmos chains that change their chain ID during upgrades (e.g., `cosmoshub-4` to `cosmoshub-5`), the EVM Chain ID must remain **constant** across upgrades to maintain compatibility with deployed smart contracts and existing wallets. + + +Only the Cosmos Chain ID may change during chain upgrades if needed for consensus-breaking changes. The EVM Chain ID should never change once set. + +## Examples + +Here are some example configurations: + +| Network Type | Cosmos Chain ID | EVM Chain ID | Notes | +|-------------|-----------------|--------------|-------| +| Mainnet | `"cosmosevm-1"` | `9000` | Production network | +| Testnet | `"cosmosevm-testnet-1"` | `9001` | Public testnet | +| Devnet | `"cosmosevm-dev-1"` | `9002` | Development network | +| Local | `"cosmosevm-local-1"` | `31337` | Local development | + +## Troubleshooting + +### Common Issues + +1. **"Chain ID mismatch" errors** + - **Cause**: Using Cosmos Chain ID where EVM Chain ID is expected (or vice versa) + - **Solution**: Ensure you're using the correct type of chain ID for each context + +2. **MetaMask connection failures** + - **Cause**: Incorrect EVM Chain ID in wallet configuration + - **Solution**: Use the integer EVM Chain ID, not the string Cosmos Chain ID + +3. **IBC transfer failures** + - **Cause**: Using EVM Chain ID for IBC operations + - **Solution**: IBC always uses the Cosmos Chain ID (string format) + +4. **Smart contract deployment issues** + - **Cause**: EIP-155 replay protection using wrong chain ID + - **Solution**: Ensure your EVM Chain ID matches what's configured in the chain + +### Verification Commands + +To verify your chain IDs are correctly configured: -| ChainID | Identifier | EIP-155 Number | Version Number | -| ------------- | ---------- | -------------- | -------------- | -| `cosmoshub-1` | cosmoshub | 9000 | 1 | -| `cosmoshub-2` | cosmoshub | 9000 | 2 | -| `...` | ... | ... | ... | -| `cosmoshub-N` | cosmoshub | 9000 | N | +```bash +# Check Cosmos Chain ID +curl -s http://localhost:26657/status | jq '.result.node_info.network' +# Check EVM Chain ID +curl -X POST -H "Content-Type: application/json" \ + --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \ + http://localhost:8545 | jq '.result' +``` \ No newline at end of file diff --git a/docs/documentation/concepts/encoding.mdx b/docs/documentation/concepts/encoding.mdx index 8f62caa3..1094c027 100644 --- a/docs/documentation/concepts/encoding.mdx +++ b/docs/documentation/concepts/encoding.mdx @@ -9,28 +9,102 @@ In Ethereum, integers must be represented in big-endian binary form with no lead The Cosmos Stargate release introduces protobuf as the main encoding format for both client and state serialization. All the EVM module types that are used for state and clients, such as transaction messages, genesis, query services, etc., will be implemented as protocol buffer messages. The Cosmos SDK also supports the legacy Amino encoding. Protocol Buffers (protobuf) is a language-agnostic binary serialization format that is smaller and faster than JSON. It is used to serialize structured data, such as messages, and is designed to be highly efficient and extensible. The encoding format is defined in a language-agnostic language called Protocol Buffers Language (proto3), and the encoded messages can be used to generate code for a variety of programming languages. The main advantage of protobuf is its efficiency, which results in smaller message sizes and faster serialization and deserialization times. The RLP decoding process is as follows: according to the first byte (i.e., prefix) of input data and decoding the data type, the length of the actual data and offset; according to the type and offset of data, decode the data correspondingly. -## Prerequisite Readings[​](#prerequisite-readings "Direct link to Prerequisite Readings") +## Prerequisite Readings -* [Cosmos SDK Encoding](https://docs.cosmos.network/main/learn/advanced/encoding) -* [Ethereum RLP](https://eth.wiki/en/fundamentals/rlp) + + + Learn about protobuf and Amino encoding in Cosmos SDK + + + Understand Recursive Length Prefix encoding + + -## Encoding Formats[​](#encoding-formats "Direct link to Encoding Formats") +## Encoding Formats -### Protocol Buffers[​](#protocol-buffers "Direct link to Protocol Buffers") + + + **Primary encoding for Cosmos EVM** + + The Cosmos [Stargate](https://stargate.cosmos.network/) release introduces [protobuf](https://developers.google.com/protocol-buffers) as the main encoding format for both client and state serialization. + + + All EVM module types (transaction messages, genesis, query services) are implemented as protocol buffer messages. + + + **Advantages:** + - Language-agnostic binary serialization + - Smaller message sizes than JSON + - Faster serialization/deserialization + - Strongly typed with schema validation + + + + **Ethereum compatibility encoding** + + Recursive Length Prefix ([RLP](https://eth.wiki/en/fundamentals/rlp)) is an encoding/decoding algorithm that serializes messages for Ethereum compatibility. + + + Cosmos EVM uses RLP specifically for Ethereum transaction encoding to ensure JSON-RPC compatibility. + + + **Usage in Cosmos EVM:** + - Encoding `MsgEthereumTx` for JSON-RPC + - Transaction signing and verification + - Block hash computation + - Merkle tree data structures + + + + **Backwards compatibility only** + + The Cosmos SDK supports legacy Amino encoding for backwards compatibility with older versions. + + + Cosmos EVM does not support Amino in the EVM module. It's only available for other Cosmos SDK modules that enable it. + + + **Limited use cases:** + - Client encoding for legacy wallets + - Signing with Ledger devices + - Not used for EVM transactions + + -The Cosmos [Stargate](https://stargate.cosmos.network/) release introduces [protobuf](https://developers.google.com/protocol-buffers) as the main encoding format for both client and state serialization. All the EVM module types that are used for state and clients (transaction messages, genesis, query services, etc) will be implemented as protocol buffer messages. +### Transaction Encoding Implementation -### Amino[​](#amino "Direct link to Amino") +The `x/vm` module handles `MsgEthereumTx` encoding by converting to go-ethereum's `Transaction` format and using RLP: -The Cosmos SDK also supports the legacy Amino encoding format for backwards compatibility with previous versions, specially for client encoding and signing with Ledger devices. Cosmos EVM does not support Amino in the EVM module, but it is supported for all other Cosmos SDK modules that enable it. - -### RLP[​](#rlp "Direct link to RLP") - -Recursive Length Prefix ([RLP](https://eth.wiki/en/fundamentals/rlp)), is an encoding/decoding algorithm that serializes a message and allows for quick reconstruction of encoded data. Cosmos EVM uses RLP to encode/decode Ethereum messages for JSON-RPC handling to conform messages to the proper Ethereum format. This allows messages to be encoded and decoded in the exact format as Ethereum's. - -The `x/vm` transactions (`MsgEthereumTx`) encoding is performed by casting the message to a go-ethereum's `Transaction` and then marshaling the transaction data using RLP: + +```go TxEncoder +// TxEncoder overwrites sdk.TxEncoder to support MsgEthereumTx +func (g txConfig) TxEncoder() sdk.TxEncoder { + return func(tx sdk.Tx) ([]byte, error) { + msg, ok := tx.(*evmtypes.MsgEthereumTx) + if ok { + return msg.AsTransaction().MarshalBinary() + } + return g.TxConfig.TxEncoder()(tx) + } +} ``` -// TxEncoder overwrites sdk.TxEncoder to support MsgEthereumTxfunc (g txConfig) TxEncoder() sdk.TxEncoder {return func(tx sdk.Tx) ([]byte, error) { msg, ok := tx.(*evmtypes.MsgEthereumTx) if ok { return msg.AsTransaction().MarshalBinary() } return g.TxConfig.TxEncoder()(tx)}}// TxDecoder overwrites sdk.TxDecoder to support MsgEthereumTxfunc (g txConfig) TxDecoder() sdk.TxDecoder {return func(txBytes []byte) (sdk.Tx, error) { tx := ðtypes.Transaction{} err := tx.UnmarshalBinary(txBytes) if err == nil { msg := &evmtypes.MsgEthereumTx{} msg.FromEthereumTx(tx) return msg, nil } return g.TxConfig.TxDecoder()(txBytes)}} + +```go TxDecoder +// TxDecoder overwrites sdk.TxDecoder to support MsgEthereumTx +func (g txConfig) TxDecoder() sdk.TxDecoder { + return func(txBytes []byte) (sdk.Tx, error) { + tx := ðtypes.Transaction{} + err := tx.UnmarshalBinary(txBytes) + if err == nil { + msg := &evmtypes.MsgEthereumTx{} + msg.FromEthereumTx(tx) + return msg, nil + } + return g.TxConfig.TxDecoder()(txBytes) + } +} ``` + + diff --git a/docs/documentation/concepts/mempool.mdx b/docs/documentation/concepts/mempool.mdx index 39dd719b..c658140c 100644 --- a/docs/documentation/concepts/mempool.mdx +++ b/docs/documentation/concepts/mempool.mdx @@ -1,116 +1,62 @@ --- -title: "App-Side Mempool for EVM Compatibility" -description: "Two-tiered mempool architecture enabling full Ethereum tooling compatibility" +title: "Mempool" +description: "Design and Rationale" --- -## Overview - -The mempool is a two-tiered system that queues transactions with nonce gaps locally while broadcasting only executable transactions to the network. This allows Ethereum tooling (Hardhat, Foundry, ethers.js) to submit transaction batches out-of-order without rejection. - -### Problem - -CometBFT rejects transactions with: -- Nonce gaps (non-sequential nonces) -- Out-of-order batches (common in deployment scripts) - -Ethereum tooling expects these transactions to queue rather than fail. - -### Design Rationale - -The two-tiered approach: -- **Local queue**: Stores gapped transactions without network propagation, preventing invalid transaction gossip -- **Public pool**: Contains only valid transactions, maintaining consensus integrity -- **Automatic promotion**: Moves transactions from local to public when gaps fill, ensuring eventual inclusion - -This preserves CometBFT's validation guarantees while providing Ethereum's expected queuing behavior. - -### What Gets Queued vs Rejected - -**Queued (Local Storage)**: -- **Nonce gaps**: Transactions with nonce > expected nonce -- These are stored locally and promoted when gaps fill - -**Rejected (Immediate Failure)**: -- **Insufficient fees**: `GasFeeCap < BaseFee` -- **Insufficient balance**: Transaction cost exceeds account balance -- **Invalid signature**: Malformed or improperly signed transactions -- **Gas limit exceeded**: Transactions exceeding block gas limit - -Only nonce-gapped transactions are intercepted and queued. All other validation failures result in immediate rejection with error returned to the client. - -## Architecture - -The mempool uses a two-tiered system with local and public transaction pools: - -```mermaid -flowchart TD - Submit[Transaction Submission
Client or Peer Node] - - Submit --> Receive - - Receive[CometBFT Receives
Transaction arrives at node] - - Receive --> CheckTx - - CheckTx[CheckTx Validation
App validates transaction] - - CheckTx --> Route{Route
Decision} - - Route -->|Success| AddComet - Route -->|Nonce Gap| Intercept - Route -->|Other Error| Discard - - AddComet[Add to Comet Mempool
Executable transactions] - - Intercept[Intercept Error
Return success to client
Add to local queue] + +This mempool implementation is **experimental** and under active development. It is intended for testing and evaluation purposes. Use in production environments is **not recommended** without thorough testing and risk assessment. +Please [report issues](https://github.com/cosmos/evm/issues/new) and submit feedback to help improve stability. + - Discard[Reject
Transaction discarded] - - Intercept --> LocalQueue - - LocalQueue[Local Queue
Non-executable
Nonce gaps] - - LocalQueue --> ValidQueue - - ValidQueue[Valid Queue
Waiting for gaps to fill] - - AddComet --> Broadcast - - Broadcast[P2P Broadcast
Share with network peers] - - AddComet --> BuildBlock - ValidQueue --> BuildBlock +## Overview - BuildBlock[Build Block
ProcessProposal
Sort by fee & nonce] +The EVM mempool is responsible for managing both EVM and Cosmos transactions in a unified pool, enabling Ethereum-compatible transaction flows including out-of-order transactions and nonce gap handling. It serves as a replacement for the default CometBFT FIFO mempool to support Ethereum tooling expectations while maintaining Cosmos SDK compatibility. - ValidQueue -.->|Gap Filled| Promotion +## Usage Examples - Promotion[Promotion
Periodic scan
Re-broadcast when ready] +The EVM mempool enables seamless interaction with Ethereum tooling and deployment scripts that send multiple transactions in quick succession. - Promotion --> AddComet +### Complex Contract Deployments - BuildBlock --> FinalBlock[Final Block
Proposed to network] +DeFi protocols like Uniswap deploy multiple interdependent contracts in rapid succession. With the EVM mempool, these deployment scripts work without modification: - style LocalQueue fill:#ffe0e0 - style ValidQueue fill:#e0ffe0 - style AddComet fill:#e0f0ff - style Intercept fill:#fff7e6 - style Discard fill:#ffcccc +```javascript +// Deploy Uniswap V3 contracts - sends many transactions at once +const factory = await UniswapV3Factory.deploy(); +const router = await SwapRouter.deploy(factory.address, WETH); +const quoter = await Quoter.deploy(factory.address, WETH); +const multicall = await UniswapInterfaceMulticall.deploy(); + +// All transactions queue properly even if they arrive out of order +// The mempool handles nonce gaps automatically ``` -### Core Components +### Batch Transaction Submission -**CheckTx Handler** -Intercepts nonce gap errors during validation, routes gapped transactions to the local queue, and returns success to maintain compatibility with Ethereum tooling that expects queuing behavior. Only nonce gaps are intercepted - other validation failures (insufficient fees, balance, etc.) are rejected immediately. +```javascript +// Ethereum tooling sends multiple transactions +await wallet.sendTransaction({nonce: 100, ...}); // OK: Immediate execution +await wallet.sendTransaction({nonce: 101, ...}); // OK: Immediate execution +await wallet.sendTransaction({nonce: 103, ...}); // OK: Queued locally (gap) +await wallet.sendTransaction({nonce: 102, ...}); // OK: Fills gap, both execute +``` -**TxPool** -Direct port of Ethereum's transaction pool that manages both pending (executable) and queued (future) transactions. Handles promotion, eviction, and replacement according to Ethereum rules. +### Transaction Replacement -**LegacyPool** -Stores non-executable transactions with nonce gaps, tracks dependencies between transactions, and automatically promotes them when gaps are filled. The queue contains only transactions waiting for earlier nonces - not transactions with insufficient fees. +```javascript +// Speed up transaction with same nonce, higher fee +const tx1 = await wallet.sendTransaction({ + nonce: 100, + gasPrice: parseUnits("20", "gwei") +}); -**ExperimentalEVMMempool** -Unified structure that manages both EVM and Cosmos transaction pools while providing a single interface for transaction insertion, selection, and removal. +// Replace with higher fee +const tx2 = await wallet.sendTransaction({ + nonce: 100, // Same nonce + gasPrice: parseUnits("30", "gwei") // Higher fee +}); +// tx1 is replaced by tx2 +``` ## Transaction Flow @@ -122,27 +68,24 @@ CometBFT receives the transactions and validates them in the app using CheckTx. ### 3. CheckTx Routing -The CheckTx handler processes transactions with special handling for nonce gaps: +The CheckTx handler processes transactions with special handling for nonce gaps ([source](https://github.com/cosmos/evm/blob/main/mempool/check_tx.go)): -**Success Path** - Immediately executable transactions proceed to the Comet mempool: -```go -// Transaction with correct nonce passes validation -if txNonce == accountNonce { - // Proceed to Comet mempool for broadcast - return success -} -``` +**Success Path** - Valid transactions with correct nonces pass through to the Comet mempool for broadcast. -**Nonce Gap** - Transactions with future nonces are intercepted and queued locally: +**Nonce Gap Detection** - Transactions with future nonces are intercepted and queued locally: ```go -if txNonce > accountNonce { - // Detected nonce gap +// From mempool/check_tx.go +if err != nil { + // detect if there is a nonce gap error (only returned for EVM transactions) if errors.Is(err, ErrNonceGap) { - // Route to local queue instead of rejecting + // send it to the mempool for further triage err := mempool.InsertInvalidNonce(request.Tx) - // Note: Must intercept error and return success to EVM client - return interceptedSuccess + if err != nil { + return sdkerrors.ResponseCheckTxWithEvents(err, gInfo.GasWanted, gInfo.GasUsed, anteEvents, false), nil + } } + // anything else, return regular error + return sdkerrors.ResponseCheckTxWithEvents(err, gInfo.GasWanted, gInfo.GasUsed, anteEvents, false), nil } ``` @@ -170,7 +113,7 @@ The node periodically scans the local queue and promotes transactions when: - Nonce gaps are filled (either in mempool or from on-chain state) - Promoted transactions are re-broadcast to the network -## Transaction States +## Transaction Lifecycle ```mermaid stateDiagram-v2 @@ -193,204 +136,81 @@ stateDiagram-v2 Committed --> [*] ``` -## API Reference +## Architecture -The txpool namespace provides full Ethereum-compatible RPC methods for querying mempool state. These methods are publicly exposed via the JSON-RPC interface on port 8545: +![Mempool Architecture](/assets/public/mempool_architecture.png) - +The mempool uses a two-tiered system with local and public transaction pools. - -Returns all pending and queued transactions grouped by account. +### Problem Statement -**Request** -```json -{ - "jsonrpc": "2.0", - "method": "txpool_content", - "params": [], - "id": 1 -} -``` +CometBFT rejects transactions with: +- Nonce gaps (non-sequential nonces) +- Out-of-order batches (common in deployment scripts) -**Response** -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "pending": { - "0x1234...": { - "100": { /* transaction object */ }, - "101": { /* transaction object */ } - } - }, - "queued": { - "0x5678...": { - "103": { /* transaction object */ } - } - } - } -} -``` +Ethereum tooling expects these transactions to queue rather than fail. -- `pending`: Transactions ready for inclusion in the next block -- `queued`: Valid transactions waiting for nonce gaps to be filled - +### Solution Architecture - -Returns transactions for a specific address. +To improve DevEx, a tiered approach was implemented: a local transaction pool handles queuing nonce-gapped transactions, upgrading transactions to CometBFT mempool which allows them to be gossipped to network peers and be included in blocks. This helps reduce network spam/DOS exposure while also enabling proper EVM transaction semantics. -**Request** -```json -{ - "jsonrpc": "2.0", - "method": "txpool_contentFrom", - "params": ["0x1234567890abcdef1234567890abcdef12345678"], - "id": 1 -} -``` +The two-tiered approach: +- **Local queue**: Stores gapped transactions without network propagation, preventing invalid transaction gossip +- **Public mempool**: Contains only valid transactions, maintaining consensus integrity +- **Automatic promotion**: Moves transactions from local to public when gaps fill, ensuring inclusion once conditions are met -**Response** -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "pending": { - "100": { /* transaction object */ }, - "101": { /* transaction object */ } - }, - "queued": { - "103": { /* transaction object */ } - } - } -} -``` - - - -Returns a human-readable summary of all transactions. - -**Request** -```json -{ - "jsonrpc": "2.0", - "method": "txpool_inspect", - "params": [], - "id": 1 -} -``` +![Transaction Flow](/assets/public/mempool_transaction_flow.png) -**Response** -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "pending": { - "0x1234...": { - "100": "0x5678...: 1000 wei + 21000 gas × 20 gwei", - "101": "contract creation: 0 wei + 100000 gas × 20 gwei" - } - }, - "queued": { - "0x5678...": { - "103": "0x9abc...: 2000 wei + 21000 gas × 25 gwei" - } - } - } -} -``` +### Core Components -**Format**: `: wei + gas × wei` - +**CheckTx Handler** +Intercepts nonce gap errors during validation, routes gapped transactions to the local queue, and returns success to maintain compatibility with Ethereum tooling that expects queuing behavior. Only nonce gaps are intercepted - other validation failures (insufficient fees, balance, etc.) are rejected immediately. - -Returns transaction counts for pending and queued pools. +**TxPool** +Direct port of Ethereum's transaction pool that manages both pending (executable) and queued (future) transactions. Handles promotion, eviction, and replacement according to Ethereum rules. -**Request** -```json -{ - "jsonrpc": "2.0", - "method": "txpool_status", - "params": [], - "id": 1 -} -``` +**LegacyPool** +Stores non-executable transactions with nonce gaps, tracks dependencies between transactions, and automatically promotes them when gaps are filled. The queue contains only transactions waiting for earlier nonces - not transactions with insufficient fees. -**Response** -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "pending": "0x10", // 16 pending transactions - "queued": "0x5" // 5 queued transactions - } -} -``` - +**ExperimentalEVMMempool** +Unified structure that manages both EVM and Cosmos transaction pools while providing a single interface for transaction insertion, selection, and removal. - +### Transaction States -## Usage Examples +**Queued (Local Storage)**: +- **Nonce gaps**: Transactions with nonce > expected nonce +- These are stored locally and promoted when gaps fill -### Batch Transaction Submission +**Rejected (Immediate Failure)**: +- **Insufficient fees**: `GasFeeCap < BaseFee` +- **Insufficient balance**: Transaction cost exceeds account balance +- **Invalid signature**: Malformed or improperly signed transactions +- **Gas limit exceeded**: Transactions exceeding block gas limit -```javascript -// Ethereum tooling sends multiple transactions -await wallet.sendTransaction({nonce: 100, ...}); // OK: Immediate execution -await wallet.sendTransaction({nonce: 101, ...}); // OK: Immediate execution -await wallet.sendTransaction({nonce: 103, ...}); // OK: Queued locally (gap) -await wallet.sendTransaction({nonce: 102, ...}); // OK: Fills gap, both execute -``` +Only nonce-gapped transactions are intercepted and queued. All other validation failures result in immediate rejection with error returned to the client. +This combined approach preserves CometBFT's leading consensus mechanism while providing Ethereum's expected tx queuing behavior. -### Transaction Replacement +## API Reference -```javascript -// Speed up transaction with same nonce, higher fee -const tx1 = await wallet.sendTransaction({ - nonce: 100, - gasPrice: parseUnits("20", "gwei") -}); +The mempool exposes Ethereum-compatible RPC methods for querying transaction pool state. See the [JSON-RPC Methods documentation](/docs/api-reference/ethereum-json-rpc/methods#txpool-methods) for detailed API reference: -// Replace with higher fee -const tx2 = await wallet.sendTransaction({ - nonce: 100, // Same nonce - gasPrice: parseUnits("30", "gwei") // Higher fee -}); -// tx1 is replaced by tx2 -``` +- **`txpool_status`**: Get pending and queued transaction counts +- **`txpool_content`**: View all transactions in the pool +- **`txpool_contentFrom`**: View transactions from specific addresses +- **`txpool_inspect`**: Get human-readable transaction summaries -## Implementation Details +You can also explore these methods interactively using the [RPC Explorer](/docs/api-reference/ethereum-json-rpc/rpc-explorer). -### Configuration +## Integration -For chain developers looking to integrate the mempool into their Cosmos SDK chain, see the [EVM Integration Guide](/docs/documentation/cosmos-sdk/integrate) for complete setup instructions. +For chain developers looking to integrate the mempool into their Cosmos SDK chain, see the [EVM Mempool Integration Guide](/docs/documentation/integration/mempool-integration) for complete setup instructions. -The mempool is initialized with the following configuration: - -```go -type EVMMempoolConfig struct { - TxPool *txpool.TxPool - CosmosPool sdkmempool.ExtMempool - AnteHandler sdk.AnteHandler - BroadcastTxFn func(txs []*ethtypes.Transaction) error - BlockGasLimit uint64 -} -``` - -**Key Parameters:** -- **Block Gas Limit**: Maximum gas per block (default: 100,000,000) -- **Queue Size**: Managed by TxPool configuration -- **Eviction Rules**: Time-based and fee-based eviction from Ethereum TxPool - -### State Management +## State Management The mempool maintains transaction state through the unified `ExperimentalEVMMempool` structure, which manages separate pools for EVM and Cosmos transactions while providing a single interface. This experimental implementation handles fee-based prioritization, nonce sequencing, and transaction verification through an integrated ante handler. -### Testing +## Testing -The mempool behavior can be verified using the test scripts provided in the [cosmos/evm](https://github.com/cosmos/evm) repository. \ No newline at end of file +The mempool behavior can be verified using the test scripts provided in the [cosmos/evm](https://github.com/cosmos/evm) repository. The [`tests/systemtests/Counter/script/SimpleSends.s.sol`](https://github.com/cosmos/evm/blob/main/tests/systemtests/Counter/script/SimpleSends.s.sol) script demonstrates typical Ethereum tooling behavior - it sends 10 sequential transactions in a batch, which naturally arrive out of order and create nonce gaps. \ No newline at end of file diff --git a/docs/documentation/concepts/pending-state.mdx b/docs/documentation/concepts/pending-state.mdx index 27242e40..51c1653e 100644 --- a/docs/documentation/concepts/pending-state.mdx +++ b/docs/documentation/concepts/pending-state.mdx @@ -8,26 +8,143 @@ During the pending state, the transaction initiator is allowed to change the tra ## Prerequisite Readings[​](#prerequisite-readings "Direct link to Prerequisite Readings") * [Cosmos SDK Mempool](https://docs.cosmos.network/main/building-apps/app-mempool) +* [Mempool Architecture](/docs/documentation/concepts/mempool) ## Cosmos EVM vs Ethereum[​](#cosmos-evm-vs-ethereum "Direct link to Cosmos EVM vs Ethereum") In Ethereum, pending blocks are generated as they are queued for production by miners. These pending blocks include pending transactions that are picked out by miners, based on the highest reward paid in gas. This mechanism exists as block finality is not possible on the Ethereum network. Blocks are committed with probabilistic finality, which means that transactions and blocks become less likely to become reverted as more time (and blocks) passes. -Cosmos EVM is designed quite differently on this front as there is no concept of a "pending state". Our EVM uses [CometBFT](https://docs.cometbft.com/v1.0/) consensus which provides instant finality for transaction. For this reason, Cosmos EVM does not require a pending state mechanism, as all (if not most) of the transactions will be committed to the next block (avg. block time on Cosmos chains is \~8s). However, this causes a few hiccups in terms of the Ethereum Web3-compatible queries that can be made to pending state. +Cosmos EVM is designed differently as it uses [CometBFT](https://docs.cometbft.com/v1.0/) consensus which provides instant finality for transactions. While there is no concept of "pending blocks" that can be reorganized, Cosmos EVM implements an **experimental EVM mempool** that provides Ethereum-compatible pending state functionality. -Another significant difference with Ethereum, is that blocks are produced by validators or block producers, who include transactions from their local mempool into blocks in a first-in-first-out (FIFO) fashion. Transactions on the Cosmos EVM cannot be ordered or cherry picked out from the CometBFT node [mempool](https://docs.cometbft.com/v1.0/explanation/core/mempool). +## EVM-Compliant Mempool + +The experimental EVM mempool introduces a two-tiered system that brings Ethereum-like pending state behavior to Cosmos EVM: + +### Transaction States + + + + Transactions with correct nonces that are immediately executable. These are in the public mempool, broadcast to peers, and ready for block inclusion. + + + Transactions with future nonces (nonce gaps) that are stored locally. These wait for earlier transactions to execute before being promoted to pending. + + + +### Key Differences from Traditional Cosmos + +1. **Nonce Gap Handling**: Unlike traditional Cosmos chains that reject out-of-order transactions, the EVM mempool queues them locally until gaps are filled. + +2. **Fee-Based Priority**: Both EVM and Cosmos transactions compete fairly based on their effective tips rather than FIFO ordering: + - **EVM transactions**: Priority = `gas_tip_cap` or `min(gas_tip_cap, gas_fee_cap - base_fee)` + - **Cosmos transactions**: Priority = `(fee_amount / gas_limit) - base_fee` + +3. **Transaction Replacement**: Supports replacing pending transactions with higher fee versions using the same nonce, enabling "speed up" functionality common in Ethereum wallets. ## Pending State Queries[​](#pending-state-queries "Direct link to Pending State Queries") -Cosmos EVM will make queries which will account for any unconfirmed transactions present in a node's transaction mempool. A pending state query made will be subjective and the query will be made on the target node's mempool. Thus, the pending state will not be the same for the same query to two different nodes. +With the experimental EVM mempool, pending state queries now reflect a more Ethereum-compatible view: + +### Transaction Pool Inspection + +The mempool provides dedicated RPC methods to inspect pending and queued transactions: + + + + Returns counts of pending and queued transactions: + ```json + { + "pending": "0x10", // 16 pending transactions + "queued": "0x5" // 5 queued transactions + } + ``` + + + + Returns detailed information about all transactions in the pool, organized by account and nonce. + + + + Provides human-readable summaries of transactions for debugging. + + + +See the [JSON-RPC Methods](/docs/api-reference/ethereum-json-rpc/methods#txpool-methods) documentation for complete details. + +### Pending State Behavior + +When making queries with "pending" as the block parameter: + +1. **Balance Queries**: Reflect the account balance after all pending transactions from that account are applied +2. **Nonce Queries**: Return the next available nonce considering all pending transactions +3. **Gas Estimates**: Account for pending transactions that may affect gas costs + + +Pending state queries are subjective to each node's local mempool view. Different nodes may return different results based on their transaction pool contents. + + +## JSON-RPC Calls Supporting Pending State[​](#json-rpc-calls-on-pending-transactions "Direct link to JSON-RPC Calls on Pending Transactions") + +The following RPC methods support the `"pending"` block parameter: + +* [`eth_getBalance`](/docs/api-reference/ethereum-json-rpc/methods#eth_getbalance) - Get balance considering pending transactions +* [`eth_getTransactionCount`](/docs/api-reference/ethereum-json-rpc/methods#eth_gettransactioncount) - Get next nonce considering pending transactions +* [`eth_getBlockTransactionCountByNumber`](/docs/api-reference/ethereum-json-rpc/methods#eth_getblocktransactioncountbynumber) - Count pending transactions +* [`eth_getBlockByNumber`](/docs/api-reference/ethereum-json-rpc/methods#eth_getblockbynumber) - Get pending block information +* [`eth_getTransactionByHash`](/docs/api-reference/ethereum-json-rpc/methods#eth_gettransactionbyhash) - Retrieve pending transactions +* [`eth_getTransactionByBlockNumberAndIndex`](/docs/api-reference/ethereum-json-rpc/methods#eth_gettransactionbyblockhashandindex) - Access specific pending transactions +* [`eth_sendTransaction`](/docs/api-reference/ethereum-json-rpc/methods#eth_sendtransaction) - Submit new transactions + +Additionally, the `txpool_*` namespace provides specialized methods for mempool inspection: +* [`txpool_status`](/docs/api-reference/ethereum-json-rpc/methods#txpool_status) - Get pool statistics +* [`txpool_content`](/docs/api-reference/ethereum-json-rpc/methods#txpool_content) - View all pool transactions +* [`txpool_contentFrom`](/docs/api-reference/ethereum-json-rpc/methods#txpool_contentfrom) - Filter by address +* [`txpool_inspect`](/docs/api-reference/ethereum-json-rpc/methods#txpool_inspect) - Human-readable summaries + +## Practical Examples + +### Monitoring Transaction Status + +```javascript +// Check if transaction is pending +const tx = await provider.getTransaction(txHash); +if (tx && !tx.blockNumber) { + console.log("Transaction is pending"); + + // Check pool status + const poolStatus = await provider.send("txpool_status", []); + console.log(`Pool has ${poolStatus.pending} pending, ${poolStatus.queued} queued`); +} +``` + +### Handling Nonce Gaps + +```javascript +// Send transactions with nonce gaps (they'll be queued) +await wallet.sendTransaction({nonce: 100, ...}); // Executes immediately +await wallet.sendTransaction({nonce: 102, ...}); // Queued (gap at 101) +await wallet.sendTransaction({nonce: 101, ...}); // Fills gap, both execute +``` + +### Transaction Replacement + +```javascript +// Speed up a pending transaction +const originalTx = await wallet.sendTransaction({ + nonce: 100, + gasPrice: parseUnits("20", "gwei") +}); -### JSON-RPC Calls on Pending Transactions[​](#json-rpc-calls-on-pending-transactions "Direct link to JSON-RPC Calls on Pending Transactions") +// Replace with higher fee +const fasterTx = await wallet.sendTransaction({ + nonce: 100, // Same nonce + gasPrice: parseUnits("30", "gwei") // Higher fee +}); +``` -* [`eth_getBalance`](/docs/api-reference/ethereum-json-rpc/methods#eth_getbalance) -* [`eth_getTransactionCount`](/docs/api-reference/ethereum-json-rpc/methods#eth_gettransactioncount) -* [`eth_getBlockTransactionCountByNumber`](/docs/api-reference/ethereum-json-rpc/methods#eth_getblocktransactioncountbynumber) -* [`eth_getBlockByNumber`](/docs/api-reference/ethereum-json-rpc/methods#eth_getblockbynumber) -* [`eth_getTransactionByHash`](/docs/api-reference/ethereum-json-rpc/methods#eth_gettransactionbyhash) -* [`eth_getTransactionByBlockNumberAndIndex`](/docs/api-reference/ethereum-json-rpc/methods#eth_gettransactionbyblockhashandindex) -* [`eth_sendTransaction`](/docs/api-reference/ethereum-json-rpc/methods#eth_sendtransaction) +## Architecture Details +For a detailed understanding of how the pending state is managed: +- See [Mempool Architecture](/docs/documentation/concepts/mempool#architecture) for the two-tier system design +- Review [Transaction Flow](/docs/documentation/concepts/mempool#transaction-flow) for state transitions +- Check [Integration Guide](/docs/documentation/integration/mempool-integration) for implementation details \ No newline at end of file diff --git a/docs/documentation/concepts/replay-protection.mdx b/docs/documentation/concepts/replay-protection.mdx index a04c6885..68282998 100644 --- a/docs/documentation/concepts/replay-protection.mdx +++ b/docs/documentation/concepts/replay-protection.mdx @@ -1,25 +1,164 @@ --- title: "EIP-155: Replay Protection" -description: "EIP-155 is an Ethereum Improvement Proposal, that has introduced a simple replay protection mechanism, by including the chain ID information into the signed transaction data. This was necessary, because Ethereum-based transactions rely on the Hex representation of addresses, which are not necessarily unique to a given network. This means that single signed transaction could be valid on multiple networks, as the same addresses are involved e.g. in a token transfer. This holds the potential for exploits and is addressed by enforcing the EIP-155 replay protection." +description: "EIP-155 is an Ethereum Improvement Proposal that introduced replay protection by including chain ID information in signed transaction data. This prevents a signed transaction from being valid on multiple networks, protecting users from replay attacks." --- -Cosmos SDK-based blockchains use Bech32 representations for addresses, which contain a unique prefix per chain. This means, that for Cosmos transactions, replay protection is inherently present as addresses of a given chain are not valid addresses on other chains. However, as the Cosmos EVM also accepts EVM transactions, handling only those transactions that conform to EIP-155 becomes a requirement again. +## Background -This requires special care to be taken when selecting an EIP-155 compliant [chain ID](/docs/documentation/concepts/chain-id) to avoid duplication amongst chains. +**Replay attacks** occur when a signed transaction valid on one network can be replayed on another network without the user's consent. This is particularly problematic for Ethereum-based networks because: -## Configuring Replay Protection[​](#configuring-replay-protection "Direct link to Configuring Replay Protection") +1. **Address Format**: Ethereum uses hexadecimal addresses (e.g., `0x123...`) that are identical across all EVM chains +2. **Transaction Format**: The same transaction structure and signing scheme across networks +3. **Network Forks**: When chains fork, the same addresses exist on both chains with potentially different balances -By default, replay protection is enabled on any Cosmos EVM node. There are two distinct steps required to accept unprotected transactions, i.e. those that do not contain the chain ID in the signed transaction data: +EIP-155 solves this by incorporating the chain ID into the transaction signing process, making signatures network-specific. -1. **Disable Module Parameter**: The [EVM module](/docs/documentation/cosmos-sdk/modules/vm#parameters) contains a governance controlled parameter, that globally dictates if unprotected transactions are supported. This has to be disabled via a governance vote or by setting the `allow_unprotected_txs` field to `true` in the genesis of a local node. +## How Cosmos EVM Handles Replay Protection -2. **Adjust Node Configuration**: When the global parameter is set accordingly, each node operator has the option to individually opt into allowing unprotected transactions to be sent to their nodes. +### Dual Protection System -The EIP-155 replay protection is enabled globally in the EVM module parameters. In case this is disabled as a global requirement, node operators can opt into supporting unprotected transactions by adjusting the corresponding setting in the [node configuration](https://github.com/cosmos/evm/blob/v0.1.0/server/config/toml.go#L74-L76): +Cosmos EVM implements replay protection at two levels: + + + **Inherent Protection**: Uses Bech32 addresses with chain-specific prefixes (e.g., `cosmos1...`, `osmo1...`). Addresses from one chain are invalid on others, providing automatic replay protection. + + + **EIP-155 Protection**: Requires chain ID in transaction signatures. The EVM module enforces this by default, rejecting transactions without proper chain ID. + + + +### Chain ID Configuration + +As documented in the [Chain ID](/docs/documentation/concepts/chain-id) page, Cosmos EVM uses: +- **EVM Chain ID** (integer): Used for EIP-155 replay protection in Ethereum transactions +- **Cosmos Chain ID** (string): Used for Cosmos SDK transactions with Bech32 addresses + + +The EVM Chain ID must be unique across all EVM-compatible chains to ensure replay protection. Check [chainlist.org](https://chainlist.org/) before selecting your chain ID. + + +## Configuring Replay Protection + +By default, replay protection is **enabled** on all Cosmos EVM nodes. The system enforces EIP-155 signed transactions, rejecting any unprotected transactions. + +### Allowing Unprotected Transactions (Not Recommended) + + +Disabling replay protection is **strongly discouraged** as it exposes users to replay attacks. Only consider this for specific testing scenarios or migration purposes. + + +If absolutely necessary, unprotected transactions can be allowed through a two-step process: + +#### Step 1: Global Module Parameter + +The EVM module parameter must be changed via governance proposal or genesis configuration: + +```go +// In x/vm/types/params.go +DefaultAllowUnprotectedTxs = false // Default value + +// To enable in genesis.json +{ + "vm": { + "params": { + "allow_unprotected_txs": true // Must be set to true + } + } +} ``` -# in $HOME/.appd/config/config.toml# AllowUnprotectedTxs restricts unprotected (non EIP-155 signed) transactions to be submitted via# the node's RPC when the global parameter is disabled.allow-unprotected-txs = true # false by default + +#### Step 2: Node Configuration + +Even when globally enabled, each node operator must individually opt-in by modifying their node configuration: + +```toml +# In $HOME/.evmd/config/config.toml +[json-rpc] +# AllowUnprotectedTxs restricts unprotected (non EIP-155 signed) transactions +# to be submitted via the node's RPC when the global parameter is disabled. +allow-unprotected-txs = true # false by default +``` + + +Both conditions must be met for unprotected transactions to be accepted: +1. Global parameter `allow_unprotected_txs` = true +2. Node configuration `allow-unprotected-txs` = true + + +## Transaction Types and Protection + +### Protected Transactions (EIP-155) + +All modern Ethereum transaction types include chain ID: + +```javascript +// EIP-1559 Dynamic Fee Transaction +const tx = { + type: 2, + chainId: 9000, // EVM Chain ID included + nonce: 0, + maxFeePerGas: ..., + maxPriorityFeePerGas: ..., + // ... other fields +} +``` + +### Legacy Unprotected Transactions + +Pre-EIP-155 transactions without chain ID: + +```javascript +// Legacy transaction without chain ID (rejected by default) +const tx = { + nonce: 0, + gasPrice: ..., + gasLimit: ..., + to: ..., + value: ..., + data: ..., + // No chainId field +} +``` + +## Security Implications + +### With Replay Protection Enabled (Default) + +✅ **Protected from:** +- Cross-chain replay attacks +- Accidental transaction execution on wrong networks +- Fork-related replay vulnerabilities + +### With Replay Protection Disabled + +⚠️ **Vulnerable to:** +- Transactions being replayed on any EVM chain +- Loss of funds through replay attacks +- Unintended contract interactions on multiple chains + +## Best Practices + +1. **Always use EIP-155**: Ensure your wallet and tools sign transactions with chain ID +2. **Verify chain ID**: Double-check the chain ID in your transaction before signing +3. **Keep protection enabled**: Never disable replay protection in production +4. **Monitor transactions**: Track transaction execution across chains during migrations + +## Verification + +To verify replay protection status: + +```bash +# Check global parameter +evmd q vm params | grep allow_unprotected_txs + +# Check node configuration +grep allow-unprotected-txs $HOME/.evmd/config/config.toml ``` -More information about EIP-155 can be found here: [EIP-155: Replay Protection](/docs/documentation/concepts/replay-protection). +## Related Resources +- [EIP-155 Specification](https://eips.ethereum.org/EIPS/eip-155) +- [Chain ID Configuration](/docs/documentation/concepts/chain-id) +- [Transaction Types](/docs/documentation/concepts/transactions#ethereum-tx-type) +- [EVM Module Parameters](/docs/documentation/cosmos-sdk/modules/vm#parameters) \ No newline at end of file diff --git a/docs/documentation/concepts/single-token-representation.mdx b/docs/documentation/concepts/single-token-representation.mdx index 609a7323..1c709981 100644 --- a/docs/documentation/concepts/single-token-representation.mdx +++ b/docs/documentation/concepts/single-token-representation.mdx @@ -1,6 +1,6 @@ --- -Title: Single Token Representation -Description: How Cosmos EVM unifies native and ERC20 tokens without traditional wrapping. +title: Single Token Representation +description: How Cosmos EVM unifies native and ERC20 tokens without traditional wrapping, maintaining one source of truth across both ecosystems. --- ## Overview @@ -46,50 +46,97 @@ Cosmos EVM stores token balances only once (in the bank module), providing: * **Automatic Sync:** All interfaces reflect the same state without conversion * **No Wrapping Needed:** Eliminates manual wrap/unwrap transactions -## Real-World Example (JavaScript) +## Technical Implementation + +### Precision Handling with x/precisebank + + +The key challenge is bridging Cosmos's 6-decimal precision with EVM's 18-decimal standard. The `x/precisebank` module solves this while maintaining single token representation. + + +Cosmos EVM uses the **precisebank module** to handle the precision difference: + +- **Cosmos native**: `uatom` with 6 decimals (1 ATOM = 10^6 uatom) +- **EVM native**: `aatom` with 18 decimals (1 ATOM = 10^18 aatom) +- **Conversion**: 1 uatom = 10^12 aatom + +The precisebank module wraps the bank module to: +1. Store integer balances (uatom) in x/bank +2. Store fractional balances (remainder aatom) in x/precisebank +3. Maintain the invariant: `aatom_balance = uatom_balance * 10^12 + fractional_balance` + +```go +// From x/precisebank implementation +// a(n) = b(n) * C + f(n) +// where: +// a(n) = aatom balance (18 decimals) +// b(n) = uatom balance in x/bank (6 decimals) +// f(n) = fractional balance in x/precisebank +// C = 10^12 (conversion factor) +``` + +### WERC20 Precompile + +The WERC20 precompile ([source](https://github.com/cosmos/evm/blob/main/precompiles/werc20/werc20.go)) extends the ERC20 precompile to provide deposit/withdraw functionality: + +```go +// WERC20 wraps ERC20 precompile with additional methods +type Precompile struct { + *erc20.Precompile // Inherits standard ERC20 functionality +} + +// Key methods: +// - Deposit(): Convert native tokens to ERC20 representation +// - Withdraw(): Convert ERC20 representation back to native +// - All ERC20 standard methods (transfer, approve, etc.) +``` + +**Important:** Despite the "deposit" and "withdraw" terminology, no actual wrapping occurs. These methods simply change which interface you're using to interact with the same underlying balance. + +## Real-World Example ```javascript const userAddress = "0x742d35cc6644c068532fddb11B4C36A58D6D3eAb"; -// Query native bank balance +// Query native bank balance (returns uatom units) const bankBalance = await bankContract.balances(userAddress); -console.log(bankBalance); // [{contractAddress: "0x...", amount: "100000000"}] +console.log(bankBalance); // [{denom: "uatom", amount: "100"}] = 100 uatom -// Query ERC20 balance (WATOM) +// Query ERC20 balance (returns aatom units - 18 decimals) const watomBalance = await watomContract.balanceOf(userAddress); -console.log(watomBalance); // "100000000" +console.log(watomBalance); // "100000000000000" = 100 * 10^12 aatom -// Transfer 30 tokens via ERC20 interface -await watomContract.transfer(recipient, "30000000"); +// Transfer 30 uatom worth via ERC20 interface +await watomContract.transfer(recipient, "30000000000000"); // 30 * 10^12 aatom -// Check updated balances - both show 70 tokens -console.log(await bankContract.balances(userAddress)); -console.log(await watomContract.balanceOf(userAddress)); +// Check updated balances - both show 70 uatom worth +console.log(await bankContract.balances(userAddress)); // 70 uatom +console.log(await watomContract.balanceOf(userAddress)); // 70 * 10^12 aatom // Native Cosmos operations also reflect changes automatically -// e.g. cosmos tx bank send cosmos1... cosmos1... 20uatom +// cosmos tx bank send cosmos1... cosmos1... 20uatom +// After this, ERC20 balance would show 50 * 10^12 aatom ``` ## Benefits 1. **Unified Liquidity** - * No fragmented token pools * DeFi protocols use the same liquidity regardless of interface -2. **Reduced Smart Contract Risk** +2. **Reduced Smart Contract Risk** * No wrapping contracts to exploit * Bank module is well-tested and secure -3. **Simplified User Experience** +3. **Simplified User Experience** * No manual wrapping/unwrapping * Seamless Cosmos and EVM integration -4. **Performance & Cost Efficiency** +4. **Performance & Cost Efficiency** * No extra gas for wrapping operations * Direct access to optimized bank module -5. **Developer Friendly** +5. **Developer Friendly** * Use standard ERC20 interfaces without wrapping logic * Compatible with existing EVM tooling @@ -102,22 +149,28 @@ contract LiquidityPool { IERC20 public token; function addLiquidity(uint256 amount) external { + // Works with native token's ERC20 interface + // No wrapping needed - operates on same underlying balance token.transferFrom(msg.sender, address(this), amount); - // No wrapping needed, all operate on the same underlying token } } ``` -### Cross-Ecosystem Operations (JavaScript) +### Cross-Ecosystem Operations ```javascript async function demonstrateUnification() { + // Native Cosmos transfer (6 decimals) await execCmd("cosmos tx bank send cosmos1abc... cosmos1def... 100uatom"); - await watomContract.approve(dexContract.address, "50000000"); - await dexContract.swap(watomContract.address, "50000000"); + // ERC20 operations (18 decimals) + await watomContract.approve(dexContract.address, "50000000000000"); // 50 uatom worth + await dexContract.swap(watomContract.address, "50000000000000"); + // Native staking (6 decimals) await execCmd("cosmos tx staking delegate cosmosvaloper1... 25uatom"); + + // All operations affect the same underlying balance } ``` @@ -132,18 +185,43 @@ async function demonstrateUnification() { | State Management | Dual balances | Single source of truth | | Developer Experience | Must handle wrapping | Standard ERC20 interface | | Interoperability | Manual bridges | Native cross-ecosystem | +| Decimal Precision | Fixed per token | Automatic conversion | + +## Technical Details + +### Reserve Account + +The precisebank module maintains a reserve account to ensure supply consistency: + +```go +// Reserve holds uatom equal to: +// - All fractional balances (< 10^12 aatom per account) +// - Plus remainder to maintain supply invariant +reserve_uatom = sum(all_fractional_balances) / 10^12 + remainder +``` + +This ensures: `Total_aatom = Total_uatom * 10^12 - remainder` where `0 ≤ remainder < 10^12` + +### ERC20 Token Pairs + +The x/erc20 module manages the registration and tracking of token pairs: +- Native Cosmos denoms ↔ ERC20 contract addresses +- Automatic registration for IBC tokens when enabled +- Governance-controlled token pair registration ## Related Technologies -* **WERC20 Precompile:** Implements single token representation for native tokens. See [WERC20 docs](/docs/documentation/smart-contracts/precompiles/werc20). -* **ERC20 Module:** Manages token pairs and infrastructure. See [ERC20 module docs](/docs/documentation/cosmos-sdk/modules/erc20). +* **WERC20 Precompile:** Implements single token representation for native tokens at specific addresses. See [WERC20 docs](/docs/documentation/smart-contracts/precompiles/werc20). +* **ERC20 Module:** Manages token pairs and automatic registration. See [ERC20 module docs](/docs/documentation/cosmos-sdk/modules/erc20). * **Bank Precompile:** Provides direct access to native token balances. See [Bank precompile docs](/docs/documentation/smart-contracts/precompiles/bank). +* **PreciseBank Module:** Handles decimal precision conversion transparently. ## Future Implications * Cross-chain DeFi without liquidity fragmentation -* Easier multi-chain onboarding +* Easier multi-chain onboarding without learning new token models * Native interoperability without complex bridges * Unified developer experience across blockchain ecosystems +* Seamless IBC token integration with automatic ERC20 representation --- \ No newline at end of file diff --git a/docs/documentation/concepts/transactions.mdx b/docs/documentation/concepts/transactions.mdx index 61922efc..b2f0bb73 100644 --- a/docs/documentation/concepts/transactions.mdx +++ b/docs/documentation/concepts/transactions.mdx @@ -10,10 +10,14 @@ Additionally, a transaction needs to be signed using the sender's private key. T In a nutshell, the transaction lifecycle once a signed transaction is submitted to the network is the following: * A transaction hash is cryptographically generated. -* The transaction is broadcasted to the network and added to a transaction pool consisting of all other pending network transactions. -* A validator must pick your transaction and include it in a block in order to verify the transaction and consider it "successful". +* The transaction is validated through CheckTx and added to the mempool (see [Transaction Ordering](#transaction-ordering-and-prioritization) below). +* The transaction is broadcasted to the network peers (only if immediately executable). +* A validator selects your transaction for inclusion in a block based on fee prioritization. +* The transaction is executed and the state change is committed. -For a more detailed explanation of the transaction lifecyle, see [the corresponding section](https://docs.cosmos.network/main/basics/tx-lifecycle). + +For detailed transaction flow and mempool behavior, see the [Mempool documentation](/docs/documentation/concepts/mempool#transaction-flow) and [Cosmos SDK lifecycle](https://docs.cosmos.network/main/basics/tx-lifecycle). + The transaction hash is a unique identifier and can be used to check transaction information, for example, the events emitted, if was successful or not. @@ -21,7 +25,7 @@ Transactions can fail for various reasons. For example, the provided gas or fees Nowadays, transactions can not only perform state transitions on the chain in which are submitted, but also can execute transactions on another blockchains. Interchain transactions are possible through the [Inter-Blockchain Communication protocol (IBC)](https://ibcprotocol.org/). Find a more detailed explanation on the section below. -## Transaction Types[​](#transaction-types "Direct link to Transaction Types") +## Transaction Types Cosmos EVM supports two transaction types: @@ -34,7 +38,7 @@ Although most of the information included on both of these transaction types is Find more information about these two types on the following sections. -### Cosmos Transactions[​](#cosmos-transactions "Direct link to Cosmos Transactions") +### Cosmos Transactions On Cosmos chains, transactions are comprised of metadata held in contexts and `sdk.Msg`s that trigger state changes within a module through the module's Protobuf [Msg service](https://docs.cosmos.network/main/building-modules/msg-services). @@ -51,11 +55,12 @@ A Cosmos transaction includes the following information: To submit a Cosmos transaction, users must use one of the provided clients. -### Ethereum Transactions[​](#ethereum-transactions "Direct link to Ethereum Transactions") +### Ethereum Transactions Ethereum transactions refer to actions initiated by EOAs (externally-owned accounts, managed by humans), rather than internal smart contract calls. Ethereum transactions transform the state of the EVM and therefore must be broadcasted to the entire network. -Ethereum transactions also require a fee, known as `gas`. ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)) introduced the idea of a base fee, along with a priority fee which serves as an incentive for miners to include specific transactions in blocks. +Ethereum transactions also require a fee, known as `gas`. ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)) introduced the idea of a base fee, along with a priority fee which serves as an incentive for validators to include specific transactions in blocks. + There are several categories of Ethereum transactions: @@ -88,9 +93,10 @@ Cosmos EVM supports the following Ethereum transactions. Cosmos EVM is capable of processing Ethereum transactions by wrapping them on a `sdk.Msg`. It achieves this by using the `MsgEthereumTx`. This message encapsulates an Ethereum transaction as an SDK message and contains the necessary transaction data fields. -One remark about the `MsgEthereumTx` is that it implements both the `sdk.Msg` and `sdk.Tx` interfaces (generally SDK messages only implement the former, while the latter is a group of messages bundled together). The reason of this, is because the `MsgEthereumTx` must not be included in a `auth.StdTx` (SDK's standard transaction type) as it performs gas and fee checks using the Ethereum logic from Geth instead of the Cosmos SDK checks done on the auth module `AnteHandler`. +One remark about the `MsgEthereumTx` is that it implements both the `sdk.Msg` and `sdk.Tx` interfaces (generally SDK messages only implement the former, while the latter is a group of messages bundled together). The reason for this is that the `MsgEthereumTx` must not be included in a `auth.StdTx` (SDK's standard transaction type) as it performs gas and fee checks using the Ethereum logic from Geth instead of the Cosmos SDK checks done on the auth module `AnteHandler`. + -#### Ethereum Tx Type[​](#ethereum-tx-type "Direct link to Ethereum Tx Type") +#### Ethereum Tx Type There are three types of transaction types used in Cosmos EVM's [Go Ethereum](https://github.com/ethereum/go-ethereum/blob/b946b7a13b749c99979e312c83dce34cac8dd7b1/core/types/transaction.go#L43-L48) implementation that came from Ethereum Improvement Proposals(EIPs): @@ -102,7 +108,7 @@ There are three types of transaction types used in Cosmos EVM's [Go Ethereum](ht These transaction types represent Ethereum's continuous evolution and improvements to its network, helping address challenges related to scalability, security, and user experience. -### Interchain Transactions[​](#interchain-transactions "Direct link to Interchain Transactions") +### Interchain Transactions Interchain transactions refer to the transfer of digital assets or data between two or more different blockchain networks. @@ -119,7 +125,33 @@ Another possibility is to use the [IBC (Inter-Blockchain Communication)](https:/ Interchain transactions are becoming increasingly important as the number of different blockchain networks and applications continues to grow. They enable the interoperability of different blockchain networks, allowing for greater flexibility and efficiency in the transfer of digital assets and data. -## Transaction Receipts[​](#transaction-receipts "Direct link to Transaction Receipts") +## Transaction Ordering and Prioritization + +In Cosmos EVM, both Ethereum and Cosmos transactions compete fairly for block inclusion: + + + + Transactions are ordered by their effective tips: + - **Ethereum**: `gas_tip_cap` or `min(gas_tip_cap, gas_fee_cap - base_fee)` + - **Cosmos**: `(fee_amount / gas_limit) - base_fee` + - Higher tips = higher priority, regardless of transaction type + + + + **Ethereum transactions** support nonce gaps: + - Correct nonce → immediate broadcast + - Nonce gap → queued locally + - Gap filled → automatic promotion + + **Cosmos transactions** require sequential nonces. + + + + +For detailed mempool behavior and flow diagrams, see [Mempool Architecture](/docs/documentation/concepts/mempool#architecture). + + +## Transaction Receipts A transaction receipt shows data returned by an Ethereum client to represent the result of a particular transaction, including a hash of the transaction, its block number, the amount of gas used, and, in case of deployment of a smart contract, the address of the contract. Additionally, it includes custom information from the events emitted in the smart contract. diff --git a/docs/documentation/differences.mdx b/docs/documentation/differences.mdx index 72024ba1..785c788d 100644 --- a/docs/documentation/differences.mdx +++ b/docs/documentation/differences.mdx @@ -1,6 +1,6 @@ --- -title: "Differences from Ethereum Mainnet" -description: "Comprehensive guide to all differences between Cosmos EVM and Ethereum mainnet that affect developers, infrastructure operators, and users" +title: "EVM Compatibility" +description: "High-level overview of any differences between Cosmos EVM and Ethereum mainnet" mode: "wide" keywords: ['differences', 'ethereum', 'evm', 'cosmos', 'tendermint', 'consensus', 'finality', 'json-rpc', 'compatibility', 'precompiles', 'ibc', 'mev', 'gas', 'basefee', 'uncle blocks', 'reorganization', 'stub methods', 'implementation', 'bft', 'validator', 'staking'] --- @@ -9,18 +9,28 @@ import { footnote } from '/snippets/footnote.mdx' **The EVM module is still in active development:** Some of what is detailed here may change or be removed entirely as development continues. This document represents the current state of the implementation. + + **Implementation Variations:** This document is specific to the current version of [Cosmos-EVM](https://github.com/cosmos/evm) and may not be applicable in part or in full to older versions or Ethermint variants. + + **Critical for Ethermint/Evmos Migrations:** Cosmos EVM has significant architectural differences from Ethermint/Evmos. See [Migration from Evmos/Ethermint](#migration-from-evmosethermint) for breaking changes. + + ## Quick Summary +Cosmos EVM provides Ethereum compatibility on Cosmos chains with significant architectural improvements and differences from both standard Ethereum and Ethermint-based implementations + **Most Important Differences for Developers:** 1. **Instant finality** - No block reorganizations, transactions are final after one block 2. **1-2 second blocks** - Much faster than Ethereum's 12-15 seconds -3. **Configurable base fee** - Can be dynamic (EIP-1559) or disabled, distributed to validators instead of burned -4. **Native IBC** - Built-in cross-chain communication without bridges -5. **Cosmos precompiles** - Direct access to staking, governance, and other Cosmos SDK modules -6. **No pending state** - Transactions are either in mempool or committed -7. **EIP compatibility** - Most Core EIPs supported with some consensus-related exceptions ([see full matrix](/docs/documentation/eip-compatibility)) +3. **Dual Chain IDs** - ⚠️ **Two completely independent IDs** (Cosmos string + EVM integer), unlike Ethermint's single combined format +4. **Experimental pending state** - [Two-tiered mempool](/docs/documentation/concepts/mempool) (local queue + public pool) handles nonce gaps +5. **Configurable base fee** - Can be dynamic (EIP-1559) or disabled, distributed to validators instead of burned +6. **Native IBC** - Built-in cross-chain communication without bridges +7. **Cosmos precompiles** - Direct access to staking, governance, and other Cosmos SDK modules +8. **Token precision** - Native tokens use 18 decimals via [x/precisebank module](/docs/documentation/concepts/single-token-representation) +9. **EIP compatibility** - Most Core EIPs supported with some consensus-related exceptions ([see full matrix](/docs/documentation/eip-support)) ## Core Differences @@ -28,15 +38,15 @@ import { footnote } from '/snippets/footnote.mdx' |--------|------------|--------------|---------| | **Transaction Ordering** | Fee priority and nonce with mempool | Gas price and nonce | Similar ordering with two-tier system | | **Transactions per Block** | Multiple per account | Multiple per account | Full Ethereum compatibility | -| **Mempool** | Two-tiered mempool | Traditional mempool | Supports nonce gaps and batching | +| **Mempool** | Two-tiered (local queue + public pool) | Traditional mempool | Nonce gaps queued locally, valid txs broadcast | | **Block Rewards** | Native token inflation to validators | Block rewards + tips | Different economic model | -| **Consensus** | Tendermint BFT | Proof of Work/Stake | 1-2 second vs 12+ second blocks | -| **Finality** | Immediate (single block) | Probabilistic (12+ confirmations) | No waiting period vs 12-15 minute finality | +| **Consensus** | Tendermint/CometBFT | Proof of Work/Stake | Byzantine Fault Tolerant with 2/3+ validator agreement | +| **Finality** | Immediate (single block, irreversible) | Probabilistic (12+ confirmations) | No waiting period vs 12-15 minute finality | | **Chain Reorganizations** | Not possible | Possible | Event handling simplified | -| **Address Formats** | Dual (0x... and cosmos1...) | Ethereum only (0x...) | Cross-format compatibility | -| **Gas Token** | Native chain token | ETH/derivatives | Chain-specific economics | +| **Address Formats** | Dual (0x... and cosmos1...) | Ethereum only (0x...) | Same account accessible via both formats | +| **Gas Token** | Native chain token (18 decimals via x/precisebank) | ETH/derivatives | Full precision compatibility | | **Base Fee** | Configurable (can be 0 or dynamic) | Dynamic based on demand | Chain-specific configuration | -| **Block Time** | 1-2 seconds | 12-15 seconds (varies by L2) | Faster transaction confirmation | +| **Block Time** | 1-2 seconds (configurable via `timeout_commit`) | 12-15 seconds (varies by L2) | Faster transaction confirmation | | **Cross-chain** | Native IBC | Bridge protocols | Built-in interoperability | ## Gas Price Behavior @@ -49,6 +59,9 @@ uint256 baseFee = block.basefee; // BaseFee is distributed to validators (not burned) // Lower-bounded by global min-gas-price uint256 gasPrice = tx.gasprice; + +// Priority calculation for EVM transactions +uint256 priority = min(tx.gasTipCap, tx.gasFeeCap - baseFee); ``` ```solidity Standard EVM - BASEFEE Burned @@ -57,6 +70,9 @@ uint256 baseFee = block.basefee; // Base fee is burned, not distributed uint256 maxFee = baseFee * 2 + priorityFee; + +// Priority is just the tip +uint256 priority = tx.priorityFeePerGas; ``` @@ -65,178 +81,84 @@ Cosmos EVM supports EIP-1559 with dynamic base fees, but with key differences: - Base fee is **distributed to validators** instead of burned (preserving token economics) - Can be disabled via `NoBaseFee` parameter (returns 0 for `block.basefee`) - Lower-bounded by global `min-gas-price` configuration -- Calculated based on previous block's gas usage when enabled +- Calculated using x/feemarket module based on block utilization +- Transaction priority: `min(gas_tip_cap, gas_fee_cap - base_fee)` for EVM txs - Unlike Ethereum where base fee is burned, Cosmos EVM maintains token supply ## Precompiled Contracts -### Cosmos EVM Precompiles - -Cosmos EVM provides access to native Cosmos SDK modules through precompiled contracts: - -| Module | Address | Purpose | -|--------|---------|---------| -| Staking | `0x0000000000000000000000000000000000000800` | Delegate, undelegate, redelegate tokens | -| Distribution | `0x0000000000000000000000000000000000000801` | Claim rewards, manage withdrawals | -| IBC Transfer | `0x0000000000000000000000000000000000000802` | Cross-chain token transfers | -| Governance | `0x0000000000000000000000000000000000000805` | Submit/vote on proposals | -| Bank | `0x0000000000000000000000000000000000000804` | ERC20-style access to native tokens | -| Slashing | `0x0000000000000000000000000000000000000806` | Validator slashing and jail management | -| Bech32 | `0x0000000000000000000000000000000000000400` | Address format conversion | -| P256 | `0x0000000000000000000000000000000000000100` | P-256 elliptic curve operations | -| ERC20 | Dynamic per token | Standard ERC20 for native Cosmos tokens | -| WERC20 | Dynamic per token | Wrapped native token functionality | - -For detailed usage examples and implementation guides, see [Precompiled Contracts](/docs/documentation/smart-contracts/precompiles). - -### Standard EVM Precompiles - -Standard cryptographic functions available on both implementations: - -| Address | Function | Available on Cosmos EVM | -|---------|----------|-------------------------| -| `0x01` | `ecrecover` | Yes | -| `0x02` | `sha256` | Yes | -| `0x03` | `ripemd160` | Yes | -| `0x04` | `identity` | Yes | -| `0x05` | `modexp` | Yes | -| `0x06` | `ecadd` | Yes | -| `0x07` | `ecmul` | Yes | -| `0x08` | `ecpairing` | Yes | -| `0x09` | `blake2f` | Yes | - -## JSON-RPC Method Support - -Cosmos EVM implements most standard Ethereum JSON-RPC methods with some limitations due to consensus differences: - -| Method Category | Examples | Notes | -|----------------|----------|-------| -| **Core Methods** | `eth_getBalance`, `eth_call`, `eth_sendTransaction`, `eth_getTransactionReceipt` | Full compatibility | -| **Gas Methods** | `eth_gasPrice`, `eth_estimateGas`, `eth_maxPriorityFeePerGas`, `eth_feeHistory` | Full EIP-1559 support | -| **Block Methods** | `eth_blockNumber`, `eth_getBlockByHash`, `eth_getBlockByNumber` | Immediate finality | -| **Mining Stub Methods** | `eth_coinbase`, `eth_mining` (always false), `eth_hashrate` (always 0) | Returns static values for compatibility | -| **Uncle Methods** | `eth_getUncleCount*` (always 0), `eth_getUncleBy*` (always null) | Returns expected values for no uncles | -| **Filter Methods** | `eth_newFilter`, `eth_newBlockFilter`, `eth_getFilterChanges`, `eth_getFilterLogs` | Full filter support with block range limits | -| **Transaction Methods** | `eth_resend`, `eth_sendRawTransaction`, `eth_getTransactionByHash` | Full transaction management | -| **Personal Methods** | `personal_sign`, `personal_newAccount`, `personal_importRawKey`, `personal_sendTransaction`, `personal_ecRecover`, `personal_lockAccount`, `personal_unlockAccount` | Full implementation | -| **Debug Methods** | `debug_traceTransaction`, `debug_traceBlockByNumber`, `debug_traceCall`, `debug_memStats`, `debug_freeOSMemory`, etc. | Full implementation | -| **Admin Methods** | `admin_peers`, `admin_nodeInfo`, `admin_datadir`, `admin_startHTTP`, `admin_stopHTTP`, `admin_startWS`, `admin_stopWS` | Full implementation | + + + Access staking, governance, IBC, and other Cosmos SDK modules via precompiled contracts at fixed addresses. + + + + All standard Ethereum cryptographic precompiles (ecrecover, sha256, etc.) are fully supported. + + -For a complete list of all JSON-RPC methods with implementation status, see the [Ethereum JSON-RPC Methods](/docs/api-reference/ethereum-json-rpc/methods) reference. + +Precompile addresses may vary between implementations. Verify addresses with your specific chain. + -### Additional JSON-RPC Methods - -Cosmos EVM implements these non-standard methods: - -| Method | Purpose | -|--------|---------| -| `eth_getTransactionLogs` | Retrieve logs for a specific transaction | -| `eth_getBlockReceipts` | Get all receipts for a given block | -| `debug_freeOSMemory` | Force garbage collection | -| `debug_setGCPercent` | Configure garbage collector | -| `debug_memStats` | Return detailed memory statistics | -| `debug_setBlockProfileRate` | Set block profiling rate | -| `debug_writeBlockProfile` | Write block profile to file | -| `debug_writeMemProfile` | Write memory profile to file | -| `debug_writeMutexProfile` | Write mutex profile to file | - -### Unimplemented Standard Methods - -These standard Ethereum methods are not available: - -| Method | Category | Reason | -|--------|----------|--------| -| All `trace_*` methods | Trace | Parity/OpenEthereum trace methods not implemented | -| `eth_subscribe` syncing | WebSocket | Only newHeads, logs, and newPendingTransactions events supported | - -### Non-Relevant RPC Namespaces +## JSON-RPC Method Support -The following Ethereum RPC namespaces are not implemented or relevant to Cosmos EVM due to architectural differences: +Cosmos EVM implements most standard Ethereum JSON-RPC methods with modifications for Tendermint consensus: -| Namespace | Purpose | Why Not Relevant | -|-----------|---------|------------------| -| `les_*` | Light Ethereum Subprotocol | Cosmos uses full nodes with Tendermint consensus, no light client protocol | -| `clique_*` | Clique PoA consensus | Cosmos uses Tendermint BFT consensus instead of Proof of Authority | -| `engine_*` | Engine API for PoS | Used for Ethereum's execution/consensus client separation, not applicable with Tendermint | -| `trace_*` | Parity/OpenEthereum tracing | Different tracing implementation, limited `debug_*` methods available instead | -| `miner_*` | Mining operations | No mining in Tendermint BFT consensus, but stub methods respond for compatibility | -| `parity_*` | Parity-specific methods | Client-specific methods not implemented | -| `bor_*` | Polygon Bor consensus | Chain-specific consensus methods not applicable | + + View the complete JSON-RPC methods reference with detailed implementation status for all `eth_*`, `web3_*`, `net_*`, `txpool_*`, `personal_*`, `debug_*`, and `admin_*` namespaces. + -Cosmos EVM focuses on implementing the core `eth_*`, `web3_*`, `net_*`, `txpool_*`, `personal_*`, `debug_*`, and `admin_*` namespaces that are essential for Ethereum compatibility and developer tooling. +### Key RPC Differences +- **Mining methods**: Return stub values (e.g., `eth_mining` always false, `eth_hashrate` always 0) +- **Uncle methods**: Always return 0/null since no uncle blocks exist +- **Pending state**: Full support via experimental mempool with `txpool_*` methods +- **Trace methods**: Not implemented (use `debug_*` methods instead) +- **Light client protocols**: Not supported (`les_*` namespace unavailable) -## Address Format Examples +## Address Formats -Cosmos EVM supports dual address formats with deterministic conversion: + + Same account accessible via both Ethereum (0x) and Cosmos (bech32) formats. Use Bech32 precompile for conversion. + ``` Ethereum: 0x742d35cc6644c068532fddb11B4C36A58D6D3eAb Cosmos: cosmos1wskntvnryr5qxpe4tv5k64rhc6kx6ma4dxjmav ``` -Both formats represent the same account and can be used interchangeably. Use the Bech32 precompile at `0x0000000000000000000000000000000000000400` for conversion. - ## EIP Compatibility -Cosmos EVM implements most Ethereum Improvement Proposals (EIPs) through the Geth EVM, with modifications for Cosmos consensus compatibility. - - - For a comprehensive searchable table of all 339+ Core EIPs with implementation status, see the **[EIP Compatibility Matrix](/docs/documentation/eip-compatibility)**. - - -### Implementation Strategy - -1. **Core EVM Features**: Most opcodes and execution features are inherited directly from Geth EVM -2. **Consensus-Related**: Features related to PoW/PoS consensus are not applicable due to Tendermint BFT -3. **Economic Model**: EIP-1559 implemented with modifications (fees to validators instead of burning) -4. **Future Support**: Critical EIPs like EIP-7702 are actively being developed - -### Key EIP Implementation Differences - -#### EIP-1559 (Dynamic Base Fee) -- **Supported** -- Base fee is **distributed to validators** instead of burned -- Can be disabled via `NoBaseFee` parameter -- Lower-bounded by global `min-gas-price` -- Maintains token supply unlike Ethereum - -#### EIP-155 (Replay Protection) -- **Different implementation** -- Currently enforced at chain level rather than per-transaction -- Plans to align with Geth implementation for better compatibility -- See [GitHub Issue #401](https://github.com/cosmos/evm/issues/401) - -#### EIP-7702 (Set Code for EOAs) -- **Coming in v0.5.0** -- Will enable account abstraction capabilities -- Critical for wallet innovation and user experience improvements - -#### EIP-2681 (Nonce Limit) -- **Partial support** -- Needs stricter overflow checking at AnteHandler -- See [GitHub Issue #400](https://github.com/cosmos/evm/issues/400) - -#### EIP-2930 (Access Lists) -- **Fully supported** -- `eth_createAccessList` method implemented -- Transaction access lists fully functional - -### Consensus-Related EIPs (Not Applicable) - -The following categories of EIPs are not applicable due to Cosmos using Tendermint BFT consensus: -- **Difficulty adjustments**: EIP-100, 649, 1234, 2384, 3554, 4345, 5133 -- **Proof-of-Work to Proof-of-Stake transition**: EIP-3675 -- **Beacon chain operations**: EIP-4788, 4895, 7002, etc. -- **Validator deposits and withdrawals**: EIP-6110, 7044, 7045, 7251, 7514 + + View the comprehensive matrix of all 339+ Core EIPs with detailed implementation status, searchable by EIP number or feature. + + +### Key EIP Differences + + + + Unlike Ethereum where base fees are burned, Cosmos EVM distributes them to validators. Can be disabled via `NoBaseFee` parameter. + + + + Currently enforced at chain level through AnteHandler rather than per-transaction. Migration to Geth implementation planned. + + + + Difficulty adjustments, PoW→PoS transition, and beacon chain operations don't apply to Tendermint BFT consensus. + + ## Additional Key Differences ### Transaction Execution -- **Two-tiered mempool**: Local queue for gapped nonces, public pool for executable transactions -- **Fee-priority ordering**: Transactions ordered by fee and nonce similar to Ethereum +- **Two-tiered mempool**: Local queue for nonce-gapped transactions (not broadcast), public pool for executable transactions (broadcast to network) +- **Fee-priority ordering**: Fair competition between EVM and Cosmos txs based on effective tips +- **Nonce gap handling**: Transactions with future nonces queued locally until gaps fill (unique to Cosmos EVM) - **Multiple transactions per block per EOA**: Full support for batched transactions -- **Transaction replacement**: Same-nonce transactions with higher fees replace lower-fee ones +- **Transaction replacement**: Same-nonce transactions with higher fees replace lower-fee ones (configurable threshold) +- **Automatic promotion**: Queued transactions promoted to public pool when gaps are filled - **Deterministic execution**: Same transaction order for all validators due to BFT consensus ### State and Storage @@ -245,16 +167,210 @@ The following categories of EIPs are not applicable due to Cosmos using Tendermi - **Archive nodes**: Full historical state requires archive node configuration ### Network Architecture -- **Validator set**: Fixed validator set with stake-based voting power +- **Validator set**: Fixed validator set with stake-based voting power (2/3+ required for consensus) +- **Validator selection**: Deterministic round-robin proposer selection based on voting power - **No light clients**: Full or archive nodes only, no light client protocol -- **P2P differences**: Uses Tendermint P2P networking instead of devp2p +- **P2P differences**: Uses Tendermint/CometBFT P2P networking instead of devp2p +- **Block propagation**: Gossip protocol with immediate finality, no uncle blocks ### Developer Considerations -- **Gas refunds**: At least 50% of unused gas is refunded (configurable) +- **Pending state**: Full pending state support via experimental mempool with nonce gap handling +- **Transaction batching**: Send multiple transactions at once, out-of-order delivery handled automatically +- **Gas refunds**: 50% of unused gas is refunded by default (configurable via params) - **Revert reasons**: Full revert reason strings always available - **Event reliability**: Events are immediately final, no need to wait for confirmations - **Block timestamps**: More reliable due to BFT consensus requirements - **Account abstraction**: EIP-7702 support coming in v0.5.0 for EOA code delegation -- **Opcode differences**: DIFFICULTY/PREVRANDAO returns 0, COINBASE returns empty address -- **Chain ID**: Enforced at chain level rather than per-transaction (EIP-155) +- **Opcode differences**: DIFFICULTY/PREVRANDAO returns 0, COINBASE returns validator operator address +- **Chain ID**: **Two completely separate independent values** (unlike Ethermint's single ID) - Cosmos string ID for consensus/IBC and EVM integer ID for transactions/wallets +- **Token precision**: x/precisebank module provides 18 decimal precision for native tokens - **Storage costs**: No state rent mechanisms, pruning configurable per node + +## Migration from Evmos/Ethermint + + + **Breaking Changes:** Migrating from Evmos/Ethermint requires careful planning due to fundamental architectural differences. + + +### Critical Architectural Differences + +#### Chain ID System (Most Important) + + + + **Two Completely Independent Chain IDs:** + - **Cosmos Chain ID**: String format (`"cosmosevm-1"`) + - Used by: CometBFT, IBC, native SDK transactions + - Can change during upgrades + - **EVM Chain ID**: Integer format (`9000`) + - Used by: MetaMask, EIP-155, smart contracts + - **Must remain constant** across upgrades + + ```json + // genesis.json + "chain_id": "cosmosevm-1", // Cosmos ID + + // EVM params + "evm_chain_id": "9000" // EVM ID (separate) + ``` + + + + **Single Combined Chain ID:** + - Format: `{identifier}_{EIP155}-{version}` + - Example: `evmos_9001-2` + - Both Cosmos and EVM derive from same value + - EIP-155 chain ID extracted from format + + + +### State-Breaking Parameter Changes + +#### Module Reorganization +- **x/evm → x/vm**: EVM module renamed to VM module +- **Organization change**: `evmos/os` → `cosmos/evm` +- **Protobuf changes**: Package names updated from `evmos` to `cosmos` +- **x/precisebank**: New module for 18-decimal precision + +#### Feemarket Updates + + + **Breaking change from Evmos/OS:** + ```diff + - MinGasPrice string // Old: string type + + MinGasPrice sdk.Dec // New: decimal type + ``` + - Global minimum gas price parameter enforcement + - More precise fee calculations + - Requires genesis state migration + + + + - Legacy base fee implementation removed + - New EIP-1559 implementation with validator distribution + - Base fee no longer burned (distributed to validators) + - Migration requires parameter updates + + + + - Fair competition between EVM and Cosmos transactions + - EVM priority: `min(gas_tip_cap, gas_fee_cap - base_fee)` + - Cosmos priority: `(fee_amount / gas_limit) - base_fee` + - Different from Evmos priority system + + + +#### VM/App Configuration Changes +- **EIP Support**: Different set of activated EIPs +- **Config Structure**: New configuration format +- **Precompile Changes**: Removed x/authz dependency +- **IBC Denoms**: Changed from `erc20/` to `erc20` prefix + +### Migration Checklist + + + + Export existing chain state before any migration attempts + + + + Configure separate Cosmos and EVM chain IDs (cannot use Ethermint format) + + + + Rename all references from x/evm to x/vm in code and configs + + + + Migrate feemarket parameters including min_gas_price type + + + + Verify all precompiles, transactions, and IBC functionality + + + + + For detailed migration steps, see [Migration Guide v0.3 to v0.4](/docs/documentation/integration/migration-v0.3-to-v0.4) + + +### Common Migration Issues + + +These issues frequently cause migration failures: + + +1. **Chain ID Format Errors** + - Cannot use Ethermint format `evmos_9001-2` + - Must configure two separate IDs + - EVM chain ID must remain constant post-migration + +2. **Genesis Migration Failures** + - Module name changes (x/evm → x/vm) + - Parameter type updates in feemarket + - Protobuf package name changes + +3. **Precompile Address Changes** + - Different addresses than Evmos/Ethermint + - Some precompiles removed or added + - Verify all addresses before migration + +4. **Transaction Pool Behavior** + - Different mempool architecture + - Changed priority calculations + - New nonce gap handling + +## Known Limitations and Considerations + +### Implementation-Specific Variations +- **Precompile addresses**: May vary between chains - verify with your specific implementation +- **Transaction replacement thresholds**: Configurable per chain, not standardized at 10% +- **Validator rewards**: Distribution mechanics may vary based on chain parameters +- **Mempool configuration**: Two-tiered system is experimental and optional + +### Missing Ethereum Features +- **State sync protocols**: No snap sync or fast sync equivalents +- **Light client protocol**: Only full nodes supported +- **MEV infrastructure**: No native flashbots or MEV-boost equivalents +- **Archive node pruning**: Different pruning strategies than Ethereum + +### Areas Under Development +- **EIP-155 enforcement**: Moving from chain-level to per-transaction validation +- **EIP-7702 (Account Abstraction)**: Planned for v0.5.0 +- **EIP-2681 (Nonce Limit)**: Stricter overflow checking needed +- **Parameter migrations**: Ongoing work to simplify upgrades from Evmos/Ethermint ([Issue #424](https://github.com/cosmos/evm/issues/424)) + +## Related Documentation + + + + Two-tiered system with nonce gap handling + + + + EIP-1559 implementation details + + + + Dual chain ID architecture + + + + Ethereum-compatible pending transactions + + + + Add EVM to your Cosmos chain + + + + Upgrade to latest version + + + +## Additional Resources + +- [Cosmos EVM GitHub Repository](https://github.com/cosmos/evm) +- [EVM Module Documentation](https://github.com/cosmos/evm/tree/main/docs) +- [Migration Guides](https://github.com/cosmos/evm/tree/main/docs/migrations) +- [Issue Tracker](https://github.com/cosmos/evm/issues) diff --git a/docs/documentation/eip-compatibility.mdx b/docs/documentation/eip-support.mdx similarity index 100% rename from docs/documentation/eip-compatibility.mdx rename to docs/documentation/eip-support.mdx diff --git a/docs/documentation/getting-started/index.mdx b/docs/documentation/getting-started/index.mdx index 8c1df812..cff225ab 100644 --- a/docs/documentation/getting-started/index.mdx +++ b/docs/documentation/getting-started/index.mdx @@ -20,5 +20,5 @@ For teams building or integrating chains: Integrate Cosmos EVM into new or existing Cosmos SDK chains with step-by-step guidance. - Integrate EVM → + Integrate EVM → \ No newline at end of file diff --git a/docs/documentation/cosmos-sdk/integrate.mdx b/docs/documentation/integration/evm-module-integration.mdx similarity index 100% rename from docs/documentation/cosmos-sdk/integrate.mdx rename to docs/documentation/integration/evm-module-integration.mdx diff --git a/docs/documentation/integration/mempool-integration.mdx b/docs/documentation/integration/mempool-integration.mdx new file mode 100644 index 00000000..57d97b33 --- /dev/null +++ b/docs/documentation/integration/mempool-integration.mdx @@ -0,0 +1,350 @@ +--- +title: "EVM Mempool Integration" +description: "Step-by-step guide to integrate the experimental EVM mempool in your Cosmos SDK chain" +--- + + +This mempool implementation is **experimental** and under active development. It is intended for testing and evaluation purposes. Use in production environments is **not recommended** without thorough testing and risk assessment. +Please [report issues](https://github.com/cosmos/evm/issues/new) and submit feedback to help improve stability. + + +## Overview + +This guide explains how to integrate the EVM mempool in your Cosmos SDK chain to enable Ethereum-compatible transaction flows, including out-of-order transactions and nonce gap handling. + +## Prerequisites + +Before integrating the EVM mempool: + +1. **EVM Module Integration**: Complete the [EVM module integration](/docs/documentation/integration/evm-module-integration) first +2. **FeeMarket Module**: Ensure the feemarket module is properly configured for base fee calculations +3. **Compatible AnteHandler**: Your ante handler must support EVM transaction validation + +## Quick Start + +### Step 1: Add EVM Mempool to App Struct + +Update your `app/app.go` to include the EVM mempool: + +```go +type App struct { + *baseapp.BaseApp + // ... other keepers + + // Cosmos EVM keepers + FeeMarketKeeper feemarketkeeper.Keeper + EVMKeeper *evmkeeper.Keeper + EVMMempool *evmmempool.ExperimentalEVMMempool +} +``` + +### Step 2: Configure Mempool in NewApp Constructor + + +The mempool must be initialized **after** the antehandler has been set in the app. + + +Add the following configuration in your `NewApp` constructor: + +```go +// Set the EVM priority nonce mempool +if evmtypes.GetChainConfig() != nil { + mempoolConfig := &evmmempool.EVMMempoolConfig{ + AnteHandler: app.GetAnteHandler(), + BlockGasLimit: 100_000_000, + } + + evmMempool := evmmempool.NewExperimentalEVMMempool( + app.CreateQueryContext, + logger, + app.EVMKeeper, + app.FeeMarketKeeper, + app.txConfig, + app.clientCtx, + mempoolConfig, + ) + app.EVMMempool = evmMempool + + // Set the global mempool for RPC access + if err := evmmempool.SetGlobalEVMMempool(evmMempool); err != nil { + panic(err) + } + + // Replace BaseApp mempool + app.SetMempool(evmMempool) + + // Set custom CheckTx handler for nonce gap support + checkTxHandler := evmmempool.NewCheckTxHandler(evmMempool) + app.SetCheckTxHandler(checkTxHandler) + + // Set custom PrepareProposal handler + abciProposalHandler := baseapp.NewDefaultProposalHandler(evmMempool, app) + abciProposalHandler.SetSignerExtractionAdapter( + evmmempool.NewEthSignerExtractionAdapter( + sdkmempool.NewDefaultSignerExtractionAdapter(), + ), + ) + app.SetPrepareProposal(abciProposalHandler.PrepareProposalHandler()) +} +``` + +## Configuration Options + +The `EVMMempoolConfig` struct provides several configuration options for customizing the mempool behavior: + +### Minimal Configuration + +```go +mempoolConfig := &evmmempool.EVMMempoolConfig{ + AnteHandler: app.GetAnteHandler(), + BlockGasLimit: 100_000_000, // 100M gas limit +} +``` + +### Full Configuration Options + +```go +type EVMMempoolConfig struct { + // Required: AnteHandler for transaction validation + AnteHandler sdk.AnteHandler + + // Required: Block gas limit for transaction selection + BlockGasLimit uint64 + + // Optional: Custom TxPool (defaults to LegacyPool) + TxPool *txpool.TxPool + + // Optional: Custom Cosmos pool (defaults to PriorityNonceMempool) + CosmosPool sdkmempool.ExtMempool + + // Optional: Custom broadcast function for promoted transactions + BroadCastTxFn func(txs []*ethtypes.Transaction) error +} +``` + +### Custom Cosmos Mempool Configuration + +The mempool uses a `PriorityNonceMempool` for Cosmos transactions by default. You can customize the priority calculation: + +```go +// Define custom priority calculation for Cosmos transactions +priorityConfig := sdkmempool.PriorityNonceMempoolConfig[math.Int]{ + TxPriority: sdkmempool.TxPriority[math.Int]{ + GetTxPriority: func(goCtx context.Context, tx sdk.Tx) math.Int { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return math.ZeroInt() + } + + // Get fee in bond denomination + bondDenom := "uatom" // or your chain's bond denom + fee := feeTx.GetFee() + found, coin := fee.Find(bondDenom) + if !found { + return math.ZeroInt() + } + + // Calculate gas price: fee_amount / gas_limit + gasPrice := coin.Amount.Quo(math.NewIntFromUint64(feeTx.GetGas())) + return gasPrice + }, + Compare: func(a, b math.Int) int { + return a.BigInt().Cmp(b.BigInt()) // Higher values have priority + }, + MinValue: math.ZeroInt(), + }, +} + +mempoolConfig := &evmmempool.EVMMempoolConfig{ + AnteHandler: app.GetAnteHandler(), + BlockGasLimit: 100_000_000, + CosmosPool: sdkmempool.NewPriorityMempool(priorityConfig), +} +``` + +### Custom Block Gas Limit + +Different chains may require different gas limits based on their capacity: + +```go +// Example: 50M gas limit for lower capacity chains +mempoolConfig := &evmmempool.EVMMempoolConfig{ + AnteHandler: app.GetAnteHandler(), + BlockGasLimit: 50_000_000, +} +``` + +## Architecture Components + +The EVM mempool consists of several key components: + +### ExperimentalEVMMempool + +The main coordinator implementing Cosmos SDK's `ExtMempool` interface (`mempool/mempool.go`). + +**Key Methods**: +- `Insert(ctx, tx)`: Routes transactions to appropriate pools +- `Select(ctx, filter)`: Returns unified iterator over all transactions +- `Remove(tx)`: Handles transaction removal with EVM-specific logic +- `InsertInvalidNonce(txBytes)`: Queues nonce-gapped EVM transactions locally + +### CheckTx Handler + +Custom transaction validation that handles nonce gaps specially (`mempool/check_tx.go`). + +**Special Handling**: On `ErrNonceGap` for EVM transactions: +```go +if errors.Is(err, ErrNonceGap) { + // Route to local queue instead of rejecting + err := mempool.InsertInvalidNonce(request.Tx) + // Must intercept error and return success to EVM client + return interceptedSuccess +} +``` + +### TxPool + +Direct port of Ethereum's transaction pool managing both pending and queued transactions (`mempool/txpool/`). + +**Key Features**: +- Uses `vm.StateDB` interface for Cosmos state compatibility +- Implements `BroadcastTxFn` callback for transaction promotion +- Cosmos-specific reset logic for instant finality + +### PriorityNonceMempool + +Standard Cosmos SDK mempool for non-EVM transactions with fee-based prioritization. + +**Default Priority Calculation**: +```go +// Calculate effective gas price +priority = (fee_amount / gas_limit) - base_fee +``` + +## Transaction Type Routing + +The mempool handles different transaction types appropriately: + +### Ethereum Transactions (MsgEthereumTx) + +- **Tier 1 (Local)**: EVM TxPool handles nonce gaps and promotion +- **Tier 2 (Network)**: CometBFT broadcasts executable transactions + +### Cosmos Transactions (Bank, Staking, Gov, etc.) + +- **Direct to Tier 2**: Always go directly to CometBFT mempool +- **Standard Flow**: Follow normal Cosmos SDK validation and broadcasting +- **Priority-Based**: Use PriorityNonceMempool for fee-based ordering + +## Unified Transaction Selection + +During block building, both transaction types compete fairly based on their effective tips: + +```go +// Simplified selection logic +func SelectTransactions() Iterator { + evmTxs := GetPendingEVMTransactions() // From local TxPool + cosmosTxs := GetPendingCosmosTransactions() // From Cosmos mempool + + return NewUnifiedIterator(evmTxs, cosmosTxs) // Fee-based priority +} +``` + +**Fee Comparison**: +- **EVM**: `gas_tip_cap` or `min(gas_tip_cap, gas_fee_cap - base_fee)` +- **Cosmos**: `(fee_amount / gas_limit) - base_fee` +- **Selection**: Higher effective tip gets selected first + +## Testing Your Integration + +### Verify Nonce Gap Handling + +Test that transactions with nonce gaps are properly queued: + +```javascript +// Send transactions out of order +await wallet.sendTransaction({nonce: 100, ...}); // OK: Immediate execution +await wallet.sendTransaction({nonce: 102, ...}); // OK: Queued locally (gap) +await wallet.sendTransaction({nonce: 101, ...}); // OK: Fills gap, both execute +``` + +### Test Transaction Replacement + +Verify that higher-fee transactions replace lower-fee ones: + +```javascript +// Send initial transaction +const tx1 = await wallet.sendTransaction({ + nonce: 100, + gasPrice: parseUnits("20", "gwei") +}); + +// Replace with higher fee +const tx2 = await wallet.sendTransaction({ + nonce: 100, // Same nonce + gasPrice: parseUnits("30", "gwei") // Higher fee +}); +// tx1 is replaced by tx2 +``` + +### Verify Batch Deployments + +Test typical deployment scripts (like Uniswap) that send many transactions at once: + +```javascript +// Deploy multiple contracts in quick succession +const factory = await Factory.deploy(); +const router = await Router.deploy(factory.address); +const multicall = await Multicall.deploy(); +// All transactions should queue and execute properly +``` + +## Monitoring and Debugging + +Use the [txpool RPC methods](/docs/api-reference/ethereum-json-rpc/methods#txpool-methods) to monitor mempool state: + +- `txpool_status`: Get pending and queued transaction counts +- `txpool_content`: View all transactions in the pool +- `txpool_inspect`: Get human-readable transaction summaries +- `txpool_contentFrom`: View transactions from specific addresses + +## Common Issues and Solutions + +### Transactions Not Being Queued + +**Issue**: Transactions with nonce gaps are rejected instead of queued. + +**Solution**: Ensure the CheckTx handler is properly configured: +```go +checkTxHandler := evmmempool.NewCheckTxHandler(evmMempool) +app.SetCheckTxHandler(checkTxHandler) +``` + +### Mempool Not Accepting Transactions + +**Issue**: Mempool requires block 1+ before accepting transactions. + +**Solution**: Ensure your chain has produced at least one block before testing. + +### RPC Methods Not Available + +**Issue**: `txpool_*` methods return "method not found". + +**Solution**: Set the global mempool for RPC access: +```go +if err := evmmempool.SetGlobalEVMMempool(evmMempool); err != nil { + panic(err) +} +``` + +### Transaction Promotion Not Working + +**Issue**: Queued transactions aren't promoted when gaps are filled. + +**Solution**: Verify the BroadcastTxFn callback is configured and the promotion background process is running. + +## Related Documentation + +- [Mempool Concepts](/docs/documentation/concepts/mempool) - Understanding mempool behavior and design +- [EVM Module Integration](/docs/documentation/integration/evm-module-integration) - Prerequisites for mempool integration +- [JSON-RPC Methods](/docs/api-reference/ethereum-json-rpc/methods#txpool-methods) - Mempool query methods \ No newline at end of file diff --git a/docs/documentation/integration/migration-v0.3-to-v0.4.mdx b/docs/documentation/integration/migration-v0.3-to-v0.4.mdx new file mode 100644 index 00000000..75248a56 --- /dev/null +++ b/docs/documentation/integration/migration-v0.3-to-v0.4.mdx @@ -0,0 +1,338 @@ +--- +title: "Migration: v0.3.0 to v0.4.0" +description: "Step-by-step guide for upgrading Cosmos EVM from v0.3.x to v0.4.0" +--- + + +This migration requires careful planning and testing. Create a backup of your state and configuration before proceeding. + + +## Executive Checklist + +Quick overview of required changes: + + + + - Create an upgrade branch and freeze schema-affecting changes + - Export pre-upgrade state and archive node configs + + + + - Bump `cosmos/evm` to v0.4.0 + - Align Cosmos SDK/IBC/CometBFT constraints + + + + - Rewire keepers and AppModule (imports, constructors, `RegisterServices`) + - Update app constructor return type + - Add pending transaction listener support + + + + - Audit and migrate EVM & FeeMarket params + - Implement store/params migrations in UpgradeHandler + + + +## Preparation + +### Create Upgrade Branch + +```bash +# Create a dedicated branch for the upgrade +git switch -c upgrade/evm-v0.4 + +# Ensure clean build and tests pass pre-upgrade +go test ./... + +# Snapshot current params/genesis for comparison +evmd export > pre-upgrade-genesis.json +``` + +## Dependency Updates + +### Update go.mod + + + +```diff go.mod +- github.com/cosmos/evm v0.3.1 ++ github.com/cosmos/evm v0.4.0 +``` + +```bash Terminal +# Update dependencies +go mod tidy + +# Verify no conflicts +go mod verify +``` + + + + +Check for transitive dependency bumps in `go.sum`, particularly: +- `google.golang.org/protobuf` +- `github.com/gofrs/flock` +- `github.com/consensys/gnark-crypto` + + +## App Constructor Changes + +### Update Return Type + +The app's `newApp` function must now return `evmserver.Application` instead of `servertypes.Application`. + +```go cmd/myapp/cmd/root.go +import ( + evmserver "github.com/cosmos/evm/server" +) + +func (a appCreator) newApp( + l log.Logger, + db dbm.DB, + traceStore io.Writer, + appOpts servertypes.AppOptions, +) evmserver.Application { // Changed from servertypes.Application + // ... app construction +} +``` + +### Add SDK Wrapper for CLI Commands + +Some commands still expect the SDK type, so create a wrapper: + +```go cmd/myapp/cmd/root.go +// Create wrapper for SDK-expecting commands +sdkAppCreatorWrapper := func( + l log.Logger, + d dbm.DB, + w io.Writer, + ao servertypes.AppOptions, +) servertypes.Application { + return ac.newApp(l, d, w, ao) +} + +// Use wrapper for pruning and snapshot commands +rootCmd.AddCommand( + pruning.Cmd(sdkAppCreatorWrapper, myapp.DefaultNodeHome), + snapshot.Cmd(sdkAppCreatorWrapper), +) +``` + +## Pending Transaction Listener Support + +### Add Required Imports + +```go app/app.go +import ( + "github.com/cosmos/evm/ante" + "github.com/ethereum/go-ethereum/common" +) +``` + +### Update App Structure + +```go app/app.go +type MyApp struct { + // ... existing fields + + // Add pending transaction listeners + pendingTxListeners []ante.PendingTxListener +} + +// Add registration method +func (app *MyApp) RegisterPendingTxListener(listener func(common.Hash)) { + app.pendingTxListeners = append(app.pendingTxListeners, listener) +} +``` + +## Precompile Updates + +### Add Address Codec Support + + + + ```go app/keepers/precompiles.go + import ( + "cosmossdk.io/core/address" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + sdk "github.com/cosmos/cosmos-sdk/types" + ) + + type Optionals struct { + AddressCodec address.Codec // for gov/staking + ValidatorAddrCodec address.Codec // for slashing + ConsensusAddrCodec address.Codec // for slashing + } + + func defaultOptionals() Optionals { + return Optionals{ + AddressCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()), + ValidatorAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), + ConsensusAddrCodec: addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), + } + } + ``` + + + + ```go app/keepers/precompiles.go + type Option func(*Optionals) + + func WithAddressCodec(c address.Codec) Option { + return func(o *Optionals) { o.AddressCodec = c } + } + + func WithValidatorAddrCodec(c address.Codec) Option { + return func(o *Optionals) { o.ValidatorAddrCodec = c } + } + + func WithConsensusAddrCodec(c address.Codec) Option { + return func(o *Optionals) { o.ConsensusAddrCodec = c } + } + ``` + + + +### Update Precompile Factory + +```go app/keepers/precompiles.go +func NewAvailableStaticPrecompiles( + ctx context.Context, + // ... other params + opts ...Option, // Add options parameter +) map[common.Address]vm.PrecompiledContract { + options := defaultOptionals() + for _, opt := range opts { + opt(&options) + } + // ... rest of implementation +} +``` + +### Update Individual Precompiles + + + + The ICS-20 precompile now requires `bankKeeper` as the first parameter: + + ```diff + - ibcTransferPrecompile, err := ics20precompile.NewPrecompile( + - stakingKeeper, + + ibcTransferPrecompile, err := ics20precompile.NewPrecompile( + + bankKeeper, // New first parameter + + stakingKeeper, + transferKeeper, + &channelKeeper, + // ... + ``` + + + + The governance precompile now requires an `AddressCodec`: + + ```diff + - govPrecompile, err := govprecompile.NewPrecompile(govKeeper, cdc) + + govPrecompile, err := govprecompile.NewPrecompile(govKeeper, cdc, options.AddressCodec) + ``` + + + +## Testing the Migration + +### Build Verification + +```bash +# Compile all packages +go build ./... + +# Run tests +go test ./... +``` + +### Smoke Tests + + + + Ensure RPC starts cleanly without errors + + + + Deploy a simple contract and verify events/logs + + + + Send EIP-1559 transactions and confirm base fee behavior + + + + Register a pending transaction listener and verify it receives hashes + + + +## Deployment Checklist + + + + - [ ] Package new binary + - [ ] Prepare Cosmovisor upgrade folder (if applicable) + - [ ] Confirm all validators build the same commit + - [ ] Verify no `replace` directives in `go.mod` + - [ ] Share `app.toml` diff if defaults changed + + + + - [ ] Monitor mempool and pending transaction logs + - [ ] Verify base fee progression + - [ ] Check contract events for first 20-50 blocks + - [ ] Confirm precompiles execute correctly + - [ ] Validate ICS-20 transfers (if applicable) + + + +## Common Issues and Solutions + +### CLI Command Panics + +**Problem**: `pruning` or `snapshot` commands panic with wrong type error. + +**Solution**: Ensure you're passing `sdkAppCreatorWrapper` (not `ac.newApp`) to these commands. + +### ICS-20 Precompile Build Error + +**Problem**: ICS-20 precompile fails to build. + +**Solution**: Ensure `bankKeeper` is passed as the first parameter to `NewPrecompile`. + +### Governance Precompile Address Parsing + +**Problem**: Governance precompile fails to parse addresses. + +**Solution**: Provide the correct `AddressCodec` via defaults or `WithAddressCodec(...)`. + +### Pending Transaction Listeners Not Firing + +**Problem**: Registered listeners never receive transaction hashes. + +**Solution**: Ensure `RegisterPendingTxListener` is called during app construction or module initialization. + +## Verification Steps + +Before finalizing the upgrade: + + + - [ ] `go.mod` has no `replace` lines for `github.com/cosmos/evm` + - [ ] Node boots with expected RPC namespaces + - [ ] Contracts deploy and call successfully + - [ ] Events stream correctly + - [ ] Fee market behaves as expected + - [ ] ICS-20 transfers work (if applicable) + - [ ] All precompiles execute without errors + + +## Additional Resources + +- [Cosmos EVM v0.4.0 Release Notes](https://github.com/cosmos/evm/releases/tag/v0.4.0) +- [Migration Support](https://github.com/cosmos/evm/issues) +- [Upgrade Handler Examples](https://github.com/cosmos/evm/tree/main/docs/migrations) \ No newline at end of file diff --git a/docs/documentation/overview.mdx b/docs/documentation/overview.mdx index a9df35f7..da8d05f8 100644 --- a/docs/documentation/overview.mdx +++ b/docs/documentation/overview.mdx @@ -1,5 +1,5 @@ --- -title: "Cosmos EVM Overview" +title: "Overview" description: "Cosmos EVM is an open-source implementation for teams who want to build their own chain, rollup, or EVM-compatible application using the best Layer 1 stack in blockchain, including full Ethereum composability. Build custom modules for your SDK and get fast finality, complete decentralization, and built-in interoperability between EVM and Cosmos." mode: "wide" --- @@ -7,16 +7,16 @@ mode: "wide" import { EthereumIcon, CosmosLogo } from '/snippets/icons.mdx' - + Deploy existing Solidity contracts, use familiar tools like MetaMask, Hardhat, and Foundry - + Access staking, governance, IBC, and other Cosmos SDK modules directly from smart contracts - + 1-2 second block times with instant finality via Tendermint consensus - + Built-in IBC support for interoperability across a growing list of ecosystems diff --git a/snippets/rpc-methods-viewer.jsx b/snippets/rpc-methods-viewer.jsx index fa8a88d8..000bb5f1 100644 --- a/snippets/rpc-methods-viewer.jsx +++ b/snippets/rpc-methods-viewer.jsx @@ -1,206 +1,27 @@ -import { CurlIcon, TypeScriptIcon, GoIcon, RustIcon, PythonIcon, CSharpIcon, APIIcon, NetworkIcon, EthereumIcon, SmartContractIcon } from '/snippets/icons.mdx'; - -export default function RPCMethodsViewer() { - const [selectedNamespace, setSelectedNamespace] = React.useState('all'); - const [searchTerm, setSearchTerm] = React.useState(''); - const [expandedMethods, setExpandedMethods] = React.useState({}); - const [selectedLanguage, setSelectedLanguage] = React.useState({}); - const [rpcEndpoint, setRpcEndpoint] = React.useState(''); - const [isValidEndpoint, setIsValidEndpoint] = React.useState(false); - const [isInvalidEndpoint, setIsInvalidEndpoint] = React.useState(false); - const [requestResults, setRequestResults] = React.useState({}); - const [isLoading, setIsLoading] = React.useState({}); +export default function RPCMethodsViewerVersionA() { + const [selectedNamespace, setSelectedNamespace] = useState('all'); + const [searchTerm, setSearchTerm] = useState(''); + const [expandedMethods, setExpandedMethods] = useState({}); + const [selectedLanguage, setSelectedLanguage] = useState({}); + const [rpcEndpoint, setRpcEndpoint] = useState(''); + const [isValidEndpoint, setIsValidEndpoint] = useState(false); + const [isInvalidEndpoint, setIsInvalidEndpoint] = useState(false); + const [requestResults, setRequestResults] = useState({}); + const [isLoading, setIsLoading] = useState({}); + const [copied, setCopied] = useState({}); const languages = [ - { id: 'curl', name: 'cURL', icon: CurlIcon }, - { id: 'typescript', name: 'TypeScript', icon: TypeScriptIcon }, - { id: 'go', name: 'Go', icon: GoIcon }, - { id: 'rust', name: 'Rust', icon: RustIcon }, - { id: 'python', name: 'Python', icon: PythonIcon }, - { id: 'csharp', name: 'C#', icon: CSharpIcon } + { id: 'curl', name: 'cURL' }, + { id: 'javascript', name: 'JavaScript' }, + { id: 'python', name: 'Python' }, + { id: 'go', name: 'Go' }, + { id: 'rust', name: 'Rust' }, + { id: 'csharp', name: 'C#' } ]; - const generateCodeExamples = (method, params = []) => { - const endpoint = 'http://localhost:8545'; - const paramValues = params.length > 0 ? params : []; - - return { - curl: `curl -X POST -H "Content-Type: application/json" \\ - -d '{ - "jsonrpc": "2.0", - "id": 1, - "method": "${method}", - "params": ${JSON.stringify(paramValues, null, 2).split('\n').map((line, i) => i === 0 ? line : ' ' + line).join('\n')} - }' \\ - ${endpoint}`, - - typescript: `import { ethers } from 'ethers'; - -const provider = new ethers.JsonRpcProvider('${endpoint}'); - -// Using ethers.js -const result = await provider.send('${method}', ${JSON.stringify(paramValues)}); -console.log(result); - -// Using fetch -const response = await fetch('${endpoint}', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - jsonrpc: '2.0', - id: 1, - method: '${method}', - params: ${JSON.stringify(paramValues)} - }) -}); -const data = await response.json(); -console.log(data.result);`, - - go: `package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" -) - -type JSONRPCRequest struct { - JSONRPC string \`json:"jsonrpc"\` - ID int \`json:"id"\` - Method string \`json:"method"\` - Params []interface{} \`json:"params"\` -} - -func main() { - request := JSONRPCRequest{ - JSONRPC: "2.0", - ID: 1, - Method: "${method}", - Params: []interface{}{${paramValues.map(p => typeof p === 'string' ? `"${p}"` : p).join(', ')}}, - } - - jsonData, _ := json.Marshal(request) - resp, err := http.Post("${endpoint}", "application/json", bytes.NewBuffer(jsonData)) - if err != nil { - panic(err) - } - defer resp.Body.Close() - - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println(string(body)) -}`, - - rust: `use serde_json::json; -use reqwest; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let client = reqwest::Client::new(); - - let response = client - .post("${endpoint}") - .json(&json!({ - "jsonrpc": "2.0", - "id": 1, - "method": "${method}", - "params": ${JSON.stringify(paramValues)} - })) - .send() - .await?; - - let result = response.json::().await?; - println!("{:#?}", result); - - Ok(()) -}`, - - python: `import requests -import json - -# Using requests library -payload = { - "jsonrpc": "2.0", - "id": 1, - "method": "${method}", - "params": ${JSON.stringify(paramValues)} -} - -response = requests.post("${endpoint}", json=payload) -result = response.json() -print(result) - -# Using web3.py (if applicable) -from web3 import Web3 -w3 = Web3(Web3.HTTPProvider("${endpoint}")) -result = w3.provider.make_request("${method}", ${JSON.stringify(paramValues)}) -print(result)`, - - csharp: `using System; -using System.Net.Http; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; - -class Program -{ - static async Task Main() - { - var rpcUrl = "${endpoint}"; - - var requestObj = new - { - jsonrpc = "2.0", - id = 1, - method = "${method}", - @params = ${JSON.stringify(paramValues)} - }; - - var jsonBody = JsonSerializer.Serialize(requestObj); - using var http = new HttpClient(); - using var content = new StringContent(jsonBody, Encoding.UTF8, "application/json"); - - try - { - var response = await http.PostAsync(rpcUrl, content); - var responseBody = await response.Content.ReadAsStringAsync(); - - if (!response.IsSuccessStatusCode) - { - Console.Error.WriteLine($"HTTP {(int)response.StatusCode}: {responseBody}"); - return; - } - - using var doc = JsonDocument.Parse(responseBody); - var root = doc.RootElement; - - if (root.TryGetProperty("error", out var error)) - { - Console.Error.WriteLine($"RPC Error: {error}"); - } - else if (root.TryGetProperty("result", out var result)) - { - Console.WriteLine(result.ToString()); - } - else - { - Console.Error.WriteLine("Unexpected RPC response format."); - } - } - catch (Exception ex) - { - Console.Error.WriteLine($"Request failed: {ex.Message}"); - } - } -}` - }; - }; - const namespaces = { web3: { name: 'Web3', - icon: APIIcon, methods: [ { name: 'web3_clientVersion', @@ -229,13 +50,6 @@ class Program response: { result: '0x1b84adea42d5b7d192fd8a61a85b25abe0757e9a65cab1da470258914053823f' } - }, - { - name: 'Hash empty string', - params: ['0x'], - response: { - result: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' - } } ] } @@ -243,7 +57,6 @@ class Program }, net: { name: 'Net', - icon: NetworkIcon, methods: [ { name: 'net_version', @@ -281,11 +94,6 @@ class Program name: 'Node is listening', params: [], response: { result: true } - }, - { - name: 'Node not listening', - params: [], - response: { result: false } } ] } @@ -293,7 +101,6 @@ class Program }, eth: { name: 'Eth', - icon: EthereumIcon, methods: [ { name: 'eth_blockNumber', @@ -321,11 +128,6 @@ class Program name: 'Get balance at latest block', params: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1', 'latest'], response: { result: '0x0234c8a3397aab58' } - }, - { - name: 'Get balance at specific block', - params: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1', '0x1b4'], - response: { result: '0x0' } } ] }, @@ -348,78 +150,11 @@ class Program gas: '0xc350', gasPrice: '0x4a817c800', hash: '0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b', - input: '0x68656c6c6f21', nonce: '0x15', to: '0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb', - transactionIndex: '0x41', - value: '0xf3dbb76162000', - v: '0x25', - r: '0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea', - s: '0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c' + value: '0xf3dbb76162000' } } - }, - { - name: 'Transaction not found', - params: ['0x0000000000000000000000000000000000000000000000000000000000000000'], - response: { result: null } - } - ] - }, - { - name: 'eth_sign', - description: 'Signs data with an account', - implemented: true, - private: true, - params: [ - { name: 'address', type: 'address', description: 'Account address', example: '0x9b2055d370f73ec7d8a03e965129118dc8f5bf83' }, - { name: 'message', type: 'bytes', description: 'Data to sign', example: '0xdeadbeef' } - ], - examples: [ - { - name: 'Sign message', - params: ['0x9b2055d370f73ec7d8a03e965129118dc8f5bf83', '0xdeadbeef'], - response: { - result: '0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b' - } - } - ] - }, - { - name: 'eth_sendTransaction', - description: 'Creates and submits a transaction', - implemented: true, - private: true, - params: [ - { - name: 'transaction', - type: 'object', - description: 'Transaction object', - fields: [ - { name: 'from', type: 'address', description: 'Sender address' }, - { name: 'to', type: 'address', description: 'Recipient address' }, - { name: 'gas', type: 'quantity', description: 'Gas limit (optional)' }, - { name: 'gasPrice', type: 'quantity', description: 'Gas price (optional)' }, - { name: 'value', type: 'quantity', description: 'Value to send (optional)' }, - { name: 'data', type: 'bytes', description: 'Transaction data (optional)' }, - { name: 'nonce', type: 'quantity', description: 'Nonce (optional)' } - ] - } - ], - examples: [ - { - name: 'Send transaction', - params: [{ - from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155', - to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567', - gas: '0x76c0', - gasPrice: '0x9184e72a000', - value: '0x9184e72a', - data: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675' - }], - response: { - result: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331' - } } ] }, @@ -435,16 +170,6 @@ class Program name: 'Valid transaction', params: ['0xf86c01850465...'], response: { result: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331' } - }, - { - name: 'Invalid transaction', - params: ['0xinvalid'], - response: { - error: { - code: -32602, - message: 'Invalid params' - } - } } ] }, @@ -481,1542 +206,364 @@ class Program response: { result: '0x00000000000000000000000000000000000000000000000000000000000186a0' } } ] - }, + } + ] + }, + personal: { + name: 'Personal', + methods: [ { - name: 'eth_getLogs', - description: 'Get logs matching filter criteria', + name: 'personal_newAccount', + description: 'Generate new private key and store in key store', implemented: true, + private: true, params: [ - { - name: 'filter', - type: 'object', - description: 'Filter object', - fields: [ - { name: 'fromBlock', type: 'string', description: 'Starting block' }, - { name: 'toBlock', type: 'string', description: 'Ending block' }, - { name: 'address', type: 'address', description: 'Contract address(es)' }, - { name: 'topics', type: 'array', description: 'Topic filters' } - ] - } + { name: 'passphrase', type: 'string', description: 'Passphrase for encryption', example: 'This is the passphrase' } ], examples: [ { - name: 'Get Transfer events', - params: [{ - fromBlock: '0x1', - toBlock: 'latest', - address: '0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907', - topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] - }], - response: { - result: [{ - address: '0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907', - topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'], - data: '0x00000000000000000000000000000000000000000000000000000000000186a0', - blockNumber: '0x1b4', - transactionHash: '0x67....', - transactionIndex: '0x0', - blockHash: '0x78....', - logIndex: '0x0', - removed: false - }] - } - } - ] - }, - { - name: 'eth_coinbase', - description: 'Get the mining rewards recipient address', - implemented: true, - params: [], - examples: [ - { - name: 'Get coinbase', - params: [], + name: 'Create account', + params: ['This is the passphrase'], response: { - result: '0x7cB61D4117AE31a12E393a1Cfa3BaC666481D02E' + result: '0xf0e4086ad1c6aab5d42161d5baaae2f9ad0571c0' } } ] - }, + } + ] + }, + debug: { + name: 'Debug', + methods: [ { - name: 'eth_mining', - description: 'Check if client is actively mining', + name: 'debug_traceTransaction', + description: 'Trace transaction execution', implemented: true, - params: [], - examples: [ + private: true, + params: [ + { name: 'txHash', type: 'hash', description: 'Transaction hash', example: '0x4ed38df88f88...' }, { - name: 'Check mining status', - params: [], - response: { - result: false - } + name: 'config', + type: 'object', + description: 'Trace config (optional)', + fields: [ + { name: 'tracer', type: 'string', description: 'Tracer type (callTracer, prestateTracer)' }, + { name: 'timeout', type: 'string', description: 'Execution timeout' } + ] } - ] - }, - { - name: 'eth_hashrate', - description: 'Get current hashrate', - implemented: true, - params: [], + ], examples: [ { - name: 'Get hashrate', - params: [], + name: 'Basic trace', + params: ['0x4ed38df88f88...', {}], response: { - result: '0x0' + result: { + gas: 21000, + failed: false, + returnValue: '0x', + structLogs: [] + } } } ] - }, + } + ] + }, + txpool: { + name: 'TxPool', + methods: [ { - name: 'eth_chainId', - description: 'Get chain ID for replay-protected transactions', + name: 'txpool_status', + description: 'Get number of pending and queued transactions', implemented: true, params: [], examples: [ { - name: 'Get chain ID', + name: 'Get pool status', params: [], response: { - result: '0x40000' - } - } - ] - }, - { - name: 'eth_protocolVersion', - description: 'Returns the current ethereum protocol version', - implemented: true, - params: [], - examples: [ - { - name: 'Get protocol version', - params: [], - response: { result: '0x41' } - } - ] - }, - { - name: 'eth_syncing', - description: 'Returns sync status or false', - implemented: true, - params: [], - examples: [ - { - name: 'Not syncing', - params: [], - response: { result: false } - }, - { - name: 'Currently syncing', - params: [], - response: { - result: { - startingBlock: '0x384', - currentBlock: '0x386', - highestBlock: '0x454' - } - } - } - ] - }, - { - name: 'eth_gasPrice', - description: 'Returns current gas price', - implemented: true, - params: [], - examples: [ - { - name: 'Get gas price', - params: [], - response: { result: '0x4a817c800' } - } - ] - }, - { - name: 'eth_accounts', - description: 'Returns list of addresses owned by client', - implemented: true, - params: [], - examples: [ - { - name: 'Get accounts', - params: [], - response: { result: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1'] } - } - ] - }, - { - name: 'eth_getStorageAt', - description: 'Returns value from a storage position at an address', - implemented: true, - params: [ - { name: 'address', type: 'address', description: 'Storage address', example: '0x295a70b2de5e3953354a6a8344e616ed314d7251' }, - { name: 'position', type: 'quantity', description: 'Integer position in storage', example: '0x0' }, - { name: 'block', type: 'string', description: 'Block number or tag', example: 'latest' } - ], - examples: [ - { - name: 'Get storage value', - params: ['0x295a70b2de5e3953354a6a8344e616ed314d7251', '0x0', 'latest'], - response: { result: '0x00000000000000000000000000000000000000000000000000000000000004d2' } - } - ] - }, - { - name: 'eth_getTransactionCount', - description: 'Returns the number of transactions sent from an address', - implemented: true, - params: [ - { name: 'address', type: 'address', description: 'The address', example: '0x407d73d8a49eeb85d32cf465507dd71d507100c1' }, - { name: 'block', type: 'string', description: 'Block number or tag', example: 'latest' } - ], - examples: [ - { - name: 'Get transaction count', - params: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1', 'latest'], - response: { result: '0x1' } - } - ] - }, - { - name: 'eth_getBlockTransactionCountByNumber', - description: 'Returns the number of transactions in a block by number', - implemented: true, - params: [ - { name: 'blockNumber', type: 'string', description: 'Block number or tag', example: '0x1' } - ], - examples: [ - { - name: 'Get transaction count', - params: ['0x1'], - response: { result: '0x10' } - } - ] - }, - { - name: 'eth_getBlockTransactionCountByHash', - description: 'Returns the number of transactions in a block by hash', - implemented: true, - params: [ - { name: 'blockHash', type: 'hash', description: 'Block hash', example: '0x8101cc04aea3341a6d4b3ced715e3f38de1e72867d6c0db5f5247d1a42fbb085' } - ], - examples: [ - { - name: 'Get transaction count', - params: ['0x8101cc04aea3341a6d4b3ced715e3f38de1e72867d6c0db5f5247d1a42fbb085'], - response: { result: '0x10' } - } - ] - }, - { - name: 'eth_getCode', - description: 'Returns code at a given address', - implemented: true, - params: [ - { name: 'address', type: 'address', description: 'The address', example: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b' }, - { name: 'block', type: 'string', description: 'Block number or tag', example: 'latest' } - ], - examples: [ - { - name: 'Get contract code', - params: ['0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'latest'], - response: { result: '0x600160008035811a818181146012578301005b601b6001356025565b8060005260206000f25b600060078202905091905056' } - } - ] - }, - { - name: 'eth_estimateGas', - description: 'Estimates gas needed for a transaction', - implemented: true, - params: [ - { - name: 'transaction', - type: 'object', - description: 'Transaction call object', - fields: [ - { name: 'from', type: 'address', description: 'Address to call from (optional)' }, - { name: 'to', type: 'address', description: 'Contract address' }, - { name: 'gas', type: 'quantity', description: 'Gas limit (optional)' }, - { name: 'gasPrice', type: 'quantity', description: 'Gas price (optional)' }, - { name: 'value', type: 'quantity', description: 'Value to send (optional)' }, - { name: 'data', type: 'bytes', description: 'Call data' } - ] - } - ], - examples: [ - { - name: 'Estimate gas', - params: [{ - from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155', - to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567', - value: '0x9184e72a' - }], - response: { result: '0x5208' } - } - ] - }, - { - name: 'eth_getBlockByNumber', - description: 'Returns block information by number', - implemented: true, - params: [ - { name: 'blockNumber', type: 'string', description: 'Block number or tag', example: '0x1b4' }, - { name: 'fullTransactions', type: 'boolean', description: 'Return full transaction objects', example: true } - ], - examples: [ - { - name: 'Get block by number', - params: ['0x1b4', true], - response: { - result: { - number: '0x1b4', - hash: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - parentHash: '0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5', - transactions: [] - } - } - } - ] - }, - { - name: 'eth_getBlockByHash', - description: 'Returns block information by hash', - implemented: true, - params: [ - { name: 'blockHash', type: 'hash', description: 'Block hash', example: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331' }, - { name: 'fullTransactions', type: 'boolean', description: 'Return full transaction objects', example: true } - ], - examples: [ - { - name: 'Get block by hash', - params: ['0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', true], - response: { - result: { - number: '0x1b4', - hash: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - parentHash: '0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5', - transactions: [] - } - } - } - ] - }, - { - name: 'eth_getTransactionReceipt', - description: 'Returns the receipt of a transaction', - implemented: true, - params: [ - { name: 'hash', type: 'hash', description: 'Transaction hash', example: '0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238' } - ], - examples: [ - { - name: 'Get transaction receipt', - params: ['0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238'], - response: { - result: { - transactionHash: '0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238', - transactionIndex: '0x1', - blockNumber: '0xb', - blockHash: '0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b', - cumulativeGasUsed: '0x33bc', - gasUsed: '0x4dc', - contractAddress: null, - logs: [], - status: '0x1' - } - } - } - ] - }, - { - name: 'eth_getTransactionByBlockHashAndIndex', - description: 'Returns transaction by block hash and index', - implemented: true, - params: [ - { name: 'blockHash', type: 'hash', description: 'Block hash', example: '0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4' }, - { name: 'index', type: 'quantity', description: 'Transaction index', example: '0x0' } - ], - examples: [ - { - name: 'Get transaction by index', - params: ['0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4', '0x0'], - response: { - result: { - blockHash: '0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4', - blockNumber: '0x5daf3b', - from: '0xa7d9ddbe1f17865597fbd27ec712455208b6b76d', - gas: '0xc350', - gasPrice: '0x4a817c800', - hash: '0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b', - input: '0x', - nonce: '0x15', - to: '0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb', - transactionIndex: '0x0', - value: '0xf3dbb76162000' - } - } - } - ] - }, - { - name: 'eth_getTransactionByBlockNumberAndIndex', - description: 'Returns transaction by block number and index', - implemented: true, - params: [ - { name: 'blockNumber', type: 'string', description: 'Block number or tag', example: '0x1' }, - { name: 'index', type: 'quantity', description: 'Transaction index', example: '0x0' } - ], - examples: [ - { - name: 'Get transaction by index', - params: ['0x1', '0x0'], - response: { - result: { - blockHash: '0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4', - blockNumber: '0x1', - from: '0xa7d9ddbe1f17865597fbd27ec712455208b6b76d', - gas: '0xc350', - gasPrice: '0x4a817c800', - hash: '0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b', - input: '0x', - nonce: '0x15', - to: '0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb', - transactionIndex: '0x0', - value: '0xf3dbb76162000' - } - } - } - ] - }, - { - name: 'eth_newFilter', - description: 'Creates a filter for logs', - implemented: true, - params: [ - { - name: 'filter', - type: 'object', - description: 'Filter options', - fields: [ - { name: 'fromBlock', type: 'string', description: 'Starting block' }, - { name: 'toBlock', type: 'string', description: 'Ending block' }, - { name: 'address', type: 'address', description: 'Contract address(es)' }, - { name: 'topics', type: 'array', description: 'Topic filters' } - ] - } - ], - examples: [ - { - name: 'Create filter', - params: [{ - fromBlock: '0x1', - toBlock: 'latest', - address: '0x8888f1f195afa192cfee860698584c030f4c9db1', - topics: ['0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b'] - }], - response: { result: '0x1' } - } - ] - }, - { - name: 'eth_newBlockFilter', - description: 'Creates a filter for new blocks', - implemented: true, - params: [], - examples: [ - { - name: 'Create block filter', - params: [], - response: { result: '0x1' } - } - ] - }, - { - name: 'eth_newPendingTransactionFilter', - description: 'Creates a filter for pending transactions', - implemented: true, - params: [], - examples: [ - { - name: 'Create pending transaction filter', - params: [], - response: { result: '0x1' } - } - ] - }, - { - name: 'eth_uninstallFilter', - description: 'Uninstalls a filter', - implemented: true, - params: [ - { name: 'filterId', type: 'quantity', description: 'Filter ID', example: '0xb' } - ], - examples: [ - { - name: 'Uninstall filter', - params: ['0xb'], - response: { result: true } - } - ] - }, - { - name: 'eth_getFilterChanges', - description: 'Polling method for a filter', - implemented: true, - params: [ - { name: 'filterId', type: 'quantity', description: 'Filter ID', example: '0x16' } - ], - examples: [ - { - name: 'Get filter changes', - params: ['0x16'], - response: { - error: { - code: -32000, - message: 'Filter not found' - } - } - } - ] - }, - { - name: 'eth_getFilterLogs', - description: 'Returns all logs matching filter', - implemented: true, - params: [ - { name: 'filterId', type: 'quantity', description: 'Filter ID', example: '0x16' } - ], - examples: [ - { - name: 'Get filter logs', - params: ['0x16'], - response: { - error: { - code: -32000, - message: 'Filter not found' - } - } - } - ] - }, - { - name: 'eth_maxPriorityFeePerGas', - description: 'Returns the current max priority fee per gas', - implemented: true, - params: [], - examples: [ - { - name: 'Get max priority fee', - params: [], - response: { result: '0x3b9aca00' } - } - ] - }, - { - name: 'eth_feeHistory', - description: 'Returns fee history', - implemented: true, - params: [ - { name: 'blockCount', type: 'quantity', description: 'Number of blocks', example: '0x5' }, - { name: 'newestBlock', type: 'string', description: 'Newest block', example: 'latest' }, - { name: 'rewardPercentiles', type: 'array', description: 'Percentiles', example: [20, 30] } - ], - examples: [ - { - name: 'Get fee history', - params: ['0x5', 'latest', [20, 30]], - response: { - result: { - oldestBlock: '0x3', - reward: [['0x0', '0x0'], ['0x0', '0x0'], ['0x0', '0x0'], ['0x0', '0x0'], ['0x0', '0x0']], - baseFeePerGas: ['0x0', '0x0', '0x0', '0x0', '0x0', '0x0'], - gasUsedRatio: [0, 0, 0, 0, 0] - } - } - } - ] - }, - { - name: 'eth_getProof', - description: 'Returns the merkle proof for a given account', - implemented: true, - issue: 'Requires block height > 2', - params: [ - { name: 'address', type: 'address', description: 'Account address', example: '0x7F0d15C7FAae65896648C8273B6d7E43f58Fa842' }, - { name: 'storageKeys', type: 'array', description: 'Storage keys', example: ['0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'] }, - { name: 'block', type: 'string', description: 'Block number or tag', example: 'latest' } - ], - examples: [ - { - name: 'Get account proof', - params: [ - '0x7F0d15C7FAae65896648C8273B6d7E43f58Fa842', - ['0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'], - 'latest' - ], - response: { - result: { - address: '0x7F0d15C7FAae65896648C8273B6d7E43f58Fa842', - accountProof: [], - balance: '0x0', - codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - nonce: '0x0', - storageHash: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', - storageProof: [] - } - } - } - ] - }, - { - name: 'eth_getUncleCountByBlockHash', - description: 'Get uncle count by block hash', - implemented: true, - params: [ - { name: 'blockHash', type: 'hash', description: 'Block hash', example: '0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4' } - ], - examples: [ - { - name: 'Get uncle count', - params: ['0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4'], - response: { - result: '0x0' - } - } - ] - }, - { - name: 'eth_getUncleCountByBlockNumber', - description: 'Get uncle count by block number', - implemented: true, - params: [ - { name: 'blockNumber', type: 'string', description: 'Block number or tag', example: 'latest' } - ], - examples: [ - { - name: 'Get uncle count', - params: ['latest'], - response: { - result: '0x0' - } - } - ] - }, - { - name: 'eth_getPendingTransactions', - description: 'Returns pending transactions (Cosmos-specific)', - implemented: true, - params: [], - examples: [ - { - name: 'Get pending transactions', - params: [], - response: { - result: [] - } - } - ] - }, - { - name: 'eth_pendingTransactions', - description: 'Standard Ethereum pending transactions method', - implemented: true, - params: [], - examples: [ - { - name: 'Get pending transactions', - params: [], - response: { - error: { - code: -32601, - message: 'Method not found' - } - } - } - ] - }, - { - name: 'eth_getTransactionLogs', - description: 'Get logs for a specific transaction (Cosmos-specific)', - implemented: true, - params: [ - { name: 'txHash', type: 'hash', description: 'Transaction hash', example: '0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1' } - ], - examples: [ - { - name: 'Get transaction logs', - params: ['0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1'], - response: { - result: [] - } - } - ] - }, - { - name: 'eth_getBlockReceipts', - description: 'Get all receipts for a given block (Cosmos-specific)', - implemented: true, - params: [ - { name: 'blockNumber', type: 'string', description: 'Block number or tag', example: 'latest' } - ], - examples: [ - { - name: 'Get block receipts', - params: ['latest'], - response: { - result: [] - } - } - ] - }, - { - name: 'eth_resend', - description: 'Resend transaction with different gas parameters', - implemented: true, - params: [ - { - name: 'transaction', - type: 'object', - description: 'Original transaction', - fields: [ - { name: 'from', type: 'address', description: 'Sender' }, - { name: 'to', type: 'address', description: 'Recipient' }, - { name: 'gas', type: 'quantity', description: 'Gas limit' }, - { name: 'gasPrice', type: 'quantity', description: 'Gas price' }, - { name: 'value', type: 'quantity', description: 'Value' }, - { name: 'data', type: 'data', description: 'Input data' }, - { name: 'nonce', type: 'quantity', description: 'Nonce' } - ] - }, - { name: 'gasPrice', type: 'quantity', description: 'New gas price', example: '0x3b9aca00' }, - { name: 'gasLimit', type: 'quantity', description: 'New gas limit', example: '0x5208' } - ], - examples: [ - { - name: 'Resend with higher gas', - params: [ - { - from: '0x0', - to: '0x0', - gas: '0x5208', - gasPrice: '0x3b9aca00', - value: '0x0', - data: '0x', - nonce: '0x0' - }, - '0x4a817c800', - '0x5208' - ], - response: { - result: '0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1' - } - } - ] - }, - { - name: 'eth_createAccessList', - description: 'Generate access list for transaction (EIP-2930)', - implemented: true, - params: [ - { - name: 'transaction', - type: 'object', - description: 'Transaction call object', - fields: [ - { name: 'from', type: 'address', description: 'Sender address' }, - { name: 'to', type: 'address', description: 'Contract address' }, - { name: 'gas', type: 'quantity', description: 'Gas limit' }, - { name: 'gasPrice', type: 'quantity', description: 'Gas price' }, - { name: 'value', type: 'quantity', description: 'Value' }, - { name: 'data', type: 'data', description: 'Input data' } - ] - }, - { name: 'blockNumber', type: 'string', description: 'Block number or tag', example: 'latest' } - ], - examples: [ - { - name: 'Generate access list', - params: [ - { - from: '0x0', - to: '0x0', - gas: '0x5208', - gasPrice: '0x3b9aca00', - value: '0x0', - data: '0x' - }, - 'latest' - ], - response: { - error: { - code: -32601, - message: 'Method not found' - } - } - } - ] - } - ] - }, - personal: { - name: 'Personal', - icon: SmartContractIcon, - methods: [ - { - name: 'personal_importRawKey', - description: 'Import unencrypted private key into key store', - implemented: true, - private: true, - params: [ - { name: 'privkey', type: 'string', description: 'Hex encoded private key', example: 'c5bd76cd0cd948de17a31261567d219576e992d9066fe1a6bca97496dec634e2c8e06f8949773b300b9f73fabbbc7710d5d6691e96bcf3c9145e15daf6fe07b9' }, - { name: 'password', type: 'string', description: 'Password for encryption', example: 'the key is this' } - ], - examples: [ - { - name: 'Import key', - params: ['c5bd76cd0cd948de17a31261567d219576e992d9066fe1a6bca97496dec634e2c8e06f8949773b300b9f73fabbbc7710d5d6691e96bcf3c9145e15daf6fe07b9', 'the key is this'], - response: { - result: '0x3b7252d007059ffc82d16d022da3cbf9992d2f70' - } - } - ] - }, - { - name: 'personal_listAccounts', - description: 'List addresses for accounts managed by node', - implemented: true, - private: true, - params: [], - examples: [ - { - name: 'List accounts', - params: [], - response: { - result: ['0x3b7252d007059ffc82d16d022da3cbf9992d2f70', '0xddd64b4712f7c8f1ace3c145c950339eddaf221d', '0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0'] - } - } - ] - }, - { - name: 'personal_lockAccount', - description: 'Remove private key from memory', - implemented: true, - issue: 'Not supported - always returns false', - private: true, - params: [ - { name: 'address', type: 'address', description: 'Account to lock', example: '0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0' } - ], - examples: [ - { - name: 'Lock account', - params: ['0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0'], - response: { - result: true - } - } - ] - }, - { - name: 'personal_newAccount', - description: 'Generate new private key and store in key store', - implemented: true, - private: true, - params: [ - { name: 'passphrase', type: 'string', description: 'Passphrase for encryption', example: 'This is the passphrase' } - ], - examples: [ - { - name: 'Create account', - params: ['This is the passphrase'], - response: { - result: '0xf0e4086ad1c6aab5d42161d5baaae2f9ad0571c0' - } - } - ] - }, - { - name: 'personal_unlockAccount', - description: 'Decrypt account key in memory (always returns false)', - implemented: true, - issue: 'Not supported - always returns false', - private: true, - params: [ - { name: 'address', type: 'address', description: 'Account address', example: '0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0' }, - { name: 'passphrase', type: 'string', description: 'Account passphrase', example: 'secret passphrase' }, - { name: 'duration', type: 'number', description: 'Unlock duration in seconds', example: 30 } - ], - examples: [ - { - name: 'Unlock for 30 seconds', - params: ['0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0', 'secret passphrase', 30], - response: { - result: true - } - } - ] - }, - { - name: 'personal_sendTransaction', - description: 'Validate passphrase and submit transaction', - implemented: true, - private: true, - params: [ - { - name: 'transaction', - type: 'object', - description: 'Transaction object', - fields: [ - { name: 'from', type: 'address', description: 'Sender address' }, - { name: 'to', type: 'address', description: 'Recipient address' }, - { name: 'value', type: 'quantity', description: 'Value to send' } - ] - }, - { name: 'passphrase', type: 'string', description: 'Account passphrase', example: 'passphrase' } - ], - examples: [ - { - name: 'Send transaction', - params: [ - { - from: '0x3b7252d007059ffc82d16d022da3cbf9992d2f70', - to: '0xddd64b4712f7c8f1ace3c145c950339eddaf221d', - value: '0x16345785d8a0000' - }, - 'passphrase' - ], - response: { - result: '0xd2a31ec1b89615c8d1f4d08fe4e4182efa4a9c0d5758ace6676f485ea60e154c' - } - } - ] - }, - { - name: 'personal_sign', - description: 'Sign message with account', - implemented: true, - private: true, - params: [ - { name: 'message', type: 'bytes', description: 'Message to sign', example: '0xdeadbeef' }, - { name: 'account', type: 'address', description: 'Account address', example: '0x3b7252d007059ffc82d16d022da3cbf9992d2f70' }, - { name: 'password', type: 'string', description: 'Account password', example: 'password' } - ], - examples: [ - { - name: 'Sign message', - params: ['0xdeadbeef', '0x3b7252d007059ffc82d16d022da3cbf9992d2f70', 'password'], - response: { - result: '0xf9ff74c86aefeb5f6019d77280bbb44fb695b4d45cfe97e6eed7acd62905f4a85034d5c68ed25a2e7a8eeb9baf1b8401e4f865d92ec48c1763bf649e354d900b1c' - } - } - ] - }, - { - name: 'personal_ecRecover', - description: 'Recover address from signature', - implemented: true, - private: true, - params: [ - { name: 'message', type: 'bytes', description: 'Original message', example: '0xdeadbeef' }, - { name: 'signature', type: 'string', description: 'Signature from personal_sign', example: '0xf9ff74c86aefeb5f6019d77280bbb44fb695b4d45cfe97e6eed7acd62905f4a85034d5c68ed25a2e7a8eeb9baf1b8401e4f865d92ec48c1763bf649e354d900b1c' } - ], - examples: [ - { - name: 'Recover address', - params: ['0xdeadbeef', '0xf9ff74c86aefeb5f6019d77280bbb44fb695b4d45cfe97e6eed7acd62905f4a85034d5c68ed25a2e7a8eeb9baf1b8401e4f865d92ec48c1763bf649e354d900b1c'], - response: { - result: '0x3b7252d007059ffc82d16d022da3cbf9992d2f70' - } - } - ] - }, - { - name: 'personal_initializeWallet', - description: 'Initialize new wallet at URL', - implemented: false, - private: true, - params: [ - { name: 'url', type: 'string', description: 'Wallet URL', example: 'keystore://path/to/wallet' } - ], - examples: [ - { - name: 'Initialize wallet', - params: ['keystore://path/to/wallet'], - response: { - result: '0xNewPrivateKey' - } - } - ] - }, - { - name: 'personal_unpair', - description: 'Delete wallet pairing', - implemented: false, - private: true, - params: [ - { name: 'url', type: 'string', description: 'Wallet URL', example: 'keystore://path/to/wallet' }, - { name: 'pin', type: 'string', description: 'Pairing password', example: '1234' } - ], - examples: [ - { - name: 'Unpair wallet', - params: ['keystore://path/to/wallet', '1234'], - response: { - result: true - } - } - ] - }, - { - name: 'personal_listWallets', - description: 'List wallets managed by node', - implemented: true, - private: true, - params: [], - examples: [ - { - name: 'List wallets', - params: [], - response: { - result: null - } - } - ] - } - ] - }, - debug: { - name: 'Debug', - icon: SmartContractIcon, - methods: [ - { - name: 'debug_traceTransaction', - description: 'Trace transaction execution', - implemented: true, - private: true, - issue: 'Height issues in current implementation', - params: [ - { name: 'txHash', type: 'hash', description: 'Transaction hash', example: '0x4ed38df88f88...' }, - { - name: 'config', - type: 'object', - description: 'Trace config (optional)', - fields: [ - { name: 'tracer', type: 'string', description: 'Tracer type (callTracer, prestateTracer)' }, - { name: 'timeout', type: 'string', description: 'Execution timeout' } - ] - } - ], - examples: [ - { - name: 'Basic trace', - params: ['0x4ed38df88f88...', {}], - response: { - result: { - gas: 21000, - failed: false, - returnValue: '0x', - structLogs: [] - } - } - }, - { - name: 'Call tracer', - params: ['0x4ed38df88f88...', { tracer: 'callTracer' }], - response: { - result: { - type: 'CALL', - from: '0x...', - to: '0x...', - value: '0x0', - gas: '0x5208', - gasUsed: '0x5208', - input: '0x', - output: '0x' - } - } - } - ] - }, - { - name: 'debug_traceBlockByNumber', - description: 'Replay block by number', - implemented: true, - private: true, - params: [ - { name: 'blockNumber', type: 'string', description: 'Block number', example: '0xe' }, - { name: 'config', type: 'object', description: 'Trace config (optional)' } - ], - examples: [ - { - name: 'Trace block', - params: ['0xe', {}], - response: { - result: [{ - result: { - failed: false, - gas: 21000, - returnValue: '0x', - structLogs: [] - } - }] - } - } - ] - }, - { - name: 'debug_traceBlockByHash', - description: 'Replay block by hash', - implemented: true, - private: true, - params: [ - { name: 'blockHash', type: 'hash', description: 'Block hash', example: '0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4' }, - { name: 'config', type: 'object', description: 'Trace config (optional)' } - ], - examples: [ - { - name: 'Trace block', - params: ['0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4', {}], - response: { - result: [{ - result: { - failed: false, - gas: 21000, - returnValue: '0x', - structLogs: [] - } - }] - } - } - ] - }, - { - name: 'debug_freeOSMemory', - description: 'Force garbage collection', - implemented: true, - private: true, - params: [], - examples: [ - { - name: 'Free memory', - params: [], - response: { - result: null - } - } - ] - }, - { - name: 'debug_setGCPercent', - description: 'Set garbage collection target percentage', - implemented: true, - private: true, - params: [ - { name: 'percent', type: 'number', description: 'GC percentage', example: 100 } - ], - examples: [ - { - name: 'Set GC to 100%', - params: [100], - response: { - result: 100 - } - } - ] - }, - { - name: 'debug_memStats', - description: 'Get runtime memory statistics', - implemented: true, - private: true, - params: [], - examples: [ - { - name: 'Get memory stats', - params: [], - response: { - result: { - Alloc: 83328680, - TotalAlloc: 451796592, - Sys: 166452520, - HeapAlloc: 83328680, - HeapSys: 153452544, - HeapInuse: 101883904, - HeapObjects: 299114, - NumGC: 18 - } - } - } - ] - }, - { - name: 'debug_setBlockProfileRate', - description: 'Set goroutine block profile rate', - implemented: true, - private: true, - params: [ - { name: 'rate', type: 'number', description: 'Profile rate', example: 1 } - ], - examples: [ - { - name: 'Enable profiling', - params: [1], - response: { - result: null - } - } - ] - }, - { - name: 'debug_writeBlockProfile', - description: 'Write goroutine blocking profile', - implemented: true, - private: true, - params: [ - { name: 'file', type: 'string', description: 'Output file path', example: 'block.prof' } - ], - examples: [ - { - name: 'Write profile', - params: ['block.prof'], - response: { - result: null - } - } - ] - }, - { - name: 'debug_writeMemProfile', - description: 'Write allocation profile', - implemented: true, - private: true, - params: [ - { name: 'file', type: 'string', description: 'Output file path', example: 'mem.prof' } - ], - examples: [ - { - name: 'Write profile', - params: ['mem.prof'], - response: { - result: null - } - } - ] - }, - { - name: 'debug_writeMutexProfile', - description: 'Write mutex contention profile', - implemented: true, - private: true, - params: [ - { name: 'file', type: 'string', description: 'Output file path', example: 'mutex.prof' } - ], - examples: [ - { - name: 'Write profile', - params: ['mutex.prof'], - response: { - result: null - } - } - ] - } - ] - }, - txpool: { - name: 'TxPool', - icon: NetworkIcon, - methods: [ - { - name: 'txpool_content', - description: 'Get exact details of all pending and queued transactions', - implemented: true, - params: [], - examples: [ - { - name: 'Get pool content', - params: [], - response: { - result: { - pending: {}, - queued: {} - } - } - } - ] - }, - { - name: 'txpool_inspect', - description: 'Get summary of pending and queued transactions', - implemented: true, - params: [], - examples: [ - { - name: 'Inspect pool', - params: [], - response: { - result: { - pending: {}, - queued: {} - } - } - } - ] - }, - { - name: 'txpool_status', - description: 'Get number of pending and queued transactions', - implemented: true, - params: [], - examples: [ - { - name: 'Get pool status', - params: [], - response: { - result: { - pending: '0x0', - queued: '0x0' - } + result: { + pending: '0x0', + queued: '0x0' + } } } ] } ] - }, - admin: { - name: 'Admin', - icon: NetworkIcon, - methods: [ - { - name: 'admin_addPeer', - description: 'Add a new remote peer to connect to', - implemented: true, - params: [ - { name: 'url', type: 'string', description: 'Peer URL in enode format', example: 'enode://pubkey@ip:port' } - ], - examples: [ - { - name: 'Add peer', - params: ['enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303'], - response: { result: true } - } - ] - }, - { - name: 'admin_removePeer', - description: 'Remove a remote peer', - implemented: true, - params: [ - { name: 'url', type: 'string', description: 'Peer URL in enode format', example: 'enode://pubkey@ip:port' } - ], - examples: [ - { - name: 'Remove peer', - params: ['enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303'], - response: { result: true } - } - ] - }, - { - name: 'admin_peers', - description: 'List all connected peers', - implemented: true, - params: [], - examples: [ - { - name: 'Get peers', - params: [], - response: { result: [] } - } - ] - }, - { - name: 'admin_nodeInfo', - description: 'Get node information', - implemented: true, - params: [], - examples: [ - { - name: 'Get node info', - params: [], - response: { - result: { - id: 'node-id', - name: 'Cosmos EVM', - enode: 'enode://...', - ip: '127.0.0.1', - ports: { discovery: 30303, listener: 30303 }, - listenAddr: '[::]:30303', - protocols: {} - } - } - } - ] - }, - { - name: 'admin_datadir', - description: 'Get the data directory path', - implemented: true, - params: [], - examples: [ - { - name: 'Get data directory', - params: [], - response: { result: '/home/user/.cosmos' } - } - ] - }, - { - name: 'admin_startHTTP', - description: 'Start the HTTP RPC server', - implemented: true, - params: [ - { name: 'host', type: 'string', description: 'HTTP server host', example: 'localhost' }, - { name: 'port', type: 'number', description: 'HTTP server port', example: 8545 }, - { name: 'cors', type: 'string', description: 'Allowed CORS domains', example: '*' }, - { name: 'apis', type: 'string', description: 'Enabled APIs', example: 'eth,net,web3' } - ], - examples: [ - { - name: 'Start HTTP server', - params: ['localhost', 8545, '*', 'eth,net,web3'], - response: { result: true } - } - ] - }, - { - name: 'admin_stopHTTP', - description: 'Stop the HTTP RPC server', - implemented: true, - params: [], - examples: [ - { - name: 'Stop HTTP server', - params: [], - response: { result: true } - } - ] - }, - { - name: 'admin_startWS', - description: 'Start the WebSocket RPC server', - implemented: true, - params: [ - { name: 'host', type: 'string', description: 'WebSocket server host', example: 'localhost' }, - { name: 'port', type: 'number', description: 'WebSocket server port', example: 8546 }, - { name: 'cors', type: 'string', description: 'Allowed CORS domains', example: '*' }, - { name: 'apis', type: 'string', description: 'Enabled APIs', example: 'eth,net,web3' } - ], - examples: [ - { - name: 'Start WebSocket server', - params: ['localhost', 8546, '*', 'eth,net,web3'], - response: { result: true } - } - ] - }, - { - name: 'admin_stopWS', - description: 'Stop the WebSocket RPC server', - implemented: true, - params: [], - examples: [ - { - name: 'Stop WebSocket server', - params: [], - response: { result: true } + } + }; + + const generateCodeExamples = (method, params = []) => { + const endpoint = rpcEndpoint || 'http://localhost:8545'; + const jsonRpcBody = { + jsonrpc: '2.0', + id: 1, + method: method, + params: params + }; + + const examples = { + curl: `curl -X POST -H "Content-Type: application/json" \\ + --data '${JSON.stringify(jsonRpcBody, null, 2)}' \\ + ${endpoint}`, + + javascript: `// Using native fetch API +const response = await fetch('${endpoint}', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(${JSON.stringify(jsonRpcBody, null, 2).split('\n').map((line, i) => i === 0 ? line : ' ' + line).join('\n')}) +}); + +const data = await response.json(); +console.log(data.result); + +// Using ethers.js v6 +import { JsonRpcProvider } from 'ethers'; +const provider = new JsonRpcProvider('${endpoint}'); +const result = await provider.send('${method}', ${JSON.stringify(params)}); +console.log(result); + +// Using web3.js v4 +import { Web3 } from 'web3'; +const web3 = new Web3('${endpoint}'); +const result = await web3.provider.request({ + method: '${method}', + params: ${JSON.stringify(params)} +}); +console.log(result);`, + + python: `import json +import requests + +# Using requests library +url = "${endpoint}" +headers = {"Content-Type": "application/json"} +payload = ${JSON.stringify(jsonRpcBody, null, 2).split('\n').map((line, i) => i === 0 ? line : line).join('\n')} + +response = requests.post(url, headers=headers, data=json.dumps(payload)) +result = response.json() +print(result) + +# Using web3.py v6 +from web3 import Web3 + +w3 = Web3(Web3.HTTPProvider("${endpoint}")) +result = w3.provider.make_request("${method}", ${JSON.stringify(params)}) +print(result)`, + + go: `package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" +) + +type JSONRPCRequest struct { + JSONRPC string \`json:"jsonrpc"\` + ID int \`json:"id"\` + Method string \`json:"method"\` + Params []interface{} \`json:"params"\` +} + +type JSONRPCResponse struct { + JSONRPC string \`json:"jsonrpc"\` + ID int \`json:"id"\` + Result json.RawMessage \`json:"result"\` + Error *JSONRPCError \`json:"error,omitempty"\` +} + +type JSONRPCError struct { + Code int \`json:"code"\` + Message string \`json:"message"\` +} + +func main() { + request := JSONRPCRequest{ + JSONRPC: "2.0", + ID: 1, + Method: "${method}", + Params: []interface{}{${params.map(p => { + if (typeof p === 'string') return `"${p}"`; + if (typeof p === 'object') return JSON.stringify(p); + return p; + }).join(', ')}}, + } + + jsonData, err := json.Marshal(request) + if err != nil { + panic(err) + } + + resp, err := http.Post("${endpoint}", "application/json", bytes.NewBuffer(jsonData)) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + panic(err) + } + + var response JSONRPCResponse + err = json.Unmarshal(body, &response) + if err != nil { + panic(err) + } + + if response.Error != nil { + fmt.Printf("Error: %s\\n", response.Error.Message) + } else { + fmt.Printf("Result: %s\\n", response.Result) + } +}`, + + rust: `use serde::{Deserialize, Serialize}; +use serde_json::json; + +#[derive(Debug, Serialize, Deserialize)] +struct JsonRpcRequest { + jsonrpc: String, + id: u64, + method: String, + params: Vec, +} + +#[derive(Debug, Deserialize)] +struct JsonRpcResponse { + jsonrpc: String, + id: u64, + #[serde(skip_serializing_if = "Option::is_none")] + result: Option, + #[serde(skip_serializing_if = "Option::is_none")] + error: Option, +} + +#[derive(Debug, Deserialize)] +struct JsonRpcError { + code: i32, + message: String, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = reqwest::Client::new(); + + let request = JsonRpcRequest { + jsonrpc: "2.0".to_string(), + id: 1, + method: "${method}".to_string(), + params: vec![${params.map(p => { + if (typeof p === 'string') return `json!("${p}")`; + return `json!(${JSON.stringify(p)})`; + }).join(', ')}], + }; + + let response = client + .post("${endpoint}") + .json(&request) + .send() + .await?; + + let json_response: JsonRpcResponse = response.json().await?; + + match json_response.error { + Some(error) => eprintln!("Error {}: {}", error.code, error.message), + None => { + if let Some(result) = json_response.result { + println!("Result: {}", serde_json::to_string_pretty(&result)?); } - ] } - ] - }, - miner: { - name: 'Miner', - icon: SmartContractIcon, - methods: [ + } + + Ok(()) +}`, + + csharp: `using System; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +public class JsonRpcRequest +{ + public string jsonrpc { get; set; } = "2.0"; + public int id { get; set; } = 1; + public string method { get; set; } + public object[] @params { get; set; } +} + +public class JsonRpcResponse +{ + public string jsonrpc { get; set; } + public int id { get; set; } + public T result { get; set; } + public JsonRpcError error { get; set; } +} + +public class JsonRpcError +{ + public int code { get; set; } + public string message { get; set; } +} + +class Program +{ + static async Task Main(string[] args) + { + using var httpClient = new HttpClient(); + + var request = new JsonRpcRequest { - name: 'miner_start', - description: 'Start mining (stub implementation for PoS)', - implemented: true, - params: [ - { name: 'threads', type: 'number', description: 'Number of threads', example: 1 } - ], - examples: [ - { - name: 'Start mining', - params: [1], - response: { result: true } - } - ] - }, + method = "${method}", + @params = new object[] { ${params.map(p => { + if (typeof p === 'string') return `"${p}"`; + if (typeof p === 'object') return JSON.stringify(p); + return p; + }).join(', ')} } + }; + + var json = JsonSerializer.Serialize(request, new JsonSerializerOptions { - name: 'miner_stop', - description: 'Stop mining (stub implementation for PoS)', - implemented: true, - params: [], - examples: [ - { - name: 'Stop mining', - params: [], - response: { result: true } - } - ] - }, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + try { - name: 'miner_setEtherbase', - description: 'Set the etherbase (stub implementation for PoS)', - implemented: true, - params: [ - { name: 'address', type: 'string', description: 'The etherbase address', example: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8' } - ], - examples: [ + var response = await httpClient.PostAsync("${endpoint}", content); + var responseBody = await response.Content.ReadAsStringAsync(); + + var jsonResponse = JsonSerializer.Deserialize>( + responseBody, + new JsonSerializerOptions { PropertyNameCaseInsensitive = true } + ); + + if (jsonResponse.error != null) { - name: 'Set etherbase', - params: ['0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8'], - response: { result: true } + Console.Error.WriteLine($"Error {jsonResponse.error.code}: {jsonResponse.error.message}"); } - ] - }, - { - name: 'miner_setGasPrice', - description: 'Set the gas price (stub implementation for PoS)', - implemented: true, - params: [ - { name: 'gasPrice', type: 'string', description: 'Gas price in wei', example: '0x3b9aca00' } - ], - examples: [ + else { - name: 'Set gas price', - params: ['0x3b9aca00'], - response: { result: true } + Console.WriteLine($"Result: {JsonSerializer.Serialize(jsonResponse.result)}"); } - ] - }, + } + catch (Exception ex) { - name: 'miner_setGasLimit', - description: 'Set the gas limit (stub implementation for PoS)', - implemented: true, - params: [ - { name: 'gasLimit', type: 'string', description: 'Gas limit', example: '0x5f5e100' } - ], - examples: [ - { - name: 'Set gas limit', - params: ['0x5f5e100'], - response: { result: true } - } - ] + Console.Error.WriteLine($"Request failed: {ex.Message}"); } - ] } - }; +}` + }; - const toggleMethod = (method) => { - setExpandedMethods(prev => ({ - ...prev, - [method]: !prev[method] - })); + return examples; }; const executeRpcRequest = async (methodName, params = []) => { @@ -2025,15 +572,12 @@ class Program return; } - const requestId = `${methodName}-${Date.now()}`; setIsLoading(prev => ({ ...prev, [methodName]: true })); try { const response = await fetch(rpcEndpoint, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, @@ -2043,38 +587,24 @@ class Program }); const data = await response.json(); - setRequestResults(prev => ({ - ...prev, - [methodName]: data - })); + setRequestResults(prev => ({ ...prev, [methodName]: data })); } catch (error) { setRequestResults(prev => ({ ...prev, - [methodName]: { - error: { - message: error.message, - code: -1 - } - } + [methodName]: { error: { message: error.message, code: -1 } } })); } finally { setIsLoading(prev => ({ ...prev, [methodName]: false })); } }; - const getSelectedLanguage = (methodName) => { - return selectedLanguage[methodName] || 'curl'; - }; - - const setMethodLanguage = (methodName, language) => { - setSelectedLanguage(prev => ({ - ...prev, - [methodName]: language - })); + const handleCopy = (text, id) => { + navigator.clipboard.writeText(text); + setCopied(prev => ({ ...prev, [id]: true })); + setTimeout(() => setCopied(prev => ({ ...prev, [id]: false })), 2000); }; - // Get all methods from all namespaces for global search - const allMethods = React.useMemo(() => { + const allMethods = useMemo(() => { const methods = []; Object.entries(namespaces).forEach(([key, namespace]) => { namespace.methods.forEach(method => { @@ -2084,25 +614,16 @@ class Program return methods; }, []); - const filteredMethods = React.useMemo(() => { - // If there's a search term, search all methods + const filteredMethods = useMemo(() => { if (searchTerm) { - return allMethods.filter(method => { - const matchesSearch = method.name.toLowerCase().includes(searchTerm.toLowerCase()) || - method.description.toLowerCase().includes(searchTerm.toLowerCase()); - return matchesSearch; - }); - } - - // Show all methods if 'all' is selected - if (selectedNamespace === 'all') { - return allMethods; + return allMethods.filter(method => + method.name.toLowerCase().includes(searchTerm.toLowerCase()) || + method.description.toLowerCase().includes(searchTerm.toLowerCase()) + ); } - - // Otherwise, show methods from selected namespace + if (selectedNamespace === 'all') return allMethods; const namespace = namespaces[selectedNamespace]; if (!namespace) return []; - return namespace.methods.map(method => ({ ...method, namespace: selectedNamespace, @@ -2110,367 +631,331 @@ class Program })); }, [selectedNamespace, searchTerm, allMethods]); - function CopyButton({ text }) { - const [copied, setCopied] = React.useState(false); - - const handleCopy = () => { - navigator.clipboard.writeText(text); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - return ( - - ); - } + const validateEndpoint = async () => { + if (rpcEndpoint && rpcEndpoint.match(/^https?:\/\//)) { + try { + const response = await fetch(rpcEndpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'web3_clientVersion', + params: [] + }) + }); + setIsValidEndpoint(response.ok); + setIsInvalidEndpoint(!response.ok); + } catch { + setIsValidEndpoint(false); + setIsInvalidEndpoint(true); + } + } + }; return ( -
- {/* Header */} -
-
-
-

- Ethereum JSON-RPC Methods -

-

- Complete reference for Cosmos EVM implementation -

- {isValidEndpoint && ( -

- Interactive mode active - Click "Execute" on any method example to test against {rpcEndpoint} -

- )} -
- - {/* Search and Filters */} -
-
+
+ {/* Fixed Header */} +
+
+ {/* Title and RPC Endpoint Section - Same Level */} +
+
+ {/* Title Section - Left Half */}
- setSearchTerm(e.target.value)} - /> +

+ Ethereum JSON-RPC Methods +

+

+ Complete reference for Cosmos EVM implementation +

-
- {/* Interactive RPC Section - Right-aligned, compact */} -
-
- - Enter an RPC endpoint to enable interactive testing - -
-
- - { - setRpcEndpoint(e.target.value); - setIsValidEndpoint(false); - setIsInvalidEndpoint(false); - }} - onBlur={async () => { - if (rpcEndpoint && rpcEndpoint.match(/^https?:\/\//)) { - try { - const response = await fetch(rpcEndpoint, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - jsonrpc: '2.0', - id: 1, - method: 'web3_clientVersion', - params: [] - }) - }); - if (response.ok) { - setIsValidEndpoint(true); - setIsInvalidEndpoint(false); - } else { - setIsValidEndpoint(false); - setIsInvalidEndpoint(true); - } - } catch (error) { - setIsValidEndpoint(false); - setIsInvalidEndpoint(true); - } - } - }} - /> + {/* RPC Endpoint Section - Right Half */} +
+
+
+ +
+ { + setRpcEndpoint(e.target.value); + setIsValidEndpoint(false); + setIsInvalidEndpoint(false); + }} + onBlur={validateEndpoint} + /> +
{isValidEndpoint && ( - Y + ✓ Connected )} {isInvalidEndpoint && ( - N + ✗ Failed )}
+ {isValidEndpoint && ( +

+ Interactive mode active - Click "Execute" to test +

+ )} +

+ Enter endpoint for interactive testing +

-

- Note: Some endpoints may require CORS configuration or have other restrictions that prevent browser-based requests -

-
-
- {/* Namespace Tabs */} -
-
-
- - {Object.entries(namespaces).map(([key, namespace]) => ( - - ))} + {/* Search Bar */} +
+
+ + + + setSearchTerm(e.target.value)} + /> +
-
-
- {/* Methods List */} -
-
- {filteredMethods.length === 0 ? ( -
-

- No methods found matching your criteria -

-
- ) : ( - filteredMethods.map((method) => ( -
+ {/* Namespace Tabs - Constrained Width */} +
+
+
+ {namespace.name} + + ))} +
+
+
+
+
- {expandedMethods[method.name] && ( -
- {method.issue && ( -
-

- Warning: {method.issue} -

-
- )} + {/* Methods List - Constrained Width */} +
+
+ {filteredMethods.map((method) => ( +
+ - {method.params && method.params.length > 0 && ( -
-

- Parameters -

-
- {method.params.map((param, i) => ( -
-
-
- - {param.name} - - - {param.type} - -
- {param.example && ( - - Example: {param.example} - - )} + {expandedMethods[method.name] && ( +
+ {/* Parameters */} + {method.params && method.params.length > 0 && ( +
+

Parameters

+
+ {method.params.map((param, i) => ( +
+
+
+ {param.name} + {param.type}
-

- {param.description} -

- {param.fields && ( -
- {param.fields.map((field, j) => ( -
- {field.name} - ({field.type}) - - {field.description} -
- ))} -
- )} -
- ))} -
-
- )} - - {method.examples && method.examples.length > 0 && ( -
- {method.examples.map((example, exampleIndex) => ( -
-
-
- Example: {example.name} -
- {rpcEndpoint && rpcEndpoint.match(/^https?:\/\//) && ( - + {param.example && ( + + {param.example} + )}
- - {/* Language Tabs */} -
-
- {languages.map(lang => ( - +

{param.description}

+ {param.fields && ( +
+ {param.fields.map((field, j) => ( +
+ {field.name} + ({field.type}) + - {field.description} +
))}
-
+ )} +
+ ))} +
+
+ )} + + {/* Examples */} + {method.examples && method.examples.map((example, exampleIndex) => { + const exampleKey = `${method.name}-${exampleIndex}`; + const currentLang = selectedLanguage[exampleKey] || 'curl'; + + return ( +
+
+
+ Example: {example.name} +
+ {rpcEndpoint && ( + + )} +
- {/* Code Example */} -
-
- -
-
-                                
-                                  {generateCodeExamples(method.name, example.params)[getSelectedLanguage(`${method.name}-${exampleIndex}`)]}
-                                
-                              
-
+ {/* Language Tabs */} +
+ {languages.map(lang => ( + + ))} +
- {/* Response */} - {rpcEndpoint && rpcEndpoint.match(/^https?:\/\//) && requestResults[method.name] ? ( - // Show live response when interactive mode is on and we have results -
-
-
- Live RPC Response from {rpcEndpoint} -
-
-
-                                  
-{JSON.stringify(requestResults[method.name], null, 2)}
-                                  
-                                
-
+ {/* Code Example */} +
+ +
+                            {generateCodeExamples(method.name, example.params)[currentLang]}
+                          
+
+ + {/* Response */} + {requestResults[method.name] ? ( +
+
+ Live Response +
+
+                              
+                                {JSON.stringify(requestResults[method.name], null, 2)}
+                              
+                            
- ))} + ) : ( +
+
+ Expected Response +
+
+                              
+                                {JSON.stringify({ jsonrpc: '2.0', id: 1, ...example.response }, null, 2)}
+                              
+                            
+
+ )}
- )} -
- )} -
- )) - )} + ); + })} +
+ )} +
+ ))}
-
); } \ No newline at end of file