UNPKG

@sentio/truffle-source-fetcher

Version:

Fetches verified source code from services such as Etherscan

277 lines 11.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var _a; Object.defineProperty(exports, "__esModule", { value: true }); const debug_1 = __importDefault(require("debug")); const debug = (0, debug_1.default)("source-fetcher:sourcify"); const common_1 = require("./common"); const networks_1 = require("./networks"); const async_retry_1 = __importDefault(require("async-retry")); // must polyfill AbortController to use axios >=0.20.0, <=0.27.2 on node <= v14.x require("./polyfill"); const axios_1 = __importDefault(require("axios")); //this looks awkward but the TS docs actually suggest this :P const SourcifyFetcher = (_a = class SourcifyFetcher { static get fetcherName() { return "sourcify"; } get fetcherName() { return SourcifyFetcher.fetcherName; } static forNetworkId(id, _options) { return __awaiter(this, void 0, void 0, function* () { //in the future, we may add protocol and node options, //but these don't exist yet return new SourcifyFetcher(id); }); } constructor(networkId) { //but may be in the future this.domain = "repo.sourcify.dev"; this.networkId = networkId; this.networkName = networks_1.networkNamesById[networkId]; if (this.networkName === undefined || !SourcifyFetcher.supportedNetworks.has(this.networkName)) { throw new common_1.InvalidNetworkError(networkId, "sourcify"); } } static getSupportedNetworks() { return Object.fromEntries(Object.entries(networks_1.networksByName).filter(([name, _]) => SourcifyFetcher.supportedNetworks.has(name))); } fetchSourcesForAddress(address) { return __awaiter(this, void 0, void 0, function* () { let result = yield this.fetchSourcesForAddressAndMatchType(address, "full"); if (!result) { //if we got nothing when trying a full match, try for a partial match result = yield this.fetchSourcesForAddressAndMatchType(address, "partial"); } //if partial match also fails, just return null return result; }); } fetchSourcesForAddressAndMatchType(address, matchType) { return __awaiter(this, void 0, void 0, function* () { const metadata = yield this.getMetadata(address, matchType); debug("metadata: %O", metadata); if (!metadata) { debug("no metadata"); return null; } let sources; sources = Object.assign({}, ...(yield Promise.all(Object.entries(metadata.sources).map(([sourcePath, { content: source }]) => __awaiter(this, void 0, void 0, function* () { return ({ [sourcePath]: source !== undefined ? source //sourcify doesn't support this yet but they're planning it : yield this.getSource(address, sourcePath, matchType) }); }))))); const constructorArguments = yield this.getConstructorArgs(address, matchType); debug("compilationTarget: %O", metadata.settings.compilationTarget); return { contractName: Object.values(metadata.settings.compilationTarget)[0], sources, options: { language: metadata.language, version: metadata.compiler.version, //we also pass the flag to remove compilationTarget, as its //presence can cause compile errors settings: (0, common_1.removeLibraries)(metadata.settings, true), specializations: { constructorArguments, libraries: metadata.settings.libraries } } }; }); } getMetadata(address, matchType) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.requestWithRetries({ url: `https://${this.domain}/contracts/${matchType}_match/${this.networkId}/${address}/metadata.json`, method: "get", responseType: "json", maxRedirects: 50 }); } catch (error) { //is this a 404 error? if so just return null debug("error: %O", error); if (error.response && error.response.status === 404) { return null; } //otherwise, we've got a problem; rethrow the error throw error; } }); } getSource(address, sourcePath, matchType) { return __awaiter(this, void 0, void 0, function* () { //note: sourcify replaces special characters in paths with underscores //(special characters here being anything other than ASCII alphanumerics, //hyphens, periods, and forward slashes) const transformedSourcePath = sourcePath.replace(/[^\w.\/-]/gu, "_"); return yield this.requestWithRetries({ url: `https://${this.domain}/contracts/${matchType}_match/${this.networkId}/${address}/sources/${transformedSourcePath}`, responseType: "text", method: "get", maxRedirects: 50 }); }); } getConstructorArgs(address, matchType) { return __awaiter(this, void 0, void 0, function* () { try { const constructorArgs = yield this.requestWithRetries({ url: `https://${this.domain}/contracts/${matchType}_match/${this.networkId}/${address}/constructor-args.txt`, method: "get", responseType: "text", maxRedirects: 50 }); return constructorArgs.slice(2); //remove initial "0x" } catch (error) { //is this a 404 error? if so just return undefined debug("error: %O", error); if (error.response && error.response.status === 404) { return undefined; } //otherwise, we've got a problem; rethrow the error throw error; } }); } requestWithRetries(requestObject //sorry, trying to import the type properly ran into problems ) { return __awaiter(this, void 0, void 0, function* () { return yield (0, async_retry_1.default)((bail) => __awaiter(this, void 0, void 0, function* () { try { //note: we use axios.request rather than just axios so we can stub it in tests! return (yield axios_1.default.request(requestObject)).data; } catch (error) { //check: is this a 404 error? if so give up if (error.response && error.response.status === 404) { bail(error); //don't retry } else { throw error; //retry } } }), { retries: 3 } //leaving minTimeout as default 1000 ); }); } }, _a.supportedNetworks = new Set([ "mainnet", "ropsten", "kovan", "rinkeby", "goerli", "kovan", "sepolia", "optimistic", "kovan-optimistic", "goerli-optimistic", "goerli-bedrock-optimistic", "arbitrum", "rinkeby-arbitrum", "goerli-arbitrum", "polygon", "mumbai-polygon", "gnosis", "chiado-gnosis", "optimism-gnosis", "core-poa", "sokol-poa", "binance", "testnet-binance", "celo", "alfajores-celo", "baklava-celo", "avalanche", "fuji-avalanche", "wagmi-avalanche", "dfk-avalanche", "testnet-dfk-avalanche", "dexalot-avalanche", "testnet-dexalot-avalanche", "telos", "testnet-telos", "ubiq", "oneledger", "frankenstein-oneledger", "syscoin", "tanenbaum-syscoin", "boba", "rinkeby-boba", "velas", "meter", "testnet-meter", "aurora", "testnet-aurora", "fuse", "moonbeam", "moonriver", "moonbase-alpha", "palm", "testnet-palm", "crab-darwinia", "pangolin-darwinia", "evmos", "testnet-evmos", "multivac", "candle", "gather", "devnet-gather", "testnet-gather", "energyweb", "volta-energyweb", "godwoken", "testnet-godwoken", //sourcify does *not* support xinfin mainnet...? "apothem-xinfin", "canto", "testnet-canto", "astar", "shiden-astar", "cypress-klaytn", "baobab-klaytn", //sourcify does *not* support zetachain mainnet? "athens-zetachain", "emerald-oasis", "testnet-emerald-oasis", "sapphire-oasis", "testnet-sapphire-oasis", "flare", "songbird-flare", //sourcify does *not* support stratos mainnet? "testnet-stratos", //sourcify does *not* support base mainnet? "goerli-base", "bear", "wanchain", "testnet-wanchain", "root", "porcini-root", "hedera", "symplexia", "dogechain" //I'm excluding crystaleum as it has network ID different from chain ID //excluding kekchain for the same reason ]), _a); exports.default = SourcifyFetcher; //# sourceMappingURL=sourcify.js.map