
Skill
sign-multichain-evm-transaction
sign and send EVM transactions
Description
Sign and send EVM transactions using the multichain client's invokeMethod. Covers eth_sendTransaction, personal_sign, eth_signTypedData_v4, scope selection, and RPC routing for read vs sign operations.
SKILL.md
Sign EVM Transactions via Multichain Client
When to use
Use this skill when:
- Sending EVM transactions through
invokeMethodon a multichain client - Signing messages with
personal_signoreth_signTypedData_v4 - Understanding which methods route to the RPC node vs the wallet
- Selecting the correct CAIP-2 EVM scope for a target chain
Workflow
Step 1: Ensure the client is connected with EVM scopes
import { createMultichainClient, getInfuraRpcUrls } from '@metamask/connect-multichain';
const client = await createMultichainClient({
dapp: { name: 'My DApp', url: window.location.href },
api: {
supportedNetworks: {
...getInfuraRpcUrls({ infuraApiKey: 'YOUR_INFURA_KEY', caipChainIds: ['eip155:1', 'eip155:137'] }),
},
},
});
await client.connect(
['eip155:1', 'eip155:137'], // Ethereum mainnet + Polygon
[],
);
Step 2: Understand RPC routing
The multichain client routes EVM methods based on type:
| Route | Methods | Transport |
|---|---|---|
| RPC node | eth_call, eth_getBalance, eth_blockNumber, eth_getTransactionReceipt, eth_estimateGas, eth_getCode, eth_getLogs, eth_getTransactionCount | Infura / custom RPC URL from supportedNetworks |
| Wallet | eth_sendTransaction, personal_sign, eth_signTypedData_v4, wallet_switchEthereumChain, wallet_addEthereumChain | MetaMask (extension or MWP) |
The scope in invokeMethod determines which chain the request targets. Use 'eip155:1' for Ethereum mainnet, 'eip155:137' for Polygon, etc.
Step 3: Send a transaction (eth_sendTransaction)
const txHash = await client.invokeMethod({
scope: 'eip155:1',
request: {
method: 'eth_sendTransaction',
params: [{
from: '0xYourAddress',
to: '0xRecipientAddress',
value: '0x2386F26FC10000', // 0.01 ETH in hex wei
gas: '0x5208', // 21000 gas
// gasPrice or maxFeePerGas/maxPriorityFeePerGas optional
}],
},
});
console.log('Transaction hash:', txHash);
Estimating gas before sending:
const gasEstimate = await client.invokeMethod({
scope: 'eip155:1',
request: {
method: 'eth_estimateGas',
params: [{
from: '0xYourAddress',
to: '0xRecipientAddress',
value: '0x2386F26FC10000',
}],
},
});
Step 4: Sign a message (personal_sign)
The message must be hex-encoded. The signer address is the second parameter.
const message = '0x' + Buffer.from('Hello MetaMask!').toString('hex');
const signature = await client.invokeMethod({
scope: 'eip155:1',
request: {
method: 'personal_sign',
params: [message, '0xYourAddress'],
},
});
console.log('Signature:', signature);
Step 5: Sign typed data (eth_signTypedData_v4)
Pass the signer address as the first parameter and the JSON-stringified typed data as the second.
const typedData = {
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Mail: [
{ name: 'from', type: 'string' },
{ name: 'to', type: 'string' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
domain: {
name: 'My DApp',
version: '1',
chainId: 1,
verifyingContract: '0xContractAddress',
},
message: {
from: 'Alice',
to: 'Bob',
contents: 'Hello!',
},
};
const signature = await client.invokeMethod({
scope: 'eip155:1',
request: {
method: 'eth_signTypedData_v4',
params: ['0xYourAddress', JSON.stringify(typedData)],
},
});
console.log('Typed data signature:', signature);
Step 6: Cross-chain scope selection
Each invokeMethod call targets a specific chain via its scope. You do not need to "switch chains" — just use the appropriate scope.
// Send on Polygon
await client.invokeMethod({
scope: 'eip155:137',
request: {
method: 'eth_sendTransaction',
params: [{ from: '0x...', to: '0x...', value: '0xDE0B6B3A7640000' }],
},
});
// Read balance on Ethereum
const ethBalance = await client.invokeMethod({
scope: 'eip155:1',
request: {
method: 'eth_getBalance',
params: ['0xYourAddress', 'latest'],
},
});
// Read balance on Polygon
const polyBalance = await client.invokeMethod({
scope: 'eip155:137',
request: {
method: 'eth_getBalance',
params: ['0xYourAddress', 'latest'],
},
});
Step 7: Error handling for signing
try {
const sig = await client.invokeMethod({
scope: 'eip155:1',
request: {
method: 'personal_sign',
params: [hexMessage, signerAddress],
},
});
} catch (err) {
// Multichain invokeMethod errors are wrapped in RPCInvokeMethodErr (code 53);
// the wallet's original code is on err.rpcCode
if (err instanceof RPCInvokeMethodErr && err.rpcCode === 4001) {
// User rejected the signing request
return;
}
throw err;
}
(Import the class with import { RPCInvokeMethodErr } from '@metamask/connect-multichain';. Revert reasons / custom error bytes from the wallet are available on err.rpcData.)
Important Notes
- Scope = chain target: The
scopefield ininvokeMethoddetermines which chain the method executes on. Use'eip155:<decimal-chainId>'format (e.g.,'eip155:1','eip155:137','eip155:42161'). - No chain switching needed: Unlike single-chain EVM clients, the multichain client does not require
wallet_switchEthereumChain. Each call specifies its own scope. - Read vs sign routing: Read-only methods go to the RPC node (fast, no user prompt). Signing methods go to the wallet (requires user approval in MetaMask).
- Hex encoding:
personal_signexpects the message as a hex string (0x...).eth_sendTransactionexpectsvalue,gas, and other numeric fields as hex strings. eth_signTypedData_v4: The typed data parameter must be a JSON string, not an object.- Gas estimation: Always estimate gas with
eth_estimateGasbefore sending if you don't have a reliable gas value. This routes to the RPC node and does not prompt the user. - Connected scopes:
invokeMethodwill fail if the target scope was not included in theconnect()call. Ensure you connect with all chains you intend to use.
More skills from the metamask-connect-cursor-plugin repository
View all 18 skillsmigrate-from-sdk
migrate MetaMask SDK to connect-evm and connect-solana
Jul 13EthereumMigrationSDKWeb3migrate-wagmi-metamask-connector
migrate wagmi apps to MetaMask connector
Jul 13EthereumMigrationwagmiWeb3send-evm-transaction
send EVM transactions with MetaMask
Jul 13API DevelopmentEthereumWeb3send-solana-transaction
send Solana transactions via MetaMask
Jul 13PaymentsSolanaWeb3setup-evm-browser-app
scaffold EVM browser applications with MetaMask
Jul 13EthereumFrontendJavaScriptTypeScript +1setup-evm-react-app
scaffold React apps with MetaMask integration
Jul 13API DevelopmentEthereumFrontendReact +1
More from MetaMask
View publishersmart-accounts-kit
build dApps with MetaMask Smart Accounts
smart-accounts-kit
Jul 13API DevelopmentAuthEthereumPermissions +1x402-payments
build x402 payment flows
smart-accounts-kit
Jul 13API DevelopmentPaymentsWeb3x402discovery
discover and use services
ocap-kernel
Jul 13API DevelopmentSearchmetamask
interact with MetaMask wallet capabilities
ocap-kernel
Jul 13API DevelopmentEthereumWeb3wallet
manage wallet balances and transactions
ocap-kernel
Jul 13EthereumPaymentsWeb3metamask-connect
integrate MetaMask into dApps
connect-monorepo
Jul 13API DevelopmentEthereumSolanawagmi +1