SuperAPI AI Directory

metamask-openclaw-wallet-integration

General & other

View repository

Updated 10-16 01:28

About this skill

MetaMask OpenClaw Wallet Integration

Skill by 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)

npm install metamask-openclaw-skill
# or
yarn add metamask-openclaw-skill
# or
pnpm add metamask-openclaw-skill

From source

git clone https://github.com/veryyoldman/metamask-openclaw-skill.git
cd metamask-openclaw-skill
npm install
npm run build

Windows one-command install

git clone https://github.com/veryyoldman/metamask-openclaw-skill.git && cd metamask-openclaw-skill && npm install && npm run build

Quick Start

Basic library usage

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

# 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)

<!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

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

// 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

// 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

// Sign a message (user approves in MetaMask)
const signature = await claw.signMessage("Hello, Web3!");
// Returns: string (hex signature)

Network switching

// 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

// 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

// 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

# .env file
INFURA_API_KEY=your_infura_key_here
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

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

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

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

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.

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.

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:

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.

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
  6. Remind users that they approve all signatures and transactions in MetaMask

Example safe agent response:

I can help you prepare a transaction using OpenClaw. Here's the code:

await claw.sendNative({ to: "0xRecipient", amount: "0.001" });

This will show a MetaMask popup where YOU approve the amount and gas.
OpenClaw never handles your keys — you stay in full control.

Additional Resources


Keywords: metamask, wallet integration, web3, ethereum, evm, polygon, base, arbitrum, sign message, send transaction, dapp, typescript, sdk