Files
smom-dbis-138/docs/operations/integrations/METAMASK_DEVELOPER_GUIDE.md
defiQUG 1fb7266469 Add Oracle Aggregator and CCIP Integration
- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control.
- Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities.
- Created .gitmodules to include OpenZeppelin contracts as a submodule.
- Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment.
- Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks.
- Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring.
- Created scripts for resource import and usage validation across non-US regions.
- Added tests for CCIP error handling and integration to ensure robust functionality.
- Included various new files and directories for the orchestration portal and deployment scripts.
2025-12-12 14:57:48 -08:00

6.3 KiB

MetaMask Developer Guide for ChainID 138

Developer guide for integrating ChainID 138 with MetaMask in your dapp.

Installation

Using npm

npm install @defi-oracle/metamask-sdk

Using the source

cd metamask-sdk
npm install
npm run build

Basic Usage

Import the SDK

import {
  addOrSwitchNetwork,
  addToken,
  isOnChain138,
  getCurrentChainId,
} from '@defi-oracle/metamask-sdk';

Connect to ChainID 138

async function connect() {
  try {
    await addOrSwitchNetwork();
    const isOn138 = await isOnChain138();
    if (isOn138) {
      console.log('Connected to ChainID 138');
    }
  } catch (error) {
    console.error('Connection error:', error);
  }
}

Add Token

async function addWETHToken() {
  try {
    await addToken(
      '0xYourWETHAddress',
      'WETH',
      18,
      'https://explorer.d-bis.org/images/tokens/weth.png'
    );
  } catch (error) {
    console.error('Token add error:', error);
  }
}

React Integration

Custom Hook

import { useState, useEffect } from 'react';
import { isOnChain138, addOrSwitchNetwork } from '@defi-oracle/metamask-sdk';

function useChain138() {
  const [isConnected, setIsConnected] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    checkConnection();
    
    // Listen for chain changes
    if (window.ethereum) {
      window.ethereum.on('chainChanged', checkConnection);
      return () => {
        window.ethereum.removeListener('chainChanged', checkConnection);
      };
    }
  }, []);

  async function checkConnection() {
    try {
      const on138 = await isOnChain138();
      setIsConnected(on138);
    } catch (error) {
      setIsConnected(false);
    } finally {
      setIsLoading(false);
    }
  }

  async function connect() {
    try {
      await addOrSwitchNetwork();
      await checkConnection();
    } catch (error) {
      console.error('Connection error:', error);
    }
  }

  return { isConnected, isLoading, connect };
}

React Component

import React from 'react';
import { useChain138 } from './useChain138';

function Chain138Button() {
  const { isConnected, isLoading, connect } = useChain138();

  if (isLoading) {
    return <button disabled>Loading...</button>;
  }

  if (isConnected) {
    return <button disabled>Connected to ChainID 138</button>;
  }

  return (
    <button onClick={connect}>
      Connect to ChainID 138
    </button>
  );
}

Vue Integration

Composable

import { ref, onMounted, onUnmounted } from 'vue';
import { isOnChain138, addOrSwitchNetwork } from '@defi-oracle/metamask-sdk';

export function useChain138() {
  const isConnected = ref(false);
  const isLoading = ref(true);

  async function checkConnection() {
    try {
      const on138 = await isOnChain138();
      isConnected.value = on138;
    } catch (error) {
      isConnected.value = false;
    } finally {
      isLoading.value = false;
    }
  }

  async function connect() {
    try {
      await addOrSwitchNetwork();
      await checkConnection();
    } catch (error) {
      console.error('Connection error:', error);
    }
  }

  function handleChainChanged() {
    checkConnection();
  }

  onMounted(() => {
    checkConnection();
    if (window.ethereum) {
      window.ethereum.on('chainChanged', handleChainChanged);
    }
  });

  onUnmounted(() => {
    if (window.ethereum) {
      window.ethereum.removeListener('chainChanged', handleChainChanged);
    }
  });

  return { isConnected, isLoading, connect };
}

Error Handling

Common Errors

import { addNetwork } from '@defi-oracle/metamask-sdk';

try {
  await addNetwork();
} catch (error) {
  if (error.message.includes('MetaMask is not installed')) {
    // Handle MetaMask not installed
    alert('Please install MetaMask');
  } else if (error.message.includes('User rejected')) {
    // Handle user rejection
    console.log('User rejected the request');
  } else if (error.code === 4902) {
    // Network already added, try switching
    await switchNetwork();
  } else {
    // Handle other errors
    console.error('Unexpected error:', error);
  }
}

RPC Best Practices

Rate Limiting

  • Default: 1200 requests/minute per IP
  • Use request batching when possible
  • Implement client-side rate limiting
  • Use WebSocket connections for real-time data

Request Batching

const batch = [
  { method: 'eth_blockNumber', params: [] },
  { method: 'eth_gasPrice', params: [] },
  { method: 'eth_getBalance', params: [address, 'latest'] },
];

const results = await Promise.all(
  batch.map(req => window.ethereum.request(req))
);

WebSocket Connections

const ws = new WebSocket('wss://rpc.d-bis.org');

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
};

ws.send(JSON.stringify({
  jsonrpc: '2.0',
  method: 'eth_subscribe',
  params: ['newHeads'],
  id: 1
}));

Testing

Mock MetaMask for Testing

// mock-metamask.ts
export function createMockMetaMask() {
  return {
    isMetaMask: true,
    request: async (args: { method: string; params?: unknown[] }) => {
      if (args.method === 'eth_chainId') {
        return '0x8a';
      }
      if (args.method === 'wallet_addEthereumChain') {
        return null;
      }
      if (args.method === 'wallet_switchEthereumChain') {
        return null;
      }
      throw new Error('Method not implemented');
    },
    on: () => {},
    removeListener: () => {},
  };
}

// In your tests
global.window.ethereum = createMockMetaMask();

Production Checklist

  • Verify RPC URLs are correct and accessible
  • Test network addition on all target browsers
  • Test token addition for all tokens
  • Verify error handling for all error cases
  • Test network switching functionality
  • Verify chain change event listeners
  • Test on mobile MetaMask
  • Verify token logos are accessible
  • Test with different MetaMask versions
  • Verify CORS headers on RPC endpoints

References