UNPKG

@graphprotocol/graph-cli

Version:

CLI for building for and deploying to The Graph

279 lines (278 loc) • 12.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.loadAbiFromBlockScout = exports.getStartBlockForContract = exports.fetchTransactionByHashFromRPC = exports.fetchContractCreationHashWithRetry = exports.fetchDeployContractTransactionFromEtherscan = exports.loadStartBlockForContract = exports.loadAbiFromEtherscan = void 0; const immutable_1 = __importDefault(require("immutable")); const constants_1 = require("../constants"); const debug_1 = __importDefault(require("../debug")); const fetch_1 = __importDefault(require("../fetch")); const spinner_1 = require("./spinner"); const logger = (0, debug_1.default)('graph-cli:abi-helpers'); const loadAbiFromEtherscan = async (ABICtor, network, address) => await (0, spinner_1.withSpinner)(`Fetching ABI from Etherscan`, `Failed to fetch ABI from Etherscan`, `Warnings while fetching ABI from Etherscan`, async () => { const scanApiUrl = getEtherscanLikeAPIUrl(network); const result = await (0, fetch_1.default)(`${scanApiUrl}?module=contract&action=getabi&address=${address}`); const json = await result.json(); // Etherscan returns a JSON object that has a `status`, a `message` and // a `result` field. The `status` is '0' in case of errors and '1' in // case of success if (json.status === '1') { return new ABICtor('Contract', undefined, immutable_1.default.fromJS(JSON.parse(json.result))); } throw new Error('ABI not found, try loading it from a local file'); }); exports.loadAbiFromEtherscan = loadAbiFromEtherscan; const loadStartBlockForContract = async (network, address) => await (0, spinner_1.withSpinner)(`Fetching Start Block`, `Failed to fetch Start Block`, `Warnings while fetching deploy contract transaction from Etherscan`, async () => { return (0, exports.getStartBlockForContract)(network, address); }); exports.loadStartBlockForContract = loadStartBlockForContract; const fetchDeployContractTransactionFromEtherscan = async (network, address) => { const scanApiUrl = getEtherscanLikeAPIUrl(network); const json = await (0, exports.fetchContractCreationHashWithRetry)(`${scanApiUrl}?module=contract&action=getcontractcreation&contractaddresses=${address}`, 5); if (json.status === '1') { const hash = json.result[0].txHash; logger('Successfully fetchDeployContractTransactionFromEtherscan. txHash: %s', hash); return hash; } throw new Error(`Failed to fetch deploy contract transaction`); }; exports.fetchDeployContractTransactionFromEtherscan = fetchDeployContractTransactionFromEtherscan; const fetchContractCreationHashWithRetry = async (url, retryCount) => { let json; for (let i = 0; i < retryCount; i++) { try { const result = await (0, fetch_1.default)(url); json = await result.json(); if (json.status !== '0') { return json; } } catch (error) { logger('Failed to fetchContractCreationHashWithRetry: %O', error); /* empty */ } } throw new Error(`Failed to fetch contract creation transaction hash`); }; exports.fetchContractCreationHashWithRetry = fetchContractCreationHashWithRetry; const fetchTransactionByHashFromRPC = async (network, transactionHash) => { let json; try { const RPCURL = getPublicRPCEndpoint(network); if (!RPCURL) throw new Error(`Unable to fetch RPC URL for ${network}`); const result = await (0, fetch_1.default)(String(RPCURL), { method: 'POST', headers: { 'Content-Type': 'application/json', ...constants_1.GRAPH_CLI_SHARED_HEADERS, }, body: JSON.stringify({ jsonrpc: '2.0', method: 'eth_getTransactionByHash', params: [transactionHash], id: 1, }), }); json = await result.json(); return json; } catch (error) { logger('Failed to fetchTransactionByHashFromRPC: %O', error); throw new Error('Failed to fetch contract creation transaction'); } }; exports.fetchTransactionByHashFromRPC = fetchTransactionByHashFromRPC; const getStartBlockForContract = async (network, address) => { try { const transactionHash = await (0, exports.fetchDeployContractTransactionFromEtherscan)(network, address); const txn = await (0, exports.fetchTransactionByHashFromRPC)(network, transactionHash); const blockNumber = parseInt(txn.result.blockNumber, 16); logger('Successfully getStartBlockForContract. blockNumber: %s', blockNumber); return blockNumber; } catch (error) { logger('Failed to fetch getStartBlockForContract: %O', error); throw new Error(error?.message); } }; exports.getStartBlockForContract = getStartBlockForContract; const loadAbiFromBlockScout = async (ABICtor, network, address) => await (0, spinner_1.withSpinner)(`Fetching ABI from BlockScout`, `Failed to fetch ABI from BlockScout`, `Warnings while fetching ABI from BlockScout`, async () => { const result = await (0, fetch_1.default)(`https://blockscout.com/${network.replace('-', '/')}/api?module=contract&action=getabi&address=${address}`); const json = await result.json(); // BlockScout returns a JSON object that has a `status`, a `message` and // a `result` field. The `status` is '0' in case of errors and '1' in // case of success if (json.status === '1') { logger('Successfully loadAbiFromBlockScout. address: %s', address); return new ABICtor('Contract', undefined, immutable_1.default.fromJS(JSON.parse(json.result))); } logger('Failed to loadAbiFromBlockScout. address: %s', address); throw new Error('ABI not found, try loading it from a local file'); }); exports.loadAbiFromBlockScout = loadAbiFromBlockScout; const getEtherscanLikeAPIUrl = (network) => { switch (network) { case 'mainnet': return `https://api.etherscan.io/api`; case 'arbitrum-one': return `https://api.arbiscan.io/api`; case 'arbitrum-goerli': return `https://api-goerli.arbiscan.io/api`; case 'arbitrum-sepolia': return `https://api-sepolia.arbiscan.io/api`; case 'bsc': return `https://api.bscscan.com/api`; case 'base-testnet': return `https://api-goerli.basescan.org/api`; case 'base': return `https://api.basescan.org/api`; case 'chapel': return `https://api-testnet.bscscan.com/api`; case 'matic': return `https://api.polygonscan.com/api`; case 'mumbai': return `https://api-testnet.polygonscan.com/api`; case 'aurora': return `https://explorer.mainnet.aurora.dev/api`; case 'aurora-testnet': return `https://explorer.testnet.aurora.dev/api`; case 'optimism-goerli': return `https://api-goerli-optimistic.etherscan.io/api`; case 'optimism': return `https://api-optimistic.etherscan.io/api`; case 'moonbeam': return `https://api-moonbeam.moonscan.io/api`; case 'moonriver': return `https://api-moonriver.moonscan.io/api`; case 'mbase': return `https://api-moonbase.moonscan.io/api`; case 'avalanche': return `https://api.snowtrace.io/api`; case 'fuji': return `https://api-testnet.snowtrace.io/api`; case 'celo': return `https://api.celoscan.io/api`; case 'celo-alfajores': return `https://alfajores.celoscan.io/api`; case 'gnosis': return `https://api.gnosisscan.io/api`; case 'fantom': return `https://api.ftmscan.com/api`; case 'fantom-testnet': return `https://api-testnet.ftmscan.com/api`; case 'zksync-era': return `https://block-explorer-api.mainnet.zksync.io/api`; case 'zksync-era-testnet': return `https://block-explorer-api.testnets.zksync.dev/api`; case 'zksync-era-sepolia': return 'https://block-explorer-api.sepolia.zksync.dev/api'; case 'polygon-zkevm-testnet': return `https://testnet-zkevm.polygonscan.com/api`; case 'polygon-zkevm': return `https://zkevm.polygonscan.com/api`; case 'sepolia': return `https://api-sepolia.etherscan.io/api`; case 'scroll-sepolia': return `https://api-sepolia.scrollscan.dev/api`; case 'scroll': return `https://blockscout.scroll.io/api`; case 'linea': return `https://api.lineascan.build/api`; case 'linea-goerli': return `https://api.linea-goerli.build/api`; case 'blast-testnet': return `https://api.routescan.io/v2/network/testnet/evm/168587773/etherscan/api`; default: return `https://api-${network}.etherscan.io/api`; } }; const getPublicRPCEndpoint = (network) => { switch (network) { case 'arbitrum-goerli': return 'https://goerli-rollup.arbitrum.io/rpc'; case 'arbitrum-one': return 'https://arb1.arbitrum.io/rpc'; case 'arbitrum-sepolia': return `https://sepolia-rollup.arbitrum.io/rpc`; case 'aurora': return 'https://rpc.mainnet.aurora.dev'; case 'aurora-testnet': return 'https://rpc.testnet.aurora.dev'; case 'avalanche': return 'https://api.avax.network/ext/bc/C/rpc'; case 'base-testnet': return 'https://goerli.base.org'; case 'base': return 'https://rpc.base.org'; case 'bsc': return 'https://bsc-dataseed.binance.org'; case 'celo': return 'https://forno.celo.org'; case 'celo-alfajores': return 'https://alfajores-forno.celo-testnet.org'; case 'chapel': return 'https://rpc.chapel.dev'; case 'clover': return 'https://rpc.clover.finance'; case 'fantom': return 'https://rpcapi.fantom.network'; case 'fantom-testnet': return 'https://rpc.testnet.fantom.network'; case 'fuji': return 'https://api.avax-test.network/ext/bc/C/rpc'; case 'fuse': return 'https://rpc.fuse.io'; case 'goerli': return 'https://rpc.ankr.com/eth_goerli'; case 'gnosis': return 'https://safe-transaction.gnosis.io'; case 'mainnet': return 'https://rpc.ankr.com/eth'; case 'matic': return 'https://rpc-mainnet.maticvigil.com'; case 'mbase': return 'https://rpc.moonbase.moonbeam.network'; case 'mumbai': return 'https://rpc-mumbai.maticvigil.com'; case 'moonbeam': return 'https://rpc.api.moonbeam.network'; case 'moonriver': return 'https://moonriver.public.blastapi.io'; case 'optimism': return 'https://mainnet.optimism.io'; case 'optimism-goerli': return 'https://goerli.optimism.io'; case 'poa-core': return 'https://core.poa.network'; case 'poa-sokol': return 'https://sokol.poa.network'; case 'polygon-zkevm-testnet': return 'https://rpc.public.zkevm-test.net'; case 'polygon-zkevm': return 'https://zkevm-rpc.com'; case 'rinkeby': return 'https://rpc.ankr.com/eth_rinkeby'; case 'zksync-era': return 'https://mainnet.era.zksync.io'; case 'zksync-era-testnet': return 'https://testnet.era.zksync.dev'; case 'zksync-era-sepolia': return 'https://sepolia.era.zksync.dev'; case 'sepolia': return 'https://rpc.ankr.com/eth_sepolia'; case 'scroll-sepolia': return 'https://rpc.ankr.com/scroll_sepolia_testnet'; case 'scroll': return 'https://rpc.ankr.com/scroll'; case 'linea': return 'https://linea-mainnet.public.blastapi.io'; case 'linea-goerli': return 'https://linea-goerli.public.blastapi.io'; case 'blast-testnet': return 'https://sepolia.blast.io'; default: throw new Error(`Unknown network: ${network}`); } };