/** * Contracts — Read smart contracts, multicall, simulate writes. * * Run: npx tsx examples/12-contracts.ts */ import { Spectrum } from '@spectrumnodes/sdk'; const spectrum = new Spectrum({ api: process.env.SPECTRUM_API! }); // Common ERC-20 ABI (only the functions we need) const ERC20_ABI = [ { name: 'name', type: 'function' as const, inputs: [], outputs: [{ type: 'string' }], stateMutability: 'view' as const, }, { name: 'symbol', type: 'function' as const, inputs: [], outputs: [{ type: 'string' }], stateMutability: 'view' as const, }, { name: 'decimals', type: 'function' as const, inputs: [], outputs: [{ type: 'uint8' }], stateMutability: 'view' as const, }, { name: 'totalSupply', type: 'function' as const, inputs: [], outputs: [{ type: 'uint256' }], stateMutability: 'view' as const, }, { name: 'balanceOf', type: 'function' as const, inputs: [{ name: 'account', type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'view' as const, }, { name: 'allowance', type: 'function' as const, inputs: [ { name: 'owner', type: 'address' }, { name: 'spender', type: 'address' }, ], outputs: [{ type: 'uint256' }], stateMutability: 'view' as const, }, { name: 'transfer', type: 'function' as const, inputs: [ { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }, ], outputs: [{ type: 'bool' }], stateMutability: 'nonpayable' as const, }, ]; const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; const VITALIK = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; async function main() { // --- readContract (single call) --- const name = await spectrum.contracts.readContract('ethereum', { address: USDC, abi: ERC20_ABI, functionName: 'name', }); console.log(`Token name: ${name}`); const symbol = await spectrum.contracts.readContract('ethereum', { address: USDC, abi: ERC20_ABI, functionName: 'symbol', }); console.log(`Token symbol: ${symbol}`); const decimals = await spectrum.contracts.readContract('ethereum', { address: USDC, abi: ERC20_ABI, functionName: 'decimals', }); console.log(`Decimals: ${decimals}`); const totalSupply = await spectrum.contracts.readContract('ethereum', { address: USDC, abi: ERC20_ABI, functionName: 'totalSupply', }); console.log(`Total supply: ${Number(totalSupply) / 10 ** Number(decimals)} ${symbol}`); // --- readContract with args --- const balance = await spectrum.contracts.readContract('ethereum', { address: USDC, abi: ERC20_ABI, functionName: 'balanceOf', args: [VITALIK], }); console.log(`\nVitalik's USDC: ${Number(balance) / 10 ** Number(decimals)}`); // --- readContract at historical block --- const oldBalance = await spectrum.contracts.readContract('ethereum', { address: USDC, abi: ERC20_ABI, functionName: 'balanceOf', args: [VITALIK], blockTag: 19000000, }); console.log(`Vitalik's USDC at block 19M: ${Number(oldBalance) / 10 ** Number(decimals)}`); // --- multicall (batch multiple reads in one RPC call) --- const results = await spectrum.contracts.multicall('ethereum', { calls: [ { address: USDC, abi: ERC20_ABI, functionName: 'name' }, { address: USDC, abi: ERC20_ABI, functionName: 'symbol' }, { address: USDC, abi: ERC20_ABI, functionName: 'decimals' }, { address: USDC, abi: ERC20_ABI, functionName: 'totalSupply' }, { address: USDC, abi: ERC20_ABI, functionName: 'balanceOf', args: [VITALIK] }, ], }); console.log('\nMulticall results:'); for (const r of results) { console.log(` ${r.status}: ${r.result}`); } // --- simulateContract (dry-run a write before sending) --- const sim = await spectrum.contracts.simulateContract('ethereum', { address: USDC, abi: ERC20_ABI, functionName: 'transfer', args: ['0x0000000000000000000000000000000000000001', 1000000n], // 1 USDC from: VITALIK, }); if (sim.success) { console.log(`\nTransfer simulation: would succeed`); console.log(` Return value: ${sim.result}`); console.log(` Estimated gas: ${sim.gasUsed}`); } else { console.log(`\nTransfer simulation: would revert`); console.log(` Error: ${sim.error}`); } // --- Read any contract (not just ERC-20) --- const wethAbi = [ { name: 'deposit', type: 'function' as const, inputs: [], outputs: [], stateMutability: 'payable' as const, }, ]; const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; // Simulate depositing 1 ETH to WETH const depositSim = await spectrum.contracts.simulateContract('ethereum', { address: WETH, abi: wethAbi, functionName: 'deposit', from: VITALIK, value: '0xde0b6b3a7640000', // 1 ETH in hex wei }); console.log(`\nWETH deposit simulation: ${depositSim.success ? 'success' : 'fail'}`); if (depositSim.gasUsed) { console.log(` Gas: ${depositSim.gasUsed}`); } // --- getCode (check if address is a contract) --- const code = await spectrum.contracts.getCode('ethereum', USDC); console.log(`\nUSDC contract check:`); console.log(` Is contract: ${code.isContract}`); console.log(` Bytecode size: ${code.bytecodeSize} bytes`); // Check an EOA (should not be a contract) const eoaCode = await spectrum.contracts.getCode('ethereum', VITALIK); console.log(`\nVitalik EOA check:`); console.log(` Is contract: ${eoaCode.isContract}`); } main().catch(console.error);