#!/usr/bin/env node /** * Protocolink path for deployer gas auto-route: get quote and build tx data for * swapping ERC-20 (USDT, USDC, LINK) to native gas (or WETH) on Protocolink-supported chains. * * Usage: * node scripts/deployment/protocolink-swap-to-gas.cjs --chain-id 1 [--token USDC] [--amount-raw 1000000] [--no-execute] * PROTOCOLINK_API_URL=https://api.protocolink.com node scripts/deployment/protocolink-swap-to-gas.cjs --chain-id 137 * * Requires: Node 18+ (fetch), config/deployer-gas-routes.json for chain list. * Output: JSON with quote and tx building instructions; no private key or signing in script. */ const fs = require('fs'); const path = require('path'); const PROJECT_ROOT = path.resolve(__dirname, '../..'); const CONFIG_PATH = path.join(PROJECT_ROOT, 'config/deployer-gas-routes.json'); const API_BASE = process.env.PROTOCOLINK_API_URL || 'https://api.protocolink.com'; const PROTOCOL_ID = process.env.PROTOCOLINK_PROTOCOL || 'paraswap-v5'; const LOGIC_ID = 'swap-token'; const SLIPPAGE_BPS = parseInt(process.env.SLIPPAGE_BPS || '150', 10); // Common token addresses per chain (mainnet; extend as needed) const NATIVE_OR_WETH = { 1: { address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', decimals: 18, symbol: 'WETH' }, 56: { address: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', decimals: 18, symbol: 'WBNB' }, 137: { address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', decimals: 18, symbol: 'WMATIC' }, 100: { address: '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', decimals: 18, symbol: 'WXDAI' }, 10: { address: '0x4200000000000000000000000000000000000006', decimals: 18, symbol: 'WETH' }, 42161: { address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBb1', decimals: 18, symbol: 'WETH' }, 8453: { address: '0x4200000000000000000000000000000000000006', decimals: 18, symbol: 'WETH' }, 43114: { address: '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7', decimals: 18, symbol: 'WAVAX' }, 25: { address: '0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23', decimals: 18, symbol: 'WCRO' }, }; const USDT = { 1: '0xdAC17F958D2ee523a2206206994597C13D831ec7', 56: '0x55d398326f99059fF775485246999027B3197955', 137: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', 100: '0x4ECaBa5870353805a9F068101A40E0f32ed605C6', 10: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', 42161: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', 8453: '0x0000000000000000000000000000000000000000', // TBD 43114: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', 25: '0x66e428c3f67a68878562e79A0234c1F83c208770', }; const USDC = { 1: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 56: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', 137: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', 100: '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83', 10: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', 42161: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', 8453: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', 43114: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', 25: '0xc21223249CA28397B4B6541dfFaEcC539BfF0c59', }; const LINK = { 1: '0x514910771AF9Ca656af840dff83E8264EcF986CA', 56: '0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD', 137: '0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39', 100: '0xE2e73A1c69ecF83F464CECE147577d37BCf26298', 10: '0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6', 42161: '0xf97f4df75117a78c1A5a0DBb814Af92458539FB4', 8453: '0x0000000000000000000000000000000000000000', // TBD 43114: '0x5947BB275c521040051D82396192181b413227A3', 25: '0xE7d7D8374CF6a8e3F656c433F6B0f0C81F6d06F5', }; function getTokenIn(chainId, symbol) { const s = (symbol || 'USDC').toUpperCase(); let addr; if (s === 'USDT') addr = USDT[chainId]; else if (s === 'USDC') addr = USDC[chainId]; else if (s === 'LINK') addr = LINK[chainId]; else return null; if (!addr || addr === '0x0000000000000000000000000000000000000000') return null; const decimals = s === 'LINK' ? 18 : 6; return { chainId, address: addr, decimals, symbol: s, name: s }; } function getTokenOut(chainId) { const t = NATIVE_OR_WETH[chainId]; if (!t) return null; return { chainId, address: t.address, decimals: t.decimals, symbol: t.symbol, name: t.symbol }; } function parseArgs() { const args = process.argv.slice(2); let chainId, tokenIn = 'USDC', amountRaw, noExecute = false; for (let i = 0; i < args.length; i++) { if (args[i] === '--chain-id' && args[i + 1]) chainId = parseInt(args[i + 1], 10); if (args[i] === '--token' && args[i + 1]) tokenIn = args[i + 1]; if (args[i] === '--amount-raw' && args[i + 1]) amountRaw = args[i + 1]; if (args[i] === '--no-execute') noExecute = true; } return { chainId, tokenIn, amountRaw: amountRaw || '1000000', noExecute }; } async function requestQuote(chainId, tokenInObj, tokenOutObj, amountRaw) { const url = `${API_BASE}/v1/protocols/${chainId}/${PROTOCOL_ID}/${LOGIC_ID}/quote`; const body = { input: { token: tokenInObj, amount: amountRaw, }, tokenOut: tokenOutObj, slippage: SLIPPAGE_BPS, }; const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); if (!res.ok) { const text = await res.text(); throw new Error(`Protocolink quote failed ${res.status}: ${text}`); } return res.json(); } function loadConfig() { if (!fs.existsSync(CONFIG_PATH)) return null; return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); } async function main() { const { chainId, tokenIn: tokenSymbol, amountRaw, noExecute } = parseArgs(); if (!chainId) { console.error('Usage: node protocolink-swap-to-gas.cjs --chain-id [--token USDC|USDT|LINK] [--amount-raw ] [--no-execute]'); process.exit(1); } const tokenInObj = getTokenIn(chainId, tokenSymbol); const tokenOutObj = getTokenOut(chainId); if (!tokenInObj || !tokenOutObj) { console.error(JSON.stringify({ error: 'Unsupported chain or token', chainId, tokenIn: tokenSymbol, message: 'Add token addresses for this chain in protocolink-swap-to-gas.cjs or use a supported chain (1, 56, 137, 100, 10, 42161, 8453, 43114, 25).', }, null, 2)); process.exit(1); } try { const quote = await requestQuote(chainId, tokenInObj, tokenOutObj, amountRaw); const output = { chainId, protocolId: PROTOCOL_ID, logicId: LOGIC_ID, quote, tokenIn: tokenInObj, tokenOut: tokenOutObj, amountInRaw: amountRaw, slippageBps: SLIPPAGE_BPS, nextStep: 'Use Protocolink API "build transaction" or SDK to build tx; sign with deployer key and submit. See https://docs.protocolink.com/protocolink-api/overview.', noExecute: true, }; console.log(JSON.stringify(output, null, 2)); } catch (err) { console.error(JSON.stringify({ error: err.message, chainId, tokenIn: tokenSymbol, }, null, 2)); process.exit(1); } } main();