-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
112 lines (106 loc) · 3.71 KB
/
index.js
File metadata and controls
112 lines (106 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import 'dotenv/config';
import { createPublicClient, createWalletClient, http, formatUnits, parseUnits } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
// --- Chain Configuration ---
const plumeTestnet = {
id: 98867,
name: 'Plume Testnet',
nativeCurrency: { name: 'PLUME', symbol: 'PLUME', decimals: 18 },
rpcUrls: {
default: { http: ['https://testnet-rpc.plume.org'] },
},
blockExplorers: {
default: {
name: 'Plume Testnet Explorer',
url: 'https://testnet-explorer.plume.org',
},
},
testnet: true,
};
// --- USDC Contract Configuration ---
const USDC_ADDRESS = '0xcB5f30e335672893c7eb944B374c196392C19D18';
const USDC_DECIMALS = 6;
const USDC_ABI = [
{
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' }],
},
];
// --- Environment Variables ---
const PRIVATE_KEY_RAW = process.env.PRIVATE_KEY;
const RECIPIENT = process.env.RECIPIENT_ADDRESS || process.env.RECIPIENT;
if (!PRIVATE_KEY_RAW) {
console.error('Error: Set PRIVATE_KEY in your .env file');
process.exit(1);
}
if (!RECIPIENT) {
console.error('Error: Set RECIPIENT_ADDRESS or RECIPIENT in your .env file');
process.exit(1);
}
if (!/^0x[a-fA-F0-9]{40}$/.test(RECIPIENT)) {
console.error('Error: Recipient address is not a valid Ethereum address');
process.exit(1);
}
// --- Private Key Normalization ---
const PRIVATE_KEY = PRIVATE_KEY_RAW.startsWith('0x') ? PRIVATE_KEY_RAW : '0x' + PRIVATE_KEY_RAW;
// --- Client Setup ---
const account = privateKeyToAccount(PRIVATE_KEY);
const publicClient = createPublicClient({ chain: plumeTestnet, transport: http() });
const walletClient = createWalletClient({ account, chain: plumeTestnet, transport: http() });
// --- Main Transfer Logic ---
(async () => {
try {
// Check sender balance
const balance = await publicClient.readContract({
address: USDC_ADDRESS,
abi: USDC_ABI,
functionName: 'balanceOf',
args: [account.address],
});
const balanceFormatted = Number(formatUnits(balance, USDC_DECIMALS));
const amount = 1;
console.log('Sender:', account.address);
console.log('Recipient:', RECIPIENT);
console.log('USDC balance:', balanceFormatted);
if (amount > balanceFormatted) {
console.error('Error: Insufficient USDC balance');
console.log('\nTo get test USDC on Plume testnet:');
console.log('1. Visit the Plume testnet faucet or bridge');
console.log('2. Connect your wallet with address:', account.address);
console.log('3. Request test USDC tokens');
console.log('4. Wait for the transaction to confirm');
console.log('5. Run this script again');
console.log('\nCurrent balance:', balanceFormatted, 'USDC');
console.log('Required amount:', amount, 'USDC');
process.exit(1);
}
const amountInDecimals = parseUnits(amount.toString(), USDC_DECIMALS);
// Send transfer
const hash = await walletClient.writeContract({
address: USDC_ADDRESS,
abi: USDC_ABI,
functionName: 'transfer',
args: [RECIPIENT, amountInDecimals],
});
console.log('Transfer successful!');
console.log('Tx hash:', hash);
console.log('Explorer:', `${plumeTestnet.blockExplorers.default.url}/tx/${hash}`);
} catch (err) {
console.error('Transfer failed:', err.message || err);
process.exit(1);
}
process.exit(0);
})();