Skip to main content

metamask-openclaw-wallet-integration

Connect MetaMask wallets, read balances, sign messages, and send transactions on EVM chains with a safety-first TypeScript SDK

Jump to install

Source facts

Repository
reason-machines/hermes-skills
Last source activity
May 31, 2026 at 06:48
Detected SKILL.md language
English
Stars
5
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
metamask-openclaw-wallet-integration
description
Connect MetaMask wallets, read balances, sign messages, and send transactions on EVM chains with a safety-first TypeScript SDK
triggers
["integrate MetaMask wallet into my dapp","connect to MetaMask and read user balance","sign a message with MetaMask","send a transaction using OpenClaw","switch MetaMask to a different network","set up wallet connection in my web3 app","handle MetaMask account changes","prepare an Ethereum transfer with user approval"]
# MetaMask OpenClaw Wallet Integration > Skill by [ara.so](https://ara.so) — Hermes Skills collection. ## Overview **OpenClaw** is a safety-first TypeScript toolkit and SDK for integrating MetaMask wallets into dapps. Built on the official MetaMask SDK, it provides a clean API for: - Connecting wallets (desktop browser, mobile browser, QR code to MetaMask Mobile) - Reading account balances across EVM chains (Ethereum, Polygon, Base, Arbitrum, Optimism, Linea) - Signing messages (user approves in MetaMask UI) - Preparing and sending transactions (user approves amount and gas) - Switching networks with automatic chain addition - Reacting to account/network changes **Safety guarantee**: OpenClaw never requests or handles seed phrases or private keys. All sensitive operations require user approval in the MetaMask UI. ## Installation ### As a library (npm/yarn/pnpm) ```bash npm install metamask-openclaw-skill # or yarn add metamask-openclaw-skill # or pnpm add metamask-openclaw-skill ``` ### From source ```bash git clone https://github.com/veryyoldman/metamask-openclaw-skill.git cd metamask-openclaw-skill npm install npm run build ``` ### Windows one-command install ```cmd git clone https://github.com/veryyoldman/metamask-openclaw-skill.git && cd metamask-openclaw-skill && npm install && npm run build ``` ## Quick Start ### Basic library usage ```typescript import { OpenClaw } from "metamask-openclaw-skill"; // Initialize const claw = new OpenClaw({ dappMetadata: { name: "My Dapp", url: "https://mydapp.example" }, // Optional: provide Infura key for reliable RPC infuraApiKey: process.env.INFURA_API_KEY }); // Connect wallet (triggers MetaMask popup or QR code) await claw.connect(); // Get current state const { account, chainId } = claw.getState(); console.log(`Connected: ${account} on chain ${chainId}`); ``` ### CLI usage ```bash # Connect and display address + network node dist/cli/openclaw.js connect # Check balance node dist/cli/openclaw.js balance # Prepare a transfer (user approves in MetaMask) node dist/cli/openclaw.js send 0xRecipientAddress 0.001 ``` ### Browser usage (zero build) ```html <!DOCTYPE html> <html> <head> <title>OpenClaw Demo</title> </head> <body> <button id="connect">Connect MetaMask</button> <div id="info"></div> <script type="module"> import { OpenClaw } from './dist/index.js'; const claw = new OpenClaw({ dappMetadata: { name: "Demo", url: window.location.href } }); document.getElementById('connect').onclick = async () => { await claw.connect(); const { account, chainId } = claw.getState(); const balance = await claw.getBalance(); document.getElementById('info').innerText = `${account}\nChain: ${chainId}\nBalance: ${balance.formatted} ${balance.symbol}`; }; </script> </body> </html> ``` ## Core API ### Constructor options ```typescript interface OpenClawOptions { dappMetadata: { name: string; // Your dapp name url: string; // Your dapp URL iconUrl?: string; // Optional icon }; infuraApiKey?: string; // Optional Infura key for RPC defaultChainId?: string; // e.g. "0x1" for Ethereum mainnet } const claw = new OpenClaw(options); ``` ### Connection methods ```typescript // Connect to MetaMask (shows popup or QR) await claw.connect(); // Disconnect claw.disconnect(); // Check connection status const isConnected = claw.isConnected(); // Get current state const state = claw.getState(); // Returns: { account: string | null, chainId: string | null } ``` ### Reading data ```typescript // Get native token balance const balance = await claw.getBalance(); // Returns: { formatted: string, symbol: string, raw: string } // Example: { formatted: "1.234", symbol: "ETH", raw: "1234000000000000000" } // Get balance for specific address const balance = await claw.getBalance("0xOtherAddress"); ``` ### Signing messages ```typescript // Sign a message (user approves in MetaMask) const signature = await claw.signMessage("Hello, Web3!"); // Returns: string (hex signature) ``` ### Network switching ```typescript // Switch to a different network await claw.switchChain("0x89"); // Polygon await claw.switchChain("0x2105"); // Base await claw.switchChain("0xa4b1"); // Arbitrum // If the network isn't in MetaMask, it will be added (user approves) ``` ### Sending transactions ```typescript // Send native token (user approves amount + gas in MetaMask) const txHash = await claw.sendNative({ to: "0xRecipientAddress", amount: "0.001" // in native token (e.g. ETH, POL) }); console.log(`Transaction sent: ${txHash}`); ``` ### Event handling ```typescript // Listen for account or network changes claw.onChange((state) => { console.log("Wallet changed:", state); // state: { account: string | null, chainId: string | null } }); // Remove listener const unsubscribe = claw.onChange(handler); unsubscribe(); ``` ## Configuration ### Environment variables ```bash # .env file INFURA_API_KEY=your_infura_key_here ``` ```typescript import { OpenClaw } from "metamask-openclaw-skill"; const claw = new OpenClaw({ dappMetadata: { name: process.env.DAPP_NAME || "My Dapp", url: process.env.DAPP_URL || "http://localhost:3000" }, infuraApiKey: process.env.INFURA_API_KEY }); ``` ### Supported networks | Network | Chain ID | Symbol | |------------------|-------------|--------| | Ethereum Mainnet | `0x1` | ETH | | Polygon | `0x89` | POL | | Arbitrum One | `0xa4b1` | ETH | | OP Mainnet | `0xa` | ETH | | Base | `0x2105` | ETH | | Linea | `0xe708` | ETH | | Sepolia (testnet)| `0xaa36a7` | ETH | Add custom networks in `src/chains.ts`. ## Common Patterns ### React integration ```typescript import { OpenClaw } from "metamask-openclaw-skill"; import { useEffect, useState } from "react"; function WalletButton() { const [claw] = useState(() => new OpenClaw({ dappMetadata: { name: "My App", url: window.location.href } })); const [account, setAccount] = useState<string | null>(null); const [balance, setBalance] = useState<string>(""); useEffect(() => { claw.onChange((state) => { setAccount(state.account); if (state.account) { claw.getBalance().then(b => setBalance(`${b.formatted} ${b.symbol}`)); } }); }, [claw]); const handleConnect = async () => { await claw.connect(); const state = claw.getState(); setAccount(state.account); if (state.account) { const b = await claw.getBalance(); setBalance(`${b.formatted} ${b.symbol}`); } }; return ( <div> {!account ? ( <button onClick={handleConnect}>Connect MetaMask</button> ) : ( <div> <p>{account}</p> <p>{balance}</p> </div> )} </div> ); } ``` ### Transfer with confirmation ```typescript async function sendTokens(claw: OpenClaw, recipient: string, amount: string) { const state = claw.getState(); if (!state.account) { throw new Error("No wallet connected"); } const balance = await claw.getBalance(); if (parseFloat(balance.formatted) < parseFloat(amount)) { throw new Error("Insufficient balance"); } const txHash = await claw.sendNative({ to: recipient, amount }); console.log(`Transfer initiated: ${txHash}`); return txHash; } ``` ### Multi-chain balance checker ```typescript async function checkBalanceOnChains(claw: OpenClaw, chains: string[]) { const balances: Record<string, string> = {}; for (const chainId of chains) { await claw.switchChain(chainId); const balance = await claw.getBalance(); balances[chainId] = `${balance.formatted} ${balance.symbol}`; } return balances; } // Usage const balances = await checkBalanceOnChains(claw, ["0x1", "0x89", "0x2105"]); console.log(balances); // { "0x1": "1.5 ETH", "0x89": "100 POL", "0x2105": "0.5 ETH" } ``` ### Safe message signing ```typescript async function signAuthMessage(claw: OpenClaw, nonce: string) { const state = claw.getState(); if (!state.account) { throw new Error("No wallet connected"); } const message = `Sign to authenticate\nAddress: ${state.account}\nNonce: ${nonce}`; const signature = await claw.signMessage(message); return { message, signature, address: state.account }; } ``` ## Troubleshooting ### "MetaMask not detected" **Solution**: Ensure MetaMask extension is installed and enabled. On mobile, use the in-app browser or scan the QR code. ```typescript if (!window.ethereum) { alert("Please install MetaMask: https://metamask.io/download/"); } ``` ### Connection hangs in Node.js CLI **Cause**: Waiting for MetaMask Mobile QR scan. **Solution**: The CLI prints a QR code. Open MetaMask Mobile and scan it. ### "User rejected the request" **Cause**: User clicked "Cancel" or "Reject" in MetaMask. **Solution**: Catch the error and prompt the user to try again. ```typescript try { await claw.connect(); } catch (error) { if (error.code === 4001) { console.log("User rejected connection"); } } ``` ### Balance shows "0.0" on testnet **Cause**: No funds in the test account. **Solution**: Get testnet ETH from a faucet: - Sepolia: https://sepoliafaucet.com/ ### Chain switch fails **Cause**: Network not configured in MetaMask. **Solution**: OpenClaw automatically adds the network (user approves). Ensure chain details are correct in `src/chains.ts`. ### Infura rate limiting **Cause**: No Infura API key or exceeded free tier. **Solution**: Sign up at https://infura.io/ and set `INFURA_API_KEY` environment variable. ```typescript const claw = new OpenClaw({ dappMetadata: { name: "App", url: "https://app.example" }, infuraApiKey: process.env.INFURA_API_KEY // Add this }); ``` ## Safety & Security **OpenClaw never:** - Requests your seed phrase or private keys - Signs or sends anything without user approval in MetaMask UI - Stores sensitive data **Always:** - User approves every signature and transaction in MetaMask - Keys remain in MetaMask's secure vault - Only public addresses are exposed to the dapp **Never** enter your Secret Recovery Phrase anywhere except the official MetaMask app when restoring a wallet. OpenClaw will never ask for it. ## AI Agent Usage When helping users with OpenClaw: 1. **Never** generate or request seed phrases or private keys 2. **Always** defer fund movements to user approval via `sendNative()` 3. **Verify** the user has MetaMask installed before suggesting connection 4. **Use** `getBalance()` for read-only balance checks 5. **Encourage** testnet usage (Sepolia) for learning
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub