truffle
Version:
Truffle - Simple development framework for Ethereum
1,098 lines (1,086 loc) • 252 kB
JavaScript
#!/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