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:
64
sdk/src/config.ts
Normal file
64
sdk/src/config.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Configuration for Tatum SDK integration with ChainID 138
|
||||
*/
|
||||
|
||||
// Load environment variables
|
||||
import * as dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
export const CHAIN_ID = 138;
|
||||
export const CHAIN_ID_HEX = '0x8a'; // 138 in hex
|
||||
export const CHAIN_NAME = 'DeFi Oracle Meta Mainnet';
|
||||
|
||||
/**
|
||||
* Default RPC endpoints
|
||||
* Update these to point to your deployed RPC nodes
|
||||
*/
|
||||
export const DEFAULT_RPC_URL = process.env.RPC_URL || 'http://localhost:8545';
|
||||
export const DEFAULT_WS_URL = process.env.WS_URL || 'ws://localhost:8546';
|
||||
|
||||
/**
|
||||
* Network configuration for ChainID 138
|
||||
*/
|
||||
export const NETWORK_CONFIG = {
|
||||
chainId: CHAIN_ID,
|
||||
chainIdHex: CHAIN_ID_HEX,
|
||||
name: CHAIN_NAME,
|
||||
nativeCurrency: {
|
||||
name: 'Ether',
|
||||
symbol: 'ETH',
|
||||
decimals: 18,
|
||||
},
|
||||
rpcUrls: {
|
||||
default: {
|
||||
http: [DEFAULT_RPC_URL],
|
||||
webSocket: [DEFAULT_WS_URL],
|
||||
},
|
||||
},
|
||||
blockExplorers: {
|
||||
default: {
|
||||
name: 'Blockscout',
|
||||
url: process.env.EXPLORER_URL || 'https://explorer.d-bis.org',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Tatum SDK configuration
|
||||
*/
|
||||
export interface TatumConfig {
|
||||
rpcUrl?: string;
|
||||
wsUrl?: string;
|
||||
verbose?: boolean;
|
||||
apiKey?: string; // Optional: for Tatum cloud features (not used for custom RPC)
|
||||
}
|
||||
|
||||
export const getTatumConfig = (config?: TatumConfig): TatumConfig => {
|
||||
return {
|
||||
rpcUrl: config?.rpcUrl || DEFAULT_RPC_URL,
|
||||
wsUrl: config?.wsUrl || DEFAULT_WS_URL,
|
||||
verbose: config?.verbose ?? true,
|
||||
apiKey: config?.apiKey,
|
||||
};
|
||||
};
|
||||
|
||||
53
sdk/src/examples/basic-usage.ts
Normal file
53
sdk/src/examples/basic-usage.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Basic usage example: Connect to ChainID 138 and query chain data
|
||||
*/
|
||||
|
||||
import { initTatumSDK, verifyConnection } from '../tatum-client';
|
||||
import { CHAIN_ID, CHAIN_NAME } from '../config';
|
||||
|
||||
async function main() {
|
||||
console.log(`Connecting to ${CHAIN_NAME} (ChainID ${CHAIN_ID})...`);
|
||||
|
||||
// Initialize Tatum SDK with custom RPC URL
|
||||
const tatum = await initTatumSDK({
|
||||
rpcUrl: process.env.RPC_URL || 'http://localhost:8545',
|
||||
verbose: true,
|
||||
});
|
||||
|
||||
// Verify connection and chain ID
|
||||
console.log('\n=== Verifying Connection ===');
|
||||
const connectionInfo = await verifyConnection(tatum);
|
||||
console.log('Connection verified!');
|
||||
console.log(`Chain ID: ${connectionInfo.chainId} (${connectionInfo.chainIdHex})`);
|
||||
console.log(`Current Block: ${connectionInfo.blockNumber}`);
|
||||
console.log(`Network Version: ${connectionInfo.netVersion}`);
|
||||
console.log(`Syncing: ${connectionInfo.syncing}`);
|
||||
|
||||
// Query additional chain data
|
||||
console.log('\n=== Querying Chain Data ===');
|
||||
|
||||
// Get latest block
|
||||
const latestBlock = await tatum.rpc.request('eth_getBlockByNumber', ['latest', false]);
|
||||
console.log('Latest Block:', JSON.stringify(latestBlock, null, 2));
|
||||
|
||||
// Get gas price
|
||||
const gasPrice = await tatum.rpc.request('eth_gasPrice', []);
|
||||
console.log('Gas Price:', gasPrice);
|
||||
|
||||
// Get peer count
|
||||
const peerCount = await tatum.rpc.request('net_peerCount', []);
|
||||
console.log('Peer Count:', peerCount);
|
||||
|
||||
// Get balance of an address (example)
|
||||
const exampleAddress = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb';
|
||||
const balance = await tatum.rpc.request('eth_getBalance', [exampleAddress, 'latest']);
|
||||
console.log(`Balance of ${exampleAddress}:`, balance);
|
||||
|
||||
console.log('\n=== Basic Usage Example Complete ===');
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
114
sdk/src/examples/deploy-contract.ts
Normal file
114
sdk/src/examples/deploy-contract.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Contract deployment example: Deploy a simple contract on ChainID 138
|
||||
*/
|
||||
|
||||
import { ethers } from 'ethers';
|
||||
import { initTatumSDK } from '../tatum-client';
|
||||
import { CHAIN_ID, CHAIN_NAME, DEFAULT_RPC_URL } from '../config';
|
||||
|
||||
// Simple storage contract bytecode and ABI
|
||||
const STORAGE_CONTRACT_BYTECODE = '0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d1565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b6000819050919050565b61009a81610087565b81146100a557600080fd5b50565b6000813590506100b781610091565b92915050565b6000602082840312156100d3576100d26000fd5b60006100e1848285016100a8565b91505092915050565b6100f381610087565b82525050565b600060208201905061010e60008301846100ea565b9291505056fea2646970667358221220c5f5c5e5c5e5c5e5c5e5c5e5c5e5c5e5c5e5c5e5c5e5c5e5c5e5c5e5c64736f6c63430008070033';
|
||||
const STORAGE_CONTRACT_ABI = [
|
||||
{
|
||||
inputs: [],
|
||||
name: 'retrieve',
|
||||
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
inputs: [{ internalType: 'uint256', name: 'num', type: 'uint256' }],
|
||||
name: 'store',
|
||||
outputs: [],
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
];
|
||||
|
||||
async function main() {
|
||||
console.log(`Deploying contract on ${CHAIN_NAME} (ChainID ${CHAIN_ID})...`);
|
||||
|
||||
// Load private key from environment
|
||||
const privateKey = process.env.PRIVATE_KEY;
|
||||
if (!privateKey) {
|
||||
throw new Error('PRIVATE_KEY environment variable not set');
|
||||
}
|
||||
|
||||
// Initialize ethers provider with ChainID 138
|
||||
const provider = new ethers.JsonRpcProvider(DEFAULT_RPC_URL, {
|
||||
chainId: CHAIN_ID,
|
||||
name: 'defi-oracle-mainnet',
|
||||
});
|
||||
|
||||
// Create wallet
|
||||
const wallet = new ethers.Wallet(privateKey, provider);
|
||||
console.log('Wallet Address:', wallet.address);
|
||||
|
||||
// Get balance
|
||||
const balance = await provider.getBalance(wallet.address);
|
||||
console.log('Balance:', ethers.formatEther(balance), 'ETH');
|
||||
|
||||
// Deploy contract
|
||||
console.log('\nDeploying Storage contract...');
|
||||
const factory = new ethers.ContractFactory(
|
||||
STORAGE_CONTRACT_ABI,
|
||||
STORAGE_CONTRACT_BYTECODE,
|
||||
wallet
|
||||
);
|
||||
|
||||
const contract = await factory.deploy({
|
||||
chainId: CHAIN_ID, // Important: Must match ChainID 138
|
||||
});
|
||||
|
||||
console.log('Deployment Transaction Hash:', contract.deploymentTransaction()?.hash);
|
||||
console.log('Waiting for deployment...');
|
||||
|
||||
await contract.waitForDeployment();
|
||||
const contractAddress = await contract.getAddress();
|
||||
console.log('Contract deployed at:', contractAddress);
|
||||
|
||||
// Interact with contract
|
||||
console.log('\n=== Interacting with Contract ===');
|
||||
|
||||
// Store a value
|
||||
const valueToStore = 42;
|
||||
console.log(`Storing value: ${valueToStore}`);
|
||||
const storeTx = await contract.store(valueToStore, {
|
||||
chainId: CHAIN_ID,
|
||||
});
|
||||
await storeTx.wait();
|
||||
console.log('Store transaction confirmed:', storeTx.hash);
|
||||
|
||||
// Retrieve the value
|
||||
const retrievedValue = await contract.retrieve();
|
||||
console.log('Retrieved value:', retrievedValue.toString());
|
||||
|
||||
// Verify using Tatum SDK
|
||||
console.log('\n=== Verifying Contract with Tatum SDK ===');
|
||||
const tatum = await initTatumSDK({
|
||||
rpcUrl: DEFAULT_RPC_URL,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
// Get contract code
|
||||
const code = await tatum.rpc.request('eth_getCode', [contractAddress, 'latest']);
|
||||
console.log('Contract Code:', code ? 'Present' : 'Not found');
|
||||
|
||||
// Get contract storage
|
||||
const storageValue = await tatum.rpc.request('eth_getStorageAt', [
|
||||
contractAddress,
|
||||
'0x0', // Storage slot 0
|
||||
'latest',
|
||||
]);
|
||||
console.log('Storage Value (slot 0):', storageValue);
|
||||
|
||||
console.log('\n=== Contract Deployment Example Complete ===');
|
||||
console.log(`Contract Address: ${contractAddress}`);
|
||||
console.log(`Explorer: ${process.env.EXPLORER_URL || 'https://explorer.defi-oracle-meta-mainnet.org'}/address/${contractAddress}`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
88
sdk/src/examples/send-transaction.ts
Normal file
88
sdk/src/examples/send-transaction.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Transaction example: Send a transaction on ChainID 138
|
||||
*
|
||||
* Important: Transactions must be signed with chainId: 138 (EIP-155)
|
||||
*/
|
||||
|
||||
import { ethers } from 'ethers';
|
||||
import { initTatumSDK } from '../tatum-client';
|
||||
import { CHAIN_ID, CHAIN_NAME, DEFAULT_RPC_URL } from '../config';
|
||||
|
||||
async function main() {
|
||||
console.log(`Sending transaction on ${CHAIN_NAME} (ChainID ${CHAIN_ID})...`);
|
||||
|
||||
// Load private key from environment
|
||||
const privateKey = process.env.PRIVATE_KEY;
|
||||
if (!privateKey) {
|
||||
throw new Error('PRIVATE_KEY environment variable not set');
|
||||
}
|
||||
|
||||
// Initialize ethers provider with ChainID 138
|
||||
const provider = new ethers.JsonRpcProvider(DEFAULT_RPC_URL, {
|
||||
chainId: CHAIN_ID,
|
||||
name: 'defi-oracle-mainnet',
|
||||
});
|
||||
|
||||
// Create wallet
|
||||
const wallet = new ethers.Wallet(privateKey, provider);
|
||||
console.log('Wallet Address:', wallet.address);
|
||||
|
||||
// Get balance
|
||||
const balance = await provider.getBalance(wallet.address);
|
||||
console.log('Balance:', ethers.formatEther(balance), 'ETH');
|
||||
|
||||
// Check if we have enough balance
|
||||
if (balance === 0n) {
|
||||
throw new Error('Insufficient balance. Please fund the wallet first.');
|
||||
}
|
||||
|
||||
// Get recipient address from environment or use a test address
|
||||
const recipient = process.env.RECIPIENT_ADDRESS || '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb';
|
||||
const amount = process.env.AMOUNT || '0.01'; // ETH
|
||||
|
||||
console.log(`\nSending ${amount} ETH to ${recipient}...`);
|
||||
|
||||
// Send transaction with correct chainId
|
||||
const tx = await wallet.sendTransaction({
|
||||
to: recipient,
|
||||
value: ethers.parseEther(amount),
|
||||
chainId: CHAIN_ID, // Important: Must match ChainID 138
|
||||
});
|
||||
|
||||
console.log('Transaction Hash:', tx.hash);
|
||||
console.log('Waiting for confirmation...');
|
||||
|
||||
// Wait for transaction to be mined
|
||||
const receipt = await tx.wait();
|
||||
console.log('Transaction confirmed!');
|
||||
console.log('Block Number:', receipt?.blockNumber);
|
||||
console.log('Gas Used:', receipt?.gasUsed.toString());
|
||||
|
||||
// Verify transaction using Tatum SDK
|
||||
console.log('\n=== Verifying Transaction with Tatum SDK ===');
|
||||
const tatum = await initTatumSDK({
|
||||
rpcUrl: DEFAULT_RPC_URL,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
const txFromChain = await tatum.rpc.request('eth_getTransactionByHash', [tx.hash]);
|
||||
console.log('Transaction from chain:', JSON.stringify(txFromChain, null, 2));
|
||||
|
||||
// Verify chainId in transaction
|
||||
if (txFromChain && typeof txFromChain === 'object' && 'chainId' in txFromChain) {
|
||||
const txChainId = parseInt(txFromChain.chainId as string, 16);
|
||||
if (txChainId === CHAIN_ID) {
|
||||
console.log('✓ ChainID verified in transaction');
|
||||
} else {
|
||||
console.error(`✗ ChainID mismatch! Expected ${CHAIN_ID}, got ${txChainId}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n=== Transaction Example Complete ===');
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
20
sdk/src/index.ts
Normal file
20
sdk/src/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Tatum SDK Integration for ChainID 138
|
||||
* Main export file
|
||||
*/
|
||||
|
||||
export { initTatumSDK, initTatumSDKWithNodes, verifyConnection } from './tatum-client';
|
||||
export {
|
||||
CHAIN_ID,
|
||||
CHAIN_ID_HEX,
|
||||
CHAIN_NAME,
|
||||
DEFAULT_RPC_URL,
|
||||
DEFAULT_WS_URL,
|
||||
NETWORK_CONFIG,
|
||||
getTatumConfig,
|
||||
type TatumConfig,
|
||||
} from './config';
|
||||
|
||||
// MetaMask integration helpers
|
||||
export * from './metamask';
|
||||
|
||||
41
sdk/src/metamask.ts
Normal file
41
sdk/src/metamask.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* MetaMask integration helpers for ChainID 138
|
||||
* Integrated into the main SDK
|
||||
*
|
||||
* Note: MetaMask SDK is browser-only. In Node.js environments, only configuration exports are available.
|
||||
*/
|
||||
|
||||
// Always export configuration (available in both browser and Node.js)
|
||||
export {
|
||||
CHAIN_ID,
|
||||
CHAIN_ID_HEX,
|
||||
CHAIN_NAME,
|
||||
RPC_URLS,
|
||||
BLOCK_EXPLORER_URL,
|
||||
NETWORK_METADATA,
|
||||
CAIP2_IDENTIFIER,
|
||||
} from '../../metamask-sdk/src/config';
|
||||
|
||||
export type {
|
||||
EthereumProvider,
|
||||
AddEthereumChainParameter,
|
||||
WatchAssetParameters,
|
||||
MetaMaskError,
|
||||
} from '../../metamask-sdk/src/types';
|
||||
|
||||
// Browser-only exports (conditional)
|
||||
if (typeof window !== 'undefined') {
|
||||
// Browser environment - export MetaMask SDK functions
|
||||
export {
|
||||
addNetwork,
|
||||
addOrSwitchNetwork,
|
||||
isNetworkAdded,
|
||||
getEthereumProvider,
|
||||
switchNetwork,
|
||||
getCurrentChainId,
|
||||
isOnChain138,
|
||||
addToken,
|
||||
addTokenFromList,
|
||||
} from '../../metamask-sdk/src';
|
||||
}
|
||||
|
||||
182
sdk/src/smoke-test.ts
Normal file
182
sdk/src/smoke-test.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* Smoke test: Comprehensive test of Tatum SDK integration with ChainID 138
|
||||
*
|
||||
* This test verifies:
|
||||
* 1. RPC connectivity
|
||||
* 2. Chain ID verification
|
||||
* 3. Basic RPC calls
|
||||
* 4. Transaction signing (dry run)
|
||||
* 5. Contract interaction (if deployed)
|
||||
*/
|
||||
|
||||
import { initTatumSDK, verifyConnection } from './tatum-client';
|
||||
import { CHAIN_ID, CHAIN_NAME, DEFAULT_RPC_URL } from './config';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
interface TestResult {
|
||||
name: string;
|
||||
passed: boolean;
|
||||
error?: string;
|
||||
details?: any;
|
||||
}
|
||||
|
||||
async function runTest(name: string, testFn: () => Promise<any>): Promise<TestResult> {
|
||||
try {
|
||||
const result = await testFn();
|
||||
return {
|
||||
name,
|
||||
passed: true,
|
||||
details: result,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
name,
|
||||
passed: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('='.repeat(70));
|
||||
console.log(`Smoke Test: ${CHAIN_NAME} (ChainID ${CHAIN_ID})`);
|
||||
console.log('='.repeat(70));
|
||||
console.log();
|
||||
|
||||
const results: TestResult[] = [];
|
||||
|
||||
// Test 1: Initialize Tatum SDK
|
||||
console.log('Test 1: Initializing Tatum SDK...');
|
||||
const tatum = await initTatumSDK({
|
||||
rpcUrl: DEFAULT_RPC_URL,
|
||||
verbose: false,
|
||||
});
|
||||
results.push(await runTest('Initialize Tatum SDK', async () => {
|
||||
if (!tatum) throw new Error('Tatum SDK initialization failed');
|
||||
return { initialized: true };
|
||||
}));
|
||||
console.log(results[results.length - 1].passed ? '✓' : '✗', results[results.length - 1].name);
|
||||
|
||||
// Test 2: Verify connection and chain ID
|
||||
console.log('Test 2: Verifying connection and chain ID...');
|
||||
results.push(await runTest('Verify Connection', async () => {
|
||||
return await verifyConnection(tatum);
|
||||
}));
|
||||
console.log(results[results.length - 1].passed ? '✓' : '✗', results[results.length - 1].name);
|
||||
if (results[results.length - 1].passed && results[results.length - 1].details) {
|
||||
const info = results[results.length - 1].details;
|
||||
console.log(` Chain ID: ${info.chainId}, Block: ${info.blockNumber}`);
|
||||
}
|
||||
|
||||
// Test 3: Test basic RPC calls
|
||||
console.log('Test 3: Testing basic RPC calls...');
|
||||
results.push(await runTest('RPC: eth_blockNumber', async () => {
|
||||
const blockNumber = await tatum.rpc.request('eth_blockNumber', []);
|
||||
return { blockNumber };
|
||||
}));
|
||||
console.log(results[results.length - 1].passed ? '✓' : '✗', results[results.length - 1].name);
|
||||
|
||||
results.push(await runTest('RPC: eth_chainId', async () => {
|
||||
const chainId = await tatum.rpc.request('eth_chainId', []);
|
||||
const chainIdDecimal = parseInt(chainId as string, 16);
|
||||
if (chainIdDecimal !== CHAIN_ID) {
|
||||
throw new Error(`Chain ID mismatch: expected ${CHAIN_ID}, got ${chainIdDecimal}`);
|
||||
}
|
||||
return { chainId: chainIdDecimal };
|
||||
}));
|
||||
console.log(results[results.length - 1].passed ? '✓' : '✗', results[results.length - 1].name);
|
||||
|
||||
results.push(await runTest('RPC: eth_gasPrice', async () => {
|
||||
const gasPrice = await tatum.rpc.request('eth_gasPrice', []);
|
||||
return { gasPrice };
|
||||
}));
|
||||
console.log(results[results.length - 1].passed ? '✓' : '✗', results[results.length - 1].name);
|
||||
|
||||
results.push(await runTest('RPC: net_peerCount', async () => {
|
||||
const peerCount = await tatum.rpc.request('net_peerCount', []);
|
||||
return { peerCount: parseInt(peerCount as string, 16) };
|
||||
}));
|
||||
console.log(results[results.length - 1].passed ? '✓' : '✗', results[results.length - 1].name);
|
||||
|
||||
// Test 4: Test ethers.js provider
|
||||
console.log('Test 4: Testing ethers.js provider...');
|
||||
results.push(await runTest('Ethers: Provider Initialization', async () => {
|
||||
const provider = new ethers.JsonRpcProvider(DEFAULT_RPC_URL, {
|
||||
chainId: CHAIN_ID,
|
||||
name: 'defi-oracle-mainnet',
|
||||
});
|
||||
const network = await provider.getNetwork();
|
||||
if (Number(network.chainId) !== CHAIN_ID) {
|
||||
throw new Error(`Chain ID mismatch: expected ${CHAIN_ID}, got ${Number(network.chainId)}`);
|
||||
}
|
||||
return { chainId: Number(network.chainId), name: network.name };
|
||||
}));
|
||||
console.log(results[results.length - 1].passed ? '✓' : '✗', results[results.length - 1].name);
|
||||
|
||||
// Test 5: Test transaction signing (dry run)
|
||||
console.log('Test 5: Testing transaction signing (dry run)...');
|
||||
if (process.env.PRIVATE_KEY) {
|
||||
results.push(await runTest('Ethers: Transaction Signing', async () => {
|
||||
const provider = new ethers.JsonRpcProvider(DEFAULT_RPC_URL, {
|
||||
chainId: CHAIN_ID,
|
||||
name: 'defi-oracle-mainnet',
|
||||
});
|
||||
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
|
||||
|
||||
// Create a transaction (but don't send it)
|
||||
const tx = await wallet.populateTransaction({
|
||||
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
|
||||
value: ethers.parseEther('0.001'),
|
||||
chainId: CHAIN_ID,
|
||||
});
|
||||
|
||||
// Verify chainId is set correctly
|
||||
if (tx.chainId !== BigInt(CHAIN_ID)) {
|
||||
throw new Error(`Transaction chainId mismatch: expected ${CHAIN_ID}, got ${tx.chainId}`);
|
||||
}
|
||||
|
||||
return { chainId: Number(tx.chainId), to: tx.to, value: tx.value?.toString() };
|
||||
}));
|
||||
console.log(results[results.length - 1].passed ? '✓' : '✗', results[results.length - 1].name);
|
||||
} else {
|
||||
console.log('⚠ Skipping transaction signing test (PRIVATE_KEY not set)');
|
||||
}
|
||||
|
||||
// Summary
|
||||
console.log();
|
||||
console.log('='.repeat(70));
|
||||
console.log('Test Summary');
|
||||
console.log('='.repeat(70));
|
||||
|
||||
const passed = results.filter(r => r.passed).length;
|
||||
const failed = results.filter(r => !r.passed).length;
|
||||
|
||||
console.log(`Total Tests: ${results.length}`);
|
||||
console.log(`Passed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
console.log();
|
||||
|
||||
if (failed > 0) {
|
||||
console.log('Failed Tests:');
|
||||
results.filter(r => !r.passed).forEach(r => {
|
||||
console.log(` ✗ ${r.name}: ${r.error}`);
|
||||
});
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log('='.repeat(70));
|
||||
|
||||
if (failed === 0) {
|
||||
console.log('✓ All tests passed!');
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log('✗ Some tests failed');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
103
sdk/src/tatum-client.ts
Normal file
103
sdk/src/tatum-client.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Tatum SDK client initialization for ChainID 138
|
||||
*/
|
||||
|
||||
import { TatumSDK, Network, Ethereum, RpcNodeType } from '@tatumio/tatum';
|
||||
import { getTatumConfig, CHAIN_ID, CHAIN_ID_HEX, NETWORK_CONFIG, TatumConfig } from './config';
|
||||
|
||||
/**
|
||||
* Initialize Tatum SDK for ChainID 138
|
||||
*
|
||||
* Note: With custom RPC, only RPC calls are redirected to your node.
|
||||
* Tatum's cloud services (Notifications, Blockchain Data, etc.) won't work
|
||||
* on unsupported/private chains - only raw JSON-RPC will work.
|
||||
*
|
||||
* @param config Optional configuration
|
||||
* @returns Initialized Tatum SDK instance
|
||||
*/
|
||||
export async function initTatumSDK(config?: TatumConfig) {
|
||||
const tatumConfig = getTatumConfig(config);
|
||||
|
||||
// Option A: Simple initialization with custom RPC URL
|
||||
const tatum = await TatumSDK.init<Ethereum>({
|
||||
network: Network.ETHEREUM, // Use Ethereum network type for EVM compatibility
|
||||
rpcUrl: tatumConfig.rpcUrl,
|
||||
verbose: tatumConfig.verbose,
|
||||
// apiKey is optional and only needed for Tatum cloud features
|
||||
// With custom RPC, cloud features won't work anyway
|
||||
...(tatumConfig.apiKey && { apiKey: tatumConfig.apiKey }),
|
||||
});
|
||||
|
||||
return tatum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Tatum SDK with explicit node configuration
|
||||
*
|
||||
* @param config Optional configuration
|
||||
* @returns Initialized Tatum SDK instance
|
||||
*/
|
||||
export async function initTatumSDKWithNodes(config?: TatumConfig) {
|
||||
const tatumConfig = getTatumConfig(config);
|
||||
|
||||
// Option B: Explicit node configuration
|
||||
const tatum = await TatumSDK.init<Ethereum>({
|
||||
network: Network.ETHEREUM,
|
||||
rpc: {
|
||||
nodes: [
|
||||
{
|
||||
url: tatumConfig.rpcUrl!,
|
||||
type: RpcNodeType.NORMAL,
|
||||
},
|
||||
],
|
||||
},
|
||||
verbose: tatumConfig.verbose,
|
||||
...(tatumConfig.apiKey && { apiKey: tatumConfig.apiKey }),
|
||||
});
|
||||
|
||||
return tatum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify connectivity and chain ID
|
||||
*
|
||||
* @param tatum Tatum SDK instance
|
||||
* @returns Object with block number and chain ID
|
||||
*/
|
||||
export async function verifyConnection(tatum: Ethereum) {
|
||||
try {
|
||||
// Get current block number
|
||||
const blockNumberHex = await tatum.rpc.request('eth_blockNumber', []);
|
||||
const blockNumber = parseInt(blockNumberHex as string, 16);
|
||||
|
||||
// Get chain ID
|
||||
const chainIdHex = await tatum.rpc.request('eth_chainId', []);
|
||||
const chainId = parseInt(chainIdHex as string, 16);
|
||||
|
||||
// Verify chain ID
|
||||
if (chainId !== CHAIN_ID) {
|
||||
throw new Error(
|
||||
`Chain ID mismatch! Expected ${CHAIN_ID} (${CHAIN_ID_HEX}), got ${chainId} (${chainIdHex})`
|
||||
);
|
||||
}
|
||||
|
||||
// Get network version
|
||||
const netVersion = await tatum.rpc.request('net_version', []);
|
||||
|
||||
// Get sync status
|
||||
const syncing = await tatum.rpc.request('eth_syncing', []);
|
||||
|
||||
return {
|
||||
blockNumber,
|
||||
blockNumberHex,
|
||||
chainId,
|
||||
chainIdHex,
|
||||
netVersion,
|
||||
syncing,
|
||||
networkConfig: NETWORK_CONFIG,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to verify connection: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
82
sdk/src/test-connection.ts
Normal file
82
sdk/src/test-connection.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Test connection to ChainID 138 RPC endpoint
|
||||
*/
|
||||
|
||||
import { initTatumSDK, verifyConnection } from './tatum-client';
|
||||
import { CHAIN_ID, CHAIN_NAME, DEFAULT_RPC_URL } from './config';
|
||||
|
||||
async function main() {
|
||||
console.log('='.repeat(60));
|
||||
console.log(`Testing Connection to ${CHAIN_NAME}`);
|
||||
console.log(`ChainID: ${CHAIN_ID} (0x${CHAIN_ID.toString(16)})`);
|
||||
console.log(`RPC URL: ${DEFAULT_RPC_URL}`);
|
||||
console.log('='.repeat(60));
|
||||
console.log();
|
||||
|
||||
try {
|
||||
// Initialize Tatum SDK
|
||||
console.log('Initializing Tatum SDK...');
|
||||
const tatum = await initTatumSDK({
|
||||
rpcUrl: DEFAULT_RPC_URL,
|
||||
verbose: true,
|
||||
});
|
||||
console.log('✓ Tatum SDK initialized\n');
|
||||
|
||||
// Verify connection
|
||||
console.log('Verifying connection...');
|
||||
const connectionInfo = await verifyConnection(tatum);
|
||||
console.log('✓ Connection verified\n');
|
||||
|
||||
// Display connection info
|
||||
console.log('Connection Information:');
|
||||
console.log('-'.repeat(60));
|
||||
console.log(`Chain ID: ${connectionInfo.chainId} (${connectionInfo.chainIdHex})`);
|
||||
console.log(`Current Block: ${connectionInfo.blockNumber}`);
|
||||
console.log(`Network Version: ${connectionInfo.netVersion}`);
|
||||
console.log(`Syncing: ${connectionInfo.syncing}`);
|
||||
console.log('-'.repeat(60));
|
||||
console.log();
|
||||
|
||||
// Test additional RPC calls
|
||||
console.log('Testing additional RPC calls...');
|
||||
|
||||
// Get gas price
|
||||
const gasPrice = await tatum.rpc.request('eth_gasPrice', []);
|
||||
console.log(`✓ Gas Price: ${gasPrice}`);
|
||||
|
||||
// Get peer count
|
||||
const peerCount = await tatum.rpc.request('net_peerCount', []);
|
||||
const peerCountDecimal = parseInt(peerCount as string, 16);
|
||||
console.log(`✓ Peer Count: ${peerCountDecimal}`);
|
||||
|
||||
// Get latest block
|
||||
const latestBlock = await tatum.rpc.request('eth_getBlockByNumber', ['latest', false]);
|
||||
if (latestBlock && typeof latestBlock === 'object') {
|
||||
console.log(`✓ Latest Block: ${JSON.stringify(latestBlock, null, 2).substring(0, 200)}...`);
|
||||
}
|
||||
|
||||
console.log();
|
||||
console.log('='.repeat(60));
|
||||
console.log('✓ All tests passed!');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
} catch (error) {
|
||||
console.error();
|
||||
console.error('='.repeat(60));
|
||||
console.error('✗ Connection test failed!');
|
||||
console.error('='.repeat(60));
|
||||
console.error();
|
||||
console.error('Error:', error instanceof Error ? error.message : String(error));
|
||||
console.error();
|
||||
console.error('Troubleshooting:');
|
||||
console.error('1. Ensure RPC node is running and accessible');
|
||||
console.error('2. Check RPC_URL environment variable');
|
||||
console.error('3. Verify firewall/network settings');
|
||||
console.error('4. Check node logs for errors');
|
||||
console.error();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
Reference in New Issue
Block a user