UNPKG

truffle

Version:

Truffle - Simple development framework for Ethereum

1,098 lines (1,086 loc) 252 kB
#!/usr/bin/env node exports.id = 8852; exports.ids = [8852]; exports.modules = { /***/ 8135: /***/ (function(__unused_webpack_module, exports, __webpack_require__) { "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.parseCode = void 0; const opcodes_1 = __webpack_require__(89537); const cbor = __importStar(__webpack_require__(82141)); /** * parseCode - return a list of instructions given a 0x-prefixed code string. * * The optional second options argument allows two options; both are ways of * attempting to limit the disassembly to only the code section rather than the * data section. If maxInstructionCount is used, the disassembly will be limited * to the specified number of instructions (one may pass in here the number of * instructions in the corresponding source map). * * If attemptStripMetadata is used, we will attempt to strip the metadata at the * end of the code. This is not reliable, and should be avoided if better * alternatives are available. It may be particularly unreliable when dealing with * constructors that have had arguments attached to the end! * * These options can be combined, although I'm not sure why you'd want to. * * @param {String} hexString Hex string representing the code * @return Array Array of instructions */ function parseCode(hexString, { maxInstructionCount, attemptStripMetadata } = {}) { // Convert to an array of bytes let code = new Uint8Array((hexString.slice(2).match(/(..?)/g) || []).map(hex => parseInt(hex, 16))); if (attemptStripMetadata && code.length >= 2) { // Remove the contract metadata; last two bytes encode its length (not // including those two bytes) let foundMetadata = false; const metadataLength = (code[code.length - 2] << 8) + code[code.length - 1]; //check: is this actually valid CBOR? if (metadataLength + 2 <= code.length) { const metadata = code.subarray(-(metadataLength + 2), -2); if (isValidCBOR(metadata)) { code = code.subarray(0, -(metadataLength + 2)); foundMetadata = true; } } if (!foundMetadata) { const vyper034MetadataLength = 11; //vyper 0.3.4 (that version specifically; //this will be corrected in 0.3.5, and earlier vyper versions do not include //metadata) has metadata on the end but with no length information supplied //afterward; instead it has a fixed length of 11 if (vyper034MetadataLength <= code.length) { const metadata = code.subarray(-vyper034MetadataLength); if (isValidCBOR(metadata)) { code = code.subarray(0, -vyper034MetadataLength); } } } } let instructions = []; if (maxInstructionCount === undefined) { //if maxInstructionCount wasn't passed, we'll set it to //Infinity so that we don't limit the number of instructions maxInstructionCount = Infinity; } for (let pc = 0; pc < code.length && instructions.length < maxInstructionCount; pc++) { let opcode = { pc, name: (0, opcodes_1.parseOpcode)(code[pc]) }; if (opcode.name.slice(0, 4) === "PUSH") { const length = code[pc] - 0x5f; //0x5f is code for PUSH0 let pushData = code.subarray(pc + 1, pc + length + 1); if (pushData.length < length) { //if we run out of bytes for our pushdata, fill the rest //with zeroes pushData = Uint8Array.from([ ...pushData, ...new Uint8Array(length - pushData.length) ]); } // convert pushData to hex opcode.pushData = `0x${Buffer.from(pushData).toString("hex")}`; pc += length; } instructions.push(opcode); } return instructions; } exports.parseCode = parseCode; function isValidCBOR(metadata) { try { //attempt to decode but discard the value //note this *will* throw if there's data left over, //which is what we want it to do //HACK: this version of cbor doesn't accept Uint8Arrays, //but it does accept Buffers. (Unfortunately newer versions //cause problems. :-/ ) cbor.decodeFirstSync(Buffer.from(metadata)); } catch (_a) { return false; } return true; } //# sourceMappingURL=index.js.map /***/ }), /***/ 89537: /***/ ((__unused_webpack_module, exports) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.parseOpcode = void 0; const codes = { 0x00: "STOP", 0x01: "ADD", 0x02: "MUL", 0x03: "SUB", 0x04: "DIV", 0x05: "SDIV", 0x06: "MOD", 0x07: "SMOD", 0x08: "ADDMOD", 0x09: "MULMOD", 0x0a: "EXP", 0x0b: "SIGNEXTEND", // 0x10 range - bit ops 0x10: "LT", 0x11: "GT", 0x12: "SLT", 0x13: "SGT", 0x14: "EQ", 0x15: "ISZERO", 0x16: "AND", 0x17: "OR", 0x18: "XOR", 0x19: "NOT", 0x1a: "BYTE", 0x1b: "SHL", 0x1c: "SHR", 0x1d: "SAR", // 0x20 range - crypto 0x20: "SHA3", // 0x30 range - closure state 0x30: "ADDRESS", 0x31: "BALANCE", 0x32: "ORIGIN", 0x33: "CALLER", 0x34: "CALLVALUE", 0x35: "CALLDATALOAD", 0x36: "CALLDATASIZE", 0x37: "CALLDATACOPY", 0x38: "CODESIZE", 0x39: "CODECOPY", 0x3a: "GASPRICE", 0x3b: "EXTCODESIZE", 0x3c: "EXTCODECOPY", 0x3d: "RETURNDATASIZE", 0x3e: "RETURNDATACOPY", 0x3f: "EXTCODEHASH", // 0x40 range - block operations 0x40: "BLOCKHASH", 0x41: "COINBASE", 0x42: "TIMESTAMP", 0x43: "NUMBER", 0x44: "PREVRANDAO|DIFFICULTY", 0x45: "GASLIMIT", 0x46: "CHAINID", 0x47: "SELFBALANCE", 0x48: "BASEFEE", // 0x50 range - 'storage' and execution 0x50: "POP", 0x51: "MLOAD", 0x52: "MSTORE", 0x53: "MSTORE8", 0x54: "SLOAD", 0x55: "SSTORE", 0x56: "JUMP", 0x57: "JUMPI", 0x58: "PC", 0x59: "MSIZE", 0x5a: "GAS", 0x5b: "JUMPDEST", // 0x60 & 0x70 range - pushes 0x5f: "PUSH0", 0x60: "PUSH1", 0x61: "PUSH2", 0x62: "PUSH3", 0x63: "PUSH4", 0x64: "PUSH5", 0x65: "PUSH6", 0x66: "PUSH7", 0x67: "PUSH8", 0x68: "PUSH9", 0x69: "PUSH10", 0x6a: "PUSH11", 0x6b: "PUSH12", 0x6c: "PUSH13", 0x6d: "PUSH14", 0x6e: "PUSH15", 0x6f: "PUSH16", 0x70: "PUSH17", 0x71: "PUSH18", 0x72: "PUSH19", 0x73: "PUSH20", 0x74: "PUSH21", 0x75: "PUSH22", 0x76: "PUSH23", 0x77: "PUSH24", 0x78: "PUSH25", 0x79: "PUSH26", 0x7a: "PUSH27", 0x7b: "PUSH28", 0x7c: "PUSH29", 0x7d: "PUSH30", 0x7e: "PUSH31", 0x7f: "PUSH32", // 0x80 range - duplication 0x80: "DUP1", 0x81: "DUP2", 0x82: "DUP3", 0x83: "DUP4", 0x84: "DUP5", 0x85: "DUP6", 0x86: "DUP7", 0x87: "DUP8", 0x88: "DUP9", 0x89: "DUP10", 0x8a: "DUP11", 0x8b: "DUP12", 0x8c: "DUP13", 0x8d: "DUP14", 0x8e: "DUP15", 0x8f: "DUP16", // 0x90 range - swaps 0x90: "SWAP1", 0x91: "SWAP2", 0x92: "SWAP3", 0x93: "SWAP4", 0x94: "SWAP5", 0x95: "SWAP6", 0x96: "SWAP7", 0x97: "SWAP8", 0x98: "SWAP9", 0x99: "SWAP10", 0x9a: "SWAP11", 0x9b: "SWAP12", 0x9c: "SWAP13", 0x9d: "SWAP14", 0x9e: "SWAP15", 0x9f: "SWAP16", // '0xa0' range - logging 0xa0: "LOG0", 0xa1: "LOG1", 0xa2: "LOG2", 0xa3: "LOG3", 0xa4: "LOG4", // '0xf0' range - closures 0xf0: "CREATE", 0xf1: "CALL", 0xf2: "CALLCODE", 0xf3: "RETURN", 0xf4: "DELEGATECALL", 0xf5: "CREATE2", 0xfa: "STATICCALL", 0xfd: "REVERT", 0xfe: "INVALID", 0xff: "SELFDESTRUCT" }; const parseOpcode = (op) => op in codes ? codes[op] : "INVALID"; exports.parseOpcode = parseOpcode; //# sourceMappingURL=opcodes.js.map /***/ }), /***/ 95532: /***/ (function(__unused_webpack_module, exports, __webpack_require__) { "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; 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 }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.ContractInstanceDecoder = exports.ContractDecoder = exports.ProjectDecoder = void 0; const debug_1 = __importDefault(__webpack_require__(15158)); const debug = (0, debug_1.default)("decoder:decoders"); const Abi = __importStar(__webpack_require__(7651)); const Codec = __importStar(__webpack_require__(20102)); const codec_1 = __webpack_require__(20102); const Encoder = __importStar(__webpack_require__(15967)); const encoder_1 = __webpack_require__(15967); const web3_utils_1 = __importDefault(__webpack_require__(18269)); const bn_js_1 = __importDefault(__webpack_require__(13550)); const errors_1 = __webpack_require__(13552); const fetch_signatures_1 = __webpack_require__(72794); const compile_common_1 = __webpack_require__(29833); //sorry for untyped imports! const SourceMapUtils = __webpack_require__(32731); const { default: ENS, getEnsAddress } = __webpack_require__(26143); const defaultSelectorDirectory = "https://www.4byte.directory/api"; /** * The ProjectDecoder class. Decodes transactions and logs. See below for a method listing. * @category Decoder */ class ProjectDecoder { /** * @protected */ constructor(compilations, provider, ensSettings, selectorDirectory) { var _a; this.contexts = {}; //all contexts this.deployedContexts = {}; this.contractsAndContexts = []; this.codeCache = {}; this.ensCache = {}; this.addProjectInfoNonce = 0; if (!provider) { throw new errors_1.NoProviderError(); } //check for repeat compilation IDs const repeatIds = Codec.Compilations.Utils.findRepeatCompilationIds(compilations); if (repeatIds.size !== 0) { throw new Codec.RepeatCompilationIdError([...repeatIds]); } this.providerAdapter = new encoder_1.ProviderAdapter(provider); this.compilations = compilations; this.ensSettings = { provider: (ensSettings === null || ensSettings === void 0 ? void 0 : ensSettings.provider) !== undefined ? ensSettings === null || ensSettings === void 0 ? void 0 : ensSettings.provider : provider, registryAddress: ensSettings === null || ensSettings === void 0 ? void 0 : ensSettings.registryAddress }; //we don't use an object spread because we want undefined to be ignored this.selectorDirectory = (selectorDirectory === null || selectorDirectory === void 0 ? void 0 : selectorDirectory.enabled) ? (_a = selectorDirectory.url) !== null && _a !== void 0 ? _a : defaultSelectorDirectory : null; let allocationInfo; ({ definitions: this.referenceDeclarations, typesByCompilation: this.userDefinedTypesByCompilation, types: this.userDefinedTypes } = codec_1.Compilations.Utils.collectUserDefinedTypesAndTaggedOutputs(this.compilations)); ({ contexts: this.contexts, deployedContexts: this.deployedContexts, contractsAndContexts: this.contractsAndContexts, allocationInfo } = codec_1.AbiData.Allocate.Utils.collectAllocationInfo(this.compilations)); this.allocations = {}; this.allocations.abi = codec_1.AbiData.Allocate.getAbiAllocations(this.userDefinedTypes); this.allocations.storage = codec_1.Storage.Allocate.getStorageAllocations(this.userDefinedTypesByCompilation); //not used by project decoder itself, but used by contract decoder this.allocations.calldata = codec_1.AbiData.Allocate.getCalldataAllocations(allocationInfo, this.referenceDeclarations, this.userDefinedTypes, this.allocations.abi); this.allocations.returndata = codec_1.AbiData.Allocate.getReturndataAllocations(allocationInfo, this.referenceDeclarations, this.userDefinedTypes, this.allocations.abi); this.allocations.event = codec_1.AbiData.Allocate.getEventAllocations(allocationInfo, this.referenceDeclarations, this.userDefinedTypes, this.allocations.abi); this.allocations.state = codec_1.Storage.Allocate.getStateAllocations(allocationInfo, this.referenceDeclarations, this.userDefinedTypes, this.allocations.storage); debug("done with allocation"); } /** * @protected * WARNING: this code is copypasted (w/slight modifications) from encoder!! */ init() { return __awaiter(this, void 0, void 0, function* () { debug("initting!"); const { provider, registryAddress } = this.ensSettings; if (provider) { debug("provider given!"); if (registryAddress !== undefined) { this.ens = new ENS({ provider, ensAddress: registryAddress }); } else { //if we weren't given a registry address, we use the default one, //but what is that? We have to look it up. //NOTE: ENS is supposed to do this for us in the constructor, //but due to a bug it doesn't. debug("using default registry address"); const networkId = yield new encoder_1.ProviderAdapter(provider).getNetworkId(); const registryAddress = getEnsAddress(networkId); if (registryAddress) { this.ens = new ENS({ provider: provider, ensAddress: registryAddress }); } else { //there is no default registry on this chain this.ens = null; } } } else { debug("no provider given, ens off"); this.ens = null; } }); } /** * **This function is asynchronous.** * * Adds compilations to the decoder after it has started. Note it is * only presently possible to do this with a `ProjectDecoder` and not * with the other decoder classes. * * @param compilations The compilations to be added. Take care that these * have IDs distinct from those the decoder already has. */ addCompilations(compilations) { return __awaiter(this, void 0, void 0, function* () { //first: make sure we're not adding a compilation with an existing ID const existingIds = new Set(this.compilations.map(compilation => compilation.id)); const newIds = new Set(compilations.map(compilation => compilation.id)); //we use a find() rather than a some() so that we can put the ID in the error const overlappingIds = [...newIds].filter(id => existingIds.has(id)); if (overlappingIds.length !== 0) { throw new Codec.RepeatCompilationIdError(overlappingIds); } //also: check for repeats among the ones we're adding const repeatIds = Codec.Compilations.Utils.findRepeatCompilationIds(compilations); if (repeatIds.size !== 0) { throw new Codec.RepeatCompilationIdError([...repeatIds]); } //now: checks are over, start adding stuff this.compilations = [...this.compilations, ...compilations]; const { definitions: referenceDeclarations, typesByCompilation: userDefinedTypesByCompilation, types: userDefinedTypes } = codec_1.Compilations.Utils.collectUserDefinedTypesAndTaggedOutputs(compilations); Object.assign(this.referenceDeclarations, referenceDeclarations); Object.assign(this.userDefinedTypesByCompilation, userDefinedTypesByCompilation); Object.assign(this.userDefinedTypes, userDefinedTypes); const { contexts, deployedContexts, contractsAndContexts, allocationInfo } = codec_1.AbiData.Allocate.Utils.collectAllocationInfo(compilations); this.contexts = Object.assign(contexts, this.contexts); //HACK: we want to //prioritize new contexts over old contexts, so we do this. a proper timestamp //system would be better, but this should hopefully do for now. Object.assign(this.deployedContexts, deployedContexts); this.contractsAndContexts = [ ...this.contractsAndContexts, ...contractsAndContexts ]; //everything up till now has been pretty straightforward merges. //but allocations are a bit more complicated. //some of them can be merged straightforwardly, some can't. //we'll start with the straightforward ones. const abiAllocations = codec_1.AbiData.Allocate.getAbiAllocations(userDefinedTypes); Object.assign(this.allocations.abi, abiAllocations); const storageAllocations = codec_1.Storage.Allocate.getStorageAllocations(userDefinedTypesByCompilation); Object.assign(this.allocations.storage, storageAllocations); const stateAllocations = codec_1.Storage.Allocate.getStateAllocations(allocationInfo, referenceDeclarations, userDefinedTypes, storageAllocations //only need the ones from the new compilations ); Object.assign(this.allocations.state, stateAllocations); //now we have calldata allocations. merging these is still mostly straightforward, //but slightly less so. const calldataAllocations = codec_1.AbiData.Allocate.getCalldataAllocations(allocationInfo, referenceDeclarations, userDefinedTypes, abiAllocations //only need the ones from the new compilations ); //merge constructor and function allocations separately Object.assign(this.allocations.calldata.constructorAllocations, calldataAllocations.constructorAllocations); Object.assign(this.allocations.calldata.functionAllocations, calldataAllocations.functionAllocations); //finally, redo the allocations for returndata and eventdata. //attempting to perform a merge on these is too complicated, so we //won't try; we'll just recompute. this.allocations.returndata = codec_1.AbiData.Allocate.getReturndataAllocations(allocationInfo, referenceDeclarations, userDefinedTypes, this.allocations.abi //we're doing this for merged result, so use merged input! ); this.allocations.event = codec_1.AbiData.Allocate.getEventAllocations(allocationInfo, referenceDeclarations, userDefinedTypes, this.allocations.abi //we're doing this for merged result, so use merged input! ); }); } /** * **This function is asynchronous.** * * Adds additional compilations to the decoder like [[addCompilations]], * but allows it to be specified in more general forms. * * @param projectInfo Information about the additional compilations or * contracts to be decoded. This may come in several forms; see the type * documentation for more information. If passing in `{ compilations: ... }`, * take care that the compilations have different IDs from others passed in * so far, otherwise this will error. If passed in in another form, an ID * will be assigned automatically, which should generally avoid any * collisions. */ addAdditionalProjectInfo(projectInfo) { return __awaiter(this, void 0, void 0, function* () { const compilations = codec_1.Compilations.Utils.infoToCompilations(projectInfo, `decoderAdditionalShimmedCompilationGroup(${this.addProjectInfoNonce})`); this.addProjectInfoNonce++; yield this.addCompilations(compilations); }); } /** * @protected */ getCode(address, block) { return __awaiter(this, void 0, void 0, function* () { //if pending, ignore the cache if (block === "pending") { return codec_1.Conversion.toBytes(yield this.providerAdapter.getCode(address, block)); } //otherwise, start by setting up any preliminary layers as needed if (this.codeCache[block] === undefined) { this.codeCache[block] = {}; } //now, if we have it cached, just return it if (this.codeCache[block][address] !== undefined) { return this.codeCache[block][address]; } //otherwise, get it, cache it, and return it let code = codec_1.Conversion.toBytes(yield this.providerAdapter.getCode(address, block)); this.codeCache[block][address] = code; return code; }); } /** * @protected */ regularizeBlock(block) { return __awaiter(this, void 0, void 0, function* () { if (typeof block === "number" || block === "pending") { return block; } if (block === null) { return "pending"; } return (yield this.providerAdapter.getBlockByNumber(block)).number; }); } /** * **This method is asynchronous.** * * Takes a [[Transaction]] object and decodes it. The result is a * [[CalldataDecoding]]; see the documentation on that interface for more. * * Note that decoding of transactions sent to libraries is presently not * supported and may have unreliable results. Limited support for this is * planned for future versions. * @param transaction The transaction to be decoded. */ decodeTransaction(transaction) { return __awaiter(this, void 0, void 0, function* () { return yield this.decodeTransactionWithAdditionalContexts(transaction); }); } /** * @protected */ reverseEnsResolve(address) { return __awaiter(this, void 0, void 0, function* () { debug("reverse resolving %s", address); if (this.ens === null) { debug("no ens set up!"); return null; } if (address in this.ensCache) { debug("got cached: %o", this.ensCache[address]); return this.ensCache[address]; } let name; try { //try-catch because ensjs throws on bad UTF-8 :-/ //this should be fixed later name = (yield this.ens.getName(address)).name; debug("got name: %o", name); } catch (_a) { //Normally I'd rethrow unexpected errors, but given the context here //that seems like it might be a problem name = null; } if (name !== null) { //do a forward resolution check to make sure it matches let checkAddress; try { checkAddress = yield this.ens.name(name).getAddress(); } catch (_b) { //why the try/catch? because forward resolution will throw if the //name contains certain characters that are illegal in a domain name, //but this isn't in any way enforced on reverse resolution above. yay. checkAddress = null; } if (checkAddress !== address) { //if it doesn't, the name is no good! name = null; } } const nameAsBytes = name !== null ? codec_1.Conversion.stringToBytes(name) : null; this.ensCache[address] = nameAsBytes; return nameAsBytes; }); } /** * @protected */ decodeTransactionWithAdditionalContexts(transaction, additionalContexts = {}, additionalAllocations, overrideContext, isForSelectorBasedDecoding) { return __awaiter(this, void 0, void 0, function* () { const block = transaction.blockNumber !== null ? Number(transaction.blockNumber) : null; const blockNumber = yield this.regularizeBlock(block); const isConstructor = transaction.to === null; const context = overrideContext || (yield this.getContextByAddress(transaction.to, blockNumber, transaction.input, additionalContexts)); let allocations = this.allocations; if (overrideContext) { //if we've got an override context, let's override some things //(this branch is used when doing selector-based decoding) allocations = Object.assign(Object.assign({}, this.allocations), { calldata: Object.assign(Object.assign({}, this.allocations.calldata), { functionAllocations: Object.assign(Object.assign({}, this.allocations.calldata.functionAllocations), { [context.context]: additionalAllocations }) }) }); } else if (context && !(context.context in this.contexts)) { //otherwise, if the context comes from additionalContexts, //we'll add the additional allocations to the allocations; //however, we'll allow other allocations to override it... //when we're not overriding, it's only supposed to be used if necessary! allocations = Object.assign(Object.assign({}, this.allocations), { calldata: Object.assign(Object.assign({}, this.allocations.calldata), { functionAllocations: Object.assign({ [context.context]: additionalAllocations }, this.allocations.calldata.functionAllocations) }) }); } const data = codec_1.Conversion.toBytes(transaction.input); const contexts = Object.assign(Object.assign({}, this.deployedContexts), additionalContexts); const info = { state: { storage: {}, calldata: data }, userDefinedTypes: this.userDefinedTypes, allocations, contexts, currentContext: context }; const decoder = (0, codec_1.decodeCalldata)(info, isConstructor, isForSelectorBasedDecoding); //turn on strict mode for selector-based decoding let result = decoder.next(); while (result.done === false) { let request = result.value; let response; switch (request.type) { case "code": response = yield this.getCode(request.address, blockNumber); break; case "ens-primary-name": response = yield this.reverseEnsResolve(request.address); break; //not writing a storage case as it shouldn't occur here! } result = decoder.next(response); } //at this point, result.value holds the final value let decoding = result.value; //...except wait! we're not done yet! we need to do multicall processing! if (decoding.kind === "function") { decoding = yield this.withMulticallInterpretations(decoding, transaction, additionalContexts, additionalAllocations, overrideContext); } //...and 4byte.directory processing if ((decoding.kind === "message" || decoding.kind === "unknown") && !isForSelectorBasedDecoding //prevent infinite loops! ) { const selectorBasedDecodings = yield this.decodeTransactionBySelector(transaction, data, //this is redundant but included for convenience additionalContexts, context); if (selectorBasedDecodings.length > 0) { decoding.interpretations.selectorBasedDecodings = selectorBasedDecodings; } } return decoding; }); } /** * **This method is asynchronous.** * * Takes a [[Log]] object and decodes it. Logs can be ambiguous, so * this function returns an array of [[LogDecoding|LogDecodings]]. * * Note that logs are decoded in strict mode, so (with one exception) none of the decodings should * contain errors; if a decoding would contain an error, instead it is simply excluded from the * list of possible decodings. The one exception to this is that indexed parameters of reference * type cannot meaningfully be decoded, so those will decode to an error. * * If there are multiple possible decodings, they will always be listed in the following order: * * 1. Non-anonymous events coming from the contract itself (these will moreover be ordered * from most derived to most base) * 2. Non-anonymous events coming from libraries * 3. Anonymous events coming from the contract itself (again, ordered from most derived * to most base) * 4. Anonymous events coming from libraries * * You can check the kind and class.contractKind fields to distinguish between these. * * If no possible decodings are found, the returned array of decodings will be empty. * * Note that different decodings may use different decoding modes. * * Using `options.extras = "on"` or `options.extras = "necessary"` will change the * above behavior; see the documentation on [[ExtrasAllowed]] for more. * * If absolutely necessary, you can also set `options.disableChecks = true` to allow * looser decoding. Only use this option if you know what you are doing. * * @param log The log to be decoded. * @param options Options for controlling decoding. */ decodeLog(log, options = {}) { return __awaiter(this, void 0, void 0, function* () { return yield this.decodeLogWithAdditionalOptions(log, options); }); } /** * @protected */ decodeLogWithAdditionalOptions(log, options = {}, additionalContexts = {}) { return __awaiter(this, void 0, void 0, function* () { const block = log.blockNumber !== null ? Number(log.blockNumber) : null; const blockNumber = yield this.regularizeBlock(block); const data = codec_1.Conversion.toBytes(log.data); const topics = log.topics.map(codec_1.Conversion.toBytes); const info = { state: { storage: {}, eventdata: data, eventtopics: topics }, userDefinedTypes: this.userDefinedTypes, allocations: this.allocations, contexts: Object.assign(Object.assign({}, this.deployedContexts), additionalContexts) }; const decoder = (0, codec_1.decodeEvent)(info, log.address, options); let result = decoder.next(); while (result.done === false) { let request = result.value; let response; switch (request.type) { case "code": response = yield this.getCode(request.address, blockNumber); break; case "ens-primary-name": response = yield this.reverseEnsResolve(request.address); break; //not writing a storage case as it shouldn't occur here! } result = decoder.next(response); } //at this point, result.value holds the final value return result.value; }); } /** * **This method is asynchronous.** * * Gets all events meeting certain conditions and decodes them. * This function is fairly rudimentary at the moment but more functionality * will be added in the future. * @param options Used to determine what events to fetch and how to decode * them; see the documentation on the [[EventOptions]] type for more. * @return An array of [[DecodedLog|DecodedLogs]]. * These consist of a log together with its possible decodings; see that * type for more info. And see [[decodeLog]] for more info on how log * decoding works in general. * @example `events({name: "TestEvent"})` -- get events named "TestEvent" * from the most recent block */ events(options = {}) { return __awaiter(this, void 0, void 0, function* () { return yield this.eventsWithAdditionalContexts(options); }); } /** * @protected */ eventsWithAdditionalContexts(options = {}, additionalContexts = {}) { return __awaiter(this, void 0, void 0, function* () { let { address, name, fromBlock, toBlock } = options; if (fromBlock === undefined) { fromBlock = "latest"; } if (toBlock === undefined) { toBlock = "latest"; } const fromBlockNumber = yield this.regularizeBlock(fromBlock); const toBlockNumber = yield this.regularizeBlock(toBlock); const logs = yield this.providerAdapter.getPastLogs({ address, fromBlock: fromBlockNumber, toBlock: toBlockNumber }); let events = yield Promise.all(logs.map((log) => __awaiter(this, void 0, void 0, function* () { return (Object.assign(Object.assign({}, log), { decodings: yield this.decodeLogWithAdditionalOptions(log, options, additionalContexts) })); }))); debug("events: %o", events); //if a target name was specified, we'll restrict to events that decoded //to something with that name. (note that only decodings with that name //will have been returned from decodeLogs in the first place) if (name !== undefined) { events = events.filter(event => event.decodings.length > 0); } return events; }); } /** * Takes a [[CalldataDecoding]], which may have been produced in full mode or ABI mode, * and converts it to its ABI mode equivalent. See the README for more information. * * Please only use on decodings produced by this same decoder instance; use * on decodings produced by other instances may not work consistently. * @param decoding The decoding to abify */ abifyCalldataDecoding(decoding) { return Codec.abifyCalldataDecoding(decoding, this.userDefinedTypes); } /** * Takes a [[LogDecoding]], which may have been produced in full mode or ABI mode, * and converts it to its ABI mode equivalent. See the README for more information. * * Please only use on decodings produced by this same decoder instance; use * on decodings produced by other instances may not work consistently. * @param decoding The decoding to abify */ abifyLogDecoding(decoding) { return Codec.abifyLogDecoding(decoding, this.userDefinedTypes); } /** * Takes a [[ReturndataDecoding]], which may have been produced in full mode * or ABI mode, and converts it to its ABI mode equivalent. See the README * for more information. * * Please only use on decodings produced by this same decoder instance; use * on decodings produced by other instances may not work consistently. * @param decoding The decoding to abify */ abifyReturndataDecoding(decoding) { return Codec.abifyReturndataDecoding(decoding, this.userDefinedTypes); } //normally, this function gets the code of the given address at the given block, //and checks this against the known contexts to determine the contract type //however, if this fails and constructorBinary is passed in, it will then also //attempt to determine it from that getContextByAddress(address, block, constructorBinary, additionalContexts = {}) { return __awaiter(this, void 0, void 0, function* () { let code; if (address !== null) { code = codec_1.Conversion.toHexString(yield this.getCode(address, block)); } else if (constructorBinary) { code = constructorBinary; } //if neither of these hold... we have a problem let contexts = Object.assign(Object.assign({}, this.contexts), additionalContexts); return codec_1.Contexts.Utils.findContext(contexts, code); }); } //finally: the spawners! /** * **This method is asynchronous.** * * Constructs a contract decoder for a given contract artifact. * @param artifact The artifact for the contract. * * A contract constructor object may be substituted for the artifact, so if * you're not sure which you're dealing with, it's OK. * * Note: The artifact must be for a contract that the decoder knows about; * otherwise you will have problems. */ forArtifact(artifact) { return __awaiter(this, void 0, void 0, function* () { let { compilation, contract } = codec_1.Compilations.Utils.findCompilationAndContract(this.compilations, artifact); //to be *sure* we've got the right ABI, we trust the input over what was //found contract = Object.assign(Object.assign({}, contract), { abi: artifact.abi }); let contractDecoder = new ContractDecoder(contract, compilation, this, artifact); yield contractDecoder.init(); return contractDecoder; }); } /** * **This method is asynchronous.** * * Constructs a contract instance decoder for a given instance of a contract in this * project. * @param artifact The artifact for the contract. * * A contract constructor object may be substituted for the artifact, so if * you're not sure which you're dealing with, it's OK. * * Note: The artifact must be for a contract that the decoder knows about; * otherwise you will have problems. * @param address The address of the contract instance to decode. If left out, it will be autodetected. * If an invalid address is provided, this method will throw an exception. */ forInstance(artifact, address) { return __awaiter(this, void 0, void 0, function* () { let contractDecoder = yield this.forArtifact(artifact); return yield contractDecoder.forInstance(address); }); } /** * **This method is asynchronous.** * * Constructs a contract instance decoder for a given instance of a contract in this * project. Unlike [[forInstance]], this method doesn't require an artifact; it * will automatically detect the class of the given contract. If it's not in * the project, or the decoder can't identify it, you'll get an exception. * @param address The address of the contract instance to decode. * If an invalid address is provided, this method will throw an exception. * @param block You can include this argument to specify that this should be * based on the addresses content's at a specific block (if say the contract * has since self-destructed). */ forAddress(address, block = "latest") { return __awaiter(this, void 0, void 0, function* () { if (!web3_utils_1.default.isAddress(address)) { throw new errors_1.InvalidAddressError(address); } address = web3_utils_1.default.toChecksumAddress(address); const blockNumber = yield this.regularizeBlock(block); const deployedBytecode = codec_1.Conversion.toHexString(yield this.getCode(address, blockNumber)); const contractAndContexts = this.contractsAndContexts.find(({ deployedContext }) => deployedContext && codec_1.Contexts.Utils.matchContext(deployedContext, deployedBytecode)); if (!contractAndContexts) { throw new errors_1.ContractNotFoundError(undefined, undefined, deployedBytecode, address); } const { contract, compilationId } = contractAndContexts; const compilation = this.compilations.find(compilation => compilation.id === compilationId); let contractDecoder = new ContractDecoder(contract, compilation, this); //no artifact //(artifact is only used for address autodetection, and here we're supplying the //address, so this won't cause any problems) yield contractDecoder.init(); return yield contractDecoder.forInstance(address); }); } //the following functions are intended for internal use only /** * @protected */ getReferenceDeclarations() { return this.referenceDeclarations; } /** * @protected */ getUserDefinedTypes() { return this.userDefinedTypes; } /** * @protected */ getAllocations() { return this.allocations; } /** * @protected */ getProviderAdapter() { return this.providerAdapter; } /** * @protected */ getEnsSettings() { return this.ensSettings; } /** * @protected */ getDeployedContexts() { return this.deployedContexts; } //now, the interpretation stuff. ideally this would be a separate file //(I mean, as would each decoder!) but that would cause circular imports, so... :-/ withMulticallInterpretations(decoding, transaction, additionalContexts = {}, additionalAllocations, overrideContext) { return __awaiter(this, void 0, void 0, function* () { //first, let's clone our decoding and its interpretations decoding = Object.assign(Object.assign({}, decoding), { interpretations: Object.assign({}, decoding.interpretations) }); //now we can freely modify decoding.interpretations //(note: these may return undefined) decoding.interpretations.multicall = yield this.interpretMulticall(decoding, transaction, additionalContexts, additionalAllocations, overrideContext); decoding.interpretations.aggregate = yield this.interpretAggregate(decoding, transaction, additionalContexts, additionalAllocations, overrideContext); decoding.interpretations.tryAggregate = yield this.interpretTryAggregate(decoding, transaction, additionalContexts, additionalAllocations, overrideContext); decoding.interpretations.deadlinedMulticall = yield this.interpretDeadlinedMulticall(decoding, transaction, additionalContexts, additionalAllocations, overrideContext); decoding.interpretations.specifiedBlockhashMulticall = yield this.interpretBlockhashedMulticall(decoding, transaction, additionalContexts, additionalAllocations, overrideContext); return decoding; }); } interpretMulticall(decoding, transaction, additionalContexts = {}, additionalAllocations, overrideContext) { return __awaiter(this, void 0, void 0, function* () { if (decoding.kind === "function" && decoding.abi.name === "multicall" && decoding.abi.inputs.length === 1 && decoding.abi.inputs[0].type === "bytes[]" && decoding.arguments[0].value.kind === "value") { //sorry, this is going to involve some coercion... const decodedArray = decoding.arguments[0] .value; return yield Promise.all(decodedArray.value.map((callResult) => __awaiter(this, void 0, void 0, function* () { return yield this.interpretCallInMulti(callResult, transaction, additionalContexts, additionalAllocations, overrideContext); }))); } else { return undefined; } }); } interpretCallInMulti(callResult, transaction, additionalContexts = {}, additionalAllocations, overrideContext) { return __awaiter(this, void 0, void 0, function* () { switch (callResult.kind) { case "value": return yield this.decodeTransactionWithAdditionalContexts(Object.assign(Object.assign({}, transaction), { input: callResult.value.asHex }), additionalContexts, additionalAllocations, overrideContext); case "error": return null; } }); } interpretAggregate(decoding, transaction, additionalContexts = {}, additionalAllocations, overrideContext) { return __awaiter(this, void 0, void 0, function* () { if (decoding.kind === "function" && (decoding.abi.name === "aggregate" || decoding.abi.name === "blockAndAggregate") && decoding.abi.inputs.length === 1 && decoding.abi.inputs[0].type === "tuple[]" && decoding.abi.inputs[0].components.length === 2 && decoding.abi.inputs[0].components[0].type === "address" && decoding.abi.inputs[0].components[1].type === "bytes" && decoding.arguments[0].value.kind === "value") { //sorry, this is going to involve some coercion... const decodedArray = decoding.arguments[0] .value; return yield Promise.all(decodedArray.value.map((callResult) => __awaiter(this, void 0, void 0, function* () { return yield this.interpretCallInAggregate(callResult, transaction, additionalContexts, additionalAllocations, overrideContext); }))); } else if (decoding.kind === "function" && decoding.abi.name === "aggregate3" && decoding.abi.inputs.length === 1 && decoding.abi.inputs[0].type === "tuple[]" && decoding.abi.inputs[0].components.length === 3 && decoding.abi.inputs[0].components[0].type === "address" && decoding.abi.inputs[0].components[1].type === "bool" && decoding.abi.inputs[0].components[2].type === "bytes" && decoding.arguments[0].value.kind === "value") { //Identical to above, just split out for clarity const decodedArray = decoding.arguments[0] .value; return yield Promise.all(decodedArray.value.map((callResult) => __awaiter(this, void 0, void 0, function* () { return yield this.interpretCallInAggregate(cal