// One-off Tempo USDC transfer. // // Fill in the CONFIG block below, then run: // /tmp/tempo-transfer/node_modules/.bin/tsx scripts/tempo-transfer.ts // // (The repo's node_modules has a @noble/hashes version conflict that breaks // viem here, so the script runs from an isolated install at /tmp/tempo-transfer. // To rebuild that dir if it disappears: // mkdir -p /tmp/tempo-transfer && cd /tmp/tempo-transfer \ // && npm init -y && npm install viem@2.47.6 tsx) // ─── CONFIG ────────────────────────────────────────────────────────────────── const FROM = { privateKey: '82b62a625382eef1007a77d6eb3d848a9d1df0c9b59d2bd578893962003c17e2', wallet: '0x084A80e395FaE8Aaf58F591f47F63DA1EeF06bbC', }; const TO = { privateKey: '0x0a5194196773b67790f0da564337929b842c1c17f9c445b6c449205dbd426a49', wallet: '0xc65d8a0dB80f637337B87e42b8BEb5D86D46f942', }; const AMOUNT_USD = '1'; // ───────────────────────────────────────────────────────────────────────────── import { createPublicClient, createWalletClient, http, formatUnits, parseUnits, } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { tempo } from 'viem/chains'; const USDC = '0x20c000000000000000000000b9537d11c60e8b50' as const; const DECIMALS = 6; const RPC = 'https://rpc.tempo.xyz'; const erc20Abi = [ { name: 'balanceOf', type: 'function', stateMutability: 'view', inputs: [{ name: 'account', type: 'address' }], outputs: [{ name: '', type: 'uint256' }], }, { name: 'transfer', type: 'function', stateMutability: 'nonpayable', inputs: [ { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }, ], outputs: [{ name: '', type: 'bool' }], }, ] as const; function normalizePk(pk: string): `0x${string}` { const trimmed = pk.trim(); return (trimmed.startsWith('0x') ? trimmed : `0x${trimmed}`) as `0x${string}`; } async function main() { const account = privateKeyToAccount(normalizePk(FROM.privateKey)); const to = TO.wallet as `0x${string}`; const amount = parseUnits(AMOUNT_USD, DECIMALS); if (account.address.toLowerCase() !== FROM.wallet.toLowerCase()) { console.error(`FROM.privateKey derives ${account.address}, but FROM.wallet is ${FROM.wallet}`); process.exit(1); } const publicClient = createPublicClient({ chain: tempo, transport: http(RPC) }); const walletClient = createWalletClient({ account, chain: tempo, transport: http(RPC) }); console.log(`From: ${account.address}`); console.log(`To: ${to}`); console.log(`Amount: ${AMOUNT_USD} USDC (${amount} base units)`); const balanceBefore = await publicClient.readContract({ address: USDC, abi: erc20Abi, functionName: 'balanceOf', args: [account.address], }); console.log(`Sender balance before: ${formatUnits(balanceBefore, DECIMALS)} USDC`); if (balanceBefore < amount) { console.error('Insufficient balance'); process.exit(1); } const hash = await walletClient.writeContract({ address: USDC, abi: erc20Abi, functionName: 'transfer', args: [to, amount], }); console.log(`Tx hash: ${hash}`); const receipt = await publicClient.waitForTransactionReceipt({ hash }); console.log(`Status: ${receipt.status} Block: ${receipt.blockNumber}`); const [balanceFromAfter, balanceToAfter] = await Promise.all([ publicClient.readContract({ address: USDC, abi: erc20Abi, functionName: 'balanceOf', args: [account.address], }), publicClient.readContract({ address: USDC, abi: erc20Abi, functionName: 'balanceOf', args: [to], }), ]); console.log(`Sender balance after: ${formatUnits(balanceFromAfter, DECIMALS)} USDC`); console.log(`Recipient balance after: ${formatUnits(balanceToAfter, DECIMALS)} USDC`); } main().catch((err) => { console.error(err); process.exit(1); });