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.
This commit is contained in:
93
examples/metamask-react/README.md
Normal file
93
examples/metamask-react/README.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# React MetaMask Integration Example
|
||||
|
||||
React example for integrating ChainID 138 with MetaMask.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cd examples/metamask-react
|
||||
npm install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### useChain138 Hook
|
||||
|
||||
Custom hook for managing ChainID 138 connection:
|
||||
|
||||
```typescript
|
||||
import { useChain138 } from './useChain138';
|
||||
|
||||
function MyComponent() {
|
||||
const { isConnected, isLoading, connect } = useChain138();
|
||||
|
||||
// Use the hook
|
||||
}
|
||||
```
|
||||
|
||||
### Chain138Button
|
||||
|
||||
Button component for connecting to ChainID 138:
|
||||
|
||||
```typescript
|
||||
import { Chain138Button } from './Chain138Button';
|
||||
|
||||
function App() {
|
||||
return <Chain138Button />;
|
||||
}
|
||||
```
|
||||
|
||||
### AddTokenButton
|
||||
|
||||
Button component for adding tokens:
|
||||
|
||||
```typescript
|
||||
import { AddTokenButton } from './AddTokenButton';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<AddTokenButton
|
||||
address="0xYourTokenAddress"
|
||||
symbol="WETH"
|
||||
decimals={18}
|
||||
image="https://explorer.d-bis.org/images/tokens/weth.png"
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
||||
```typescript
|
||||
import React from 'react';
|
||||
import { useChain138 } from './useChain138';
|
||||
import { AddTokenButton } from './AddTokenButton';
|
||||
|
||||
function App() {
|
||||
const { isConnected, connect } = useChain138();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!isConnected && (
|
||||
<button onClick={connect}>
|
||||
Connect to ChainID 138
|
||||
</button>
|
||||
)}
|
||||
{isConnected && (
|
||||
<AddTokenButton
|
||||
address="0xYourWETHAddress"
|
||||
symbol="WETH"
|
||||
decimals={18}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
13
examples/metamask-react/index.html
Normal file
13
examples/metamask-react/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ChainID 138 MetaMask Integration</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
23
examples/metamask-react/package.json
Normal file
23
examples/metamask-react/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "metamask-react-example",
|
||||
"version": "1.0.0",
|
||||
"description": "React example for MetaMask integration with ChainID 138",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
57
examples/metamask-react/src/AddTokenButton.tsx
Normal file
57
examples/metamask-react/src/AddTokenButton.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
interface AddTokenButtonProps {
|
||||
address: string;
|
||||
symbol: string;
|
||||
decimals: number;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
export function AddTokenButton({
|
||||
address,
|
||||
symbol,
|
||||
decimals,
|
||||
image,
|
||||
}: AddTokenButtonProps) {
|
||||
const [isAdding, setIsAdding] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const addToken = async () => {
|
||||
if (typeof window === 'undefined' || !window.ethereum) {
|
||||
setError('MetaMask is not installed');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsAdding(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_watchAsset',
|
||||
params: {
|
||||
type: 'ERC20',
|
||||
options: {
|
||||
address,
|
||||
symbol,
|
||||
decimals,
|
||||
...(image && { image }),
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to add token');
|
||||
} finally {
|
||||
setIsAdding(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={addToken} disabled={isAdding}>
|
||||
{isAdding ? 'Adding...' : `Add ${symbol}`}
|
||||
</button>
|
||||
{error && <div style={{ color: 'red' }}>{error}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
25
examples/metamask-react/src/App.tsx
Normal file
25
examples/metamask-react/src/App.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { Chain138Button } from './Chain138Button';
|
||||
import { AddTokenButton } from './AddTokenButton';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<h1>ChainID 138 MetaMask Integration Example</h1>
|
||||
|
||||
<h2>Network</h2>
|
||||
<Chain138Button />
|
||||
|
||||
<h2>Tokens</h2>
|
||||
<AddTokenButton
|
||||
address="0xYourWETHAddress" // Update after deployment
|
||||
symbol="WETH"
|
||||
decimals={18}
|
||||
image="https://explorer.d-bis.org/images/tokens/weth.png"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
21
examples/metamask-react/src/Chain138Button.tsx
Normal file
21
examples/metamask-react/src/Chain138Button.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { useChain138 } from './useChain138';
|
||||
|
||||
export 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>
|
||||
);
|
||||
}
|
||||
|
||||
10
examples/metamask-react/src/main.tsx
Normal file
10
examples/metamask-react/src/main.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
95
examples/metamask-react/src/useChain138.ts
Normal file
95
examples/metamask-react/src/useChain138.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
const CHAIN_ID = '0x8a';
|
||||
|
||||
interface UseChain138Return {
|
||||
isConnected: boolean;
|
||||
isLoading: boolean;
|
||||
connect: () => Promise<void>;
|
||||
chainId: string | null;
|
||||
}
|
||||
|
||||
export function useChain138(): UseChain138Return {
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [chainId, setChainId] = useState<string | null>(null);
|
||||
|
||||
const checkConnection = async () => {
|
||||
if (typeof window === 'undefined' || !window.ethereum) {
|
||||
setIsConnected(false);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const currentChainId = await window.ethereum.request({
|
||||
method: 'eth_chainId',
|
||||
}) as string;
|
||||
|
||||
setChainId(currentChainId);
|
||||
setIsConnected(currentChainId === CHAIN_ID);
|
||||
} catch (error) {
|
||||
setIsConnected(false);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const connect = async () => {
|
||||
if (typeof window === 'undefined' || !window.ethereum) {
|
||||
throw new Error('MetaMask is not installed');
|
||||
}
|
||||
|
||||
const networkMetadata = {
|
||||
chainId: CHAIN_ID,
|
||||
chainName: 'DeFi Oracle Meta Mainnet',
|
||||
nativeCurrency: {
|
||||
name: 'Ether',
|
||||
symbol: 'ETH',
|
||||
decimals: 18,
|
||||
},
|
||||
rpcUrls: ['https://rpc.d-bis.org'],
|
||||
blockExplorerUrls: ['https://explorer.d-bis.org'],
|
||||
};
|
||||
|
||||
try {
|
||||
// Try to switch first
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [{ chainId: CHAIN_ID }],
|
||||
});
|
||||
} catch (error: any) {
|
||||
// If switch fails, network might not be added
|
||||
if (error.code === 4902) {
|
||||
// Add the network
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [networkMetadata],
|
||||
});
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
await checkConnection();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
checkConnection();
|
||||
|
||||
if (window.ethereum) {
|
||||
const handleChainChanged = () => {
|
||||
checkConnection();
|
||||
};
|
||||
|
||||
window.ethereum.on('chainChanged', handleChainChanged);
|
||||
|
||||
return () => {
|
||||
window.ethereum?.removeListener('chainChanged', handleChainChanged);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { isConnected, isLoading, connect, chainId };
|
||||
}
|
||||
|
||||
22
examples/metamask-react/tsconfig.json
Normal file
22
examples/metamask-react/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
|
||||
11
examples/metamask-react/tsconfig.node.json
Normal file
11
examples/metamask-react/tsconfig.node.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
||||
10
examples/metamask-react/vite.config.ts
Normal file
10
examples/metamask-react/vite.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user