|
| 1 | +import { ethers } from 'ethers' |
| 2 | +import { createWalletFromPassword } from './lib/wallet.js' |
| 3 | + |
| 4 | +const RPC_URL = 'https://rpc-amoy.polygon.technology' |
| 5 | +const USDC_ADDRESS = '0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582' |
| 6 | +const USDC_ABI = [ |
| 7 | + 'function transfer(address to, uint256 amount) returns (bool)', |
| 8 | + 'function balanceOf(address account) view returns (uint256)', |
| 9 | + 'function decimals() view returns (uint8)' |
| 10 | +] |
| 11 | + |
| 12 | +// 🔐 WALLET SETUP: Change this to your own unique password! |
| 13 | +// Same password = same wallet address every time (great for tutorials) |
| 14 | +const WALLET_PASSWORD = 'change_me_to_something_unique_like_pizza_unicorn_2024' |
| 15 | + |
| 16 | +// 📤 RECIPIENT: This is a Nimiq-controlled account that collects tutorial demo transfers |
| 17 | +// If you accidentally send large amounts or want your funds back, contact us and we'll return them! |
| 18 | +// |
| 19 | +// 💡 Want to send to yourself instead? Uncomment these lines to create a second wallet: |
| 20 | +// const recipientWallet = createWalletFromPassword('my_second_wallet_password_banana_2024') |
| 21 | +// const RECIPIENT = recipientWallet.address |
| 22 | +// This way you control both wallets and can recover any funds sent! |
| 23 | +// |
| 24 | +// Or create your own wallet with a standard 24-word mnemonic (see lib/wallet.js) |
| 25 | +const RECIPIENT = '0xA3E49ef624bEaC43D29Af86bBFdE975Abaa0E184' |
| 26 | + |
| 27 | +async function main() { |
| 28 | + console.log('🚀 Polygon Basics - Complete Demo\n') |
| 29 | + console.log('This demo shows all concepts from lessons 2-4:\n') |
| 30 | + |
| 31 | + // ========== LESSON 2: WALLET SETUP & FAUCETS ========== |
| 32 | + console.log('📚 LESSON 2: Wallet Setup & Faucets') |
| 33 | + console.log('─'.repeat(50)) |
| 34 | + |
| 35 | + const provider = new ethers.providers.JsonRpcProvider(RPC_URL) |
| 36 | + |
| 37 | + // Create wallet from password (see lib/wallet.js for alternatives) |
| 38 | + const wallet = createWalletFromPassword(WALLET_PASSWORD).connect(provider) |
| 39 | + console.log('✅ Wallet created from password') |
| 40 | + |
| 41 | + if (WALLET_PASSWORD === 'change_me_to_something_unique_like_pizza_unicorn_2024') { |
| 42 | + console.log('⚠️ Using default password! Change WALLET_PASSWORD to your own unique string.') |
| 43 | + } |
| 44 | + |
| 45 | + console.log('📍 Address:', wallet.address) |
| 46 | + console.log('🔗 View on explorer:', `https://amoy.polygonscan.com/address/${wallet.address}`) |
| 47 | + |
| 48 | + // Check balances |
| 49 | + const polBalance = await provider.getBalance(wallet.address) |
| 50 | + console.log('💰 POL Balance:', ethers.utils.formatEther(polBalance), 'POL') |
| 51 | + |
| 52 | + const usdc = new ethers.Contract(USDC_ADDRESS, USDC_ABI, wallet) |
| 53 | + const usdcBalance = await usdc.balanceOf(wallet.address) |
| 54 | + const decimals = await usdc.decimals() |
| 55 | + console.log('💵 USDC Balance:', ethers.utils.formatUnits(usdcBalance, decimals), 'USDC') |
| 56 | + |
| 57 | + // Convert to 24-word mnemonic so you can import into any wallet |
| 58 | + const mnemonic = ethers.Wallet.fromMnemonic( |
| 59 | + ethers.utils.entropyToMnemonic(ethers.utils.hexZeroPad(wallet.privateKey, 32)) |
| 60 | + ).mnemonic.phrase |
| 61 | + console.log('\n📝 Mnemonic (24 words):', mnemonic) |
| 62 | + console.log('💡 You can import this mnemonic into any wallet to check your balances:') |
| 63 | + console.log(' • Nimiq Testnet Wallet: https://wallet.nimiq-testnet.com/ (supports Amoy USDC)') |
| 64 | + console.log(' • Note: Nimiq Wallet does not support POL, only USDC/USDT') |
| 65 | + console.log(' • Or use MetaMask, Trust Wallet, etc.\n') |
| 66 | + |
| 67 | + if (polBalance.eq(0)) { |
| 68 | + console.log('\n⚠️ No POL found! Get free tokens from:') |
| 69 | + console.log(' POL & USDC: https://faucet.polygon.technology/') |
| 70 | + console.log(' USDC also: https://faucet.circle.com/') |
| 71 | + console.log(' Then run this demo again!\n') |
| 72 | + return |
| 73 | + } |
| 74 | + |
| 75 | + // ========== LESSON 3: SENDING POL ========== |
| 76 | + console.log('\n📚 LESSON 3: Sending POL Transactions') |
| 77 | + console.log('─'.repeat(50)) |
| 78 | + |
| 79 | + const POL_AMOUNT = '0.0001' // Minimal amount for demo |
| 80 | + |
| 81 | + console.log('📤 Sending', POL_AMOUNT, 'POL to', RECIPIENT) |
| 82 | + |
| 83 | + try { |
| 84 | + // Get current gas price and ensure minimum for Polygon |
| 85 | + const feeData = await provider.getFeeData() |
| 86 | + const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas.lt(ethers.utils.parseUnits('30', 'gwei')) |
| 87 | + ? ethers.utils.parseUnits('30', 'gwei') |
| 88 | + : feeData.maxPriorityFeePerGas |
| 89 | + |
| 90 | + // Ensure maxFeePerGas is higher than maxPriorityFeePerGas |
| 91 | + const maxFeePerGas = feeData.maxFeePerGas.lt(maxPriorityFeePerGas) |
| 92 | + ? maxPriorityFeePerGas.mul(2) |
| 93 | + : feeData.maxFeePerGas |
| 94 | + |
| 95 | + const tx = await wallet.sendTransaction({ |
| 96 | + to: RECIPIENT, |
| 97 | + value: ethers.utils.parseEther(POL_AMOUNT), |
| 98 | + maxPriorityFeePerGas, |
| 99 | + maxFeePerGas |
| 100 | + }) |
| 101 | + console.log('⏳ Transaction hash:', tx.hash) |
| 102 | + |
| 103 | + const receipt = await tx.wait() |
| 104 | + console.log('✅ Confirmed in block:', receipt.blockNumber) |
| 105 | + console.log('⛽ Gas used:', receipt.gasUsed.toString()) |
| 106 | + console.log('🔗 View:', `https://amoy.polygonscan.com/tx/${tx.hash}`) |
| 107 | + |
| 108 | + // Show balance change |
| 109 | + const newPolBalance = await provider.getBalance(wallet.address) |
| 110 | + const spent = polBalance.sub(newPolBalance) |
| 111 | + console.log('📊 Total spent:', ethers.utils.formatEther(spent), 'POL (including gas)') |
| 112 | + } catch (error) { |
| 113 | + console.log('❌ Failed:', error.message) |
| 114 | + } |
| 115 | + |
| 116 | + // ========== LESSON 4: ERC20 & USDC ========== |
| 117 | + console.log('\n📚 LESSON 4: ERC20 Tokens & USDC Transfers') |
| 118 | + console.log('─'.repeat(50)) |
| 119 | + |
| 120 | + if (usdcBalance.eq(0)) { |
| 121 | + console.log('⚠️ No USDC found! Get free USDC from:') |
| 122 | + console.log(' https://faucet.polygon.technology/') |
| 123 | + console.log(' https://faucet.circle.com/') |
| 124 | + console.log(' Then try this section again!\n') |
| 125 | + return |
| 126 | + } |
| 127 | + |
| 128 | + const USDC_AMOUNT = '0.01' // Minimal amount for demo |
| 129 | + console.log('📤 Sending', USDC_AMOUNT, 'USDC to', RECIPIENT) |
| 130 | + |
| 131 | + try { |
| 132 | + // Get current gas price and ensure minimum for Polygon |
| 133 | + const feeData = await provider.getFeeData() |
| 134 | + const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas.lt(ethers.utils.parseUnits('30', 'gwei')) |
| 135 | + ? ethers.utils.parseUnits('30', 'gwei') |
| 136 | + : feeData.maxPriorityFeePerGas |
| 137 | + |
| 138 | + // Ensure maxFeePerGas is higher than maxPriorityFeePerGas |
| 139 | + const maxFeePerGas = feeData.maxFeePerGas.lt(maxPriorityFeePerGas) |
| 140 | + ? maxPriorityFeePerGas.mul(2) |
| 141 | + : feeData.maxFeePerGas |
| 142 | + |
| 143 | + const amountInBaseUnits = ethers.utils.parseUnits(USDC_AMOUNT, decimals) |
| 144 | + const tx = await usdc.transfer(RECIPIENT, amountInBaseUnits, { |
| 145 | + maxPriorityFeePerGas, |
| 146 | + maxFeePerGas |
| 147 | + }) |
| 148 | + console.log('⏳ Transaction hash:', tx.hash) |
| 149 | + |
| 150 | + const receipt = await tx.wait() |
| 151 | + console.log('✅ Confirmed in block:', receipt.blockNumber) |
| 152 | + console.log('🔗 View:', `https://amoy.polygonscan.com/tx/${tx.hash}`) |
| 153 | + |
| 154 | + // Show balance change |
| 155 | + const newUsdcBalance = await usdc.balanceOf(wallet.address) |
| 156 | + console.log('📊 New USDC balance:', ethers.utils.formatUnits(newUsdcBalance, decimals), 'USDC') |
| 157 | + |
| 158 | + const recipientBalance = await usdc.balanceOf(RECIPIENT) |
| 159 | + console.log('📊 Recipient USDC:', ethers.utils.formatUnits(recipientBalance, decimals), 'USDC') |
| 160 | + |
| 161 | + // Show POL used for gas (still needed for ERC20!) |
| 162 | + const finalPolBalance = await provider.getBalance(wallet.address) |
| 163 | + console.log('⛽ POL balance:', ethers.utils.formatEther(finalPolBalance), 'POL (gas paid in POL!)') |
| 164 | + } catch (error) { |
| 165 | + console.log('❌ Failed:', error.message) |
| 166 | + } |
| 167 | + |
| 168 | + console.log('\n🎉 Demo complete! Now try lessons 2-4 step by step.') |
| 169 | +} |
| 170 | + |
| 171 | +main().catch(console.error) |
0 commit comments