truffle
Version:
Truffle - Simple development framework for Ethereum
1,274 lines (1,241 loc) • 724 kB
JavaScript
#!/usr/bin/env node
"use strict";
exports.id = 102;
exports.ids = [102];
exports.modules = {
/***/ 90670:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Abi = exports.ConstructorEntry = exports.FallbackEntry = exports.ReceiveEntry = exports.FunctionEntry = exports.ErrorEntry = exports.EventEntry = exports.EventParameter = exports.Parameter = void 0;
const fc = __importStar(__webpack_require__(96418));
const change_case_1 = __webpack_require__(77159);
const wordLists_1 = __importDefault(__webpack_require__(94147));
const Parameter = () => fc
.tuple(fc.record({
name: ParameterName()
}), TypeRecord())
.map(([{ name }, type]) => (Object.assign({ name }, type)));
exports.Parameter = Parameter;
const EventParameter = () => fc
.tuple(fc.record({
name: ParameterName(),
indexed: fc.boolean()
}), TypeRecord())
.map(([{ name, indexed }, type]) => (Object.assign({ name, indexed }, type)));
exports.EventParameter = EventParameter;
const EventEntry = () => fc.record({
type: fc.constant("event"),
name: EventName(),
inputs: fc.array((0, exports.EventParameter)(), { maxLength: 10 }).filter(inputs => {
if (inputs.filter(({ indexed }) => indexed).length > 3) {
// only up to 3 params can be indexed
return false;
}
// names that are not blank should be unique
const names = inputs.map(({ name }) => name).filter(name => name !== "");
return names.length === new Set(names).size;
}),
anonymous: fc.boolean()
});
exports.EventEntry = EventEntry;
const ErrorEntry = () => fc.record({
type: fc.constant("error"),
name: ErrorName(),
inputs: fc.array((0, exports.Parameter)(), { maxLength: 10 }).filter(inputs => {
// names that are not blank should be unique
const names = inputs.map(({ name }) => name).filter(name => name !== "");
return names.length === new Set(names).size;
})
});
exports.ErrorEntry = ErrorEntry;
const FunctionEntry = () => fc
.tuple(fc.record({
type: fc.constant("function")
}, { withDeletedKeys: true }), fc.record({
name: FunctionName(),
inputs: fc.array((0, exports.Parameter)(), { maxLength: 10 })
}), fc.record({
outputs: fc.array((0, exports.Parameter)(), { maxLength: 10 })
}, { withDeletedKeys: true }), fc
.tuple(fc.oneof(fc.constant("pure"), fc.constant("view"), fc.constant("nonpayable"), fc.constant("payable")), fc.boolean(), fc.boolean())
.map(([stateMutability, includeLegacy, includeModern]) => {
const payable = stateMutability === "payable";
const constant = stateMutability === "view" || stateMutability === "pure";
const modern = { stateMutability };
const legacy = { payable, constant };
return includeLegacy && includeModern
? Object.assign(Object.assign({}, modern), legacy) : includeModern
? modern
: legacy;
}))
.map(records => records.reduce((a, b) => (Object.assign(Object.assign({}, a), b)), {}))
.filter(entry => {
const { inputs, outputs = [] } = entry;
// names that are not blank should be unique
const names = [...inputs, ...outputs]
.map(({ name }) => name)
.filter(name => name !== "");
return names.length === new Set(names).size;
});
exports.FunctionEntry = FunctionEntry;
const ReceiveEntry = () => fc.record({
type: fc.constant("receive"),
stateMutability: fc.constant("payable")
});
exports.ReceiveEntry = ReceiveEntry;
const FallbackEntry = () => fc
.tuple(fc.record({
type: fc.constant("fallback")
}), fc
.tuple(fc.oneof(fc.constant("nonpayable"), fc.constant("payable")), fc.boolean(), fc.boolean())
.map(([stateMutability, includeLegacy, includeModern]) => {
const payable = stateMutability === "payable";
const modern = { stateMutability };
const legacy = { payable };
return includeLegacy && includeModern
? Object.assign(Object.assign({}, modern), legacy) : includeModern
? modern
: legacy;
}))
.map(([{ type }, mutabilityFields]) => (Object.assign({ type }, mutabilityFields)));
exports.FallbackEntry = FallbackEntry;
const ConstructorEntry = () => fc
.tuple(fc.record({
type: fc.constant("constructor"),
inputs: fc.array((0, exports.Parameter)(), { maxLength: 10 }).filter(inputs => {
// names that are not blank should be unique
const names = inputs
.map(({ name }) => name)
.filter(name => name !== "");
return names.length === new Set(names).size;
})
}), fc
.tuple(fc.oneof(fc.constant("nonpayable"), fc.constant("payable")), fc.boolean(), fc.boolean())
.map(([stateMutability, includeLegacy, includeModern]) => {
const payable = stateMutability === "payable";
const modern = { stateMutability };
const legacy = { payable };
return includeLegacy && includeModern
? Object.assign(Object.assign({}, modern), legacy) : includeModern
? modern
: legacy;
}))
.map(([{ type, inputs }, mutabilityFields]) => (Object.assign({ type,
inputs }, mutabilityFields)));
exports.ConstructorEntry = ConstructorEntry;
const Abi = () => fc
.tuple((0, exports.ConstructorEntry)(), (0, exports.FallbackEntry)(), (0, exports.ReceiveEntry)(), fc.array(fc.oneof((0, exports.FunctionEntry)(), (0, exports.EventEntry)(), (0, exports.ErrorEntry)())))
.chain(([constructor, fallback, receive, entries]) => fc.shuffledSubarray([constructor, fallback, receive, ...entries]));
exports.Abi = Abi;
var Numerics;
(function (Numerics) {
// 0 < n <= 32
// use subtraction so that fast-check treats 32 as simpler than 1
Numerics.Bytes = () => fc.nat(31).map(k => 32 - k);
// 0 < n <= 256, 8 | n
Numerics.Bits = () => Numerics.Bytes().map(k => 8 * k);
// 0 < n <= 80
// use fancy math so that fast-check treats 18 as the simplest case
//
// 0 ----------------- 79
// lines up as:
// 18 ------ 80, 0 --- 17
Numerics.DecimalPlaces = () => fc.nat(79).map(k => ((k + 17) % 80) + 1);
Numerics.Precision = () => fc.tuple(Numerics.Bits(), Numerics.DecimalPlaces());
})(Numerics || (Numerics = {}));
var Primitives;
(function (Primitives) {
Primitives.Uint = () => Numerics.Bits().map(m => `uint${m}`);
Primitives.Int = () => Numerics.Bits().map(m => `int${m}`);
Primitives.Address = () => fc.constant("address");
Primitives.Bool = () => fc.constant("bool");
Primitives.Fixed = () => Numerics.Precision().map(([m, n]) => `fixed${m}x${n}`);
Primitives.Ufixed = () => Numerics.Precision().map(([m, n]) => `ufixed${m}x${n}`);
Primitives.BytesM = () => Numerics.Bytes().map(m => `bytes${m}`);
Primitives.Function = () => fc.constant("function");
Primitives.Bytes = () => fc.constant("bytes");
Primitives.String = () => fc.constant("string");
Primitives.Tuple = () => fc.constant("tuple");
})(Primitives || (Primitives = {}));
const Primitive = () => fc.oneof(Primitives.Uint(), Primitives.Int(), Primitives.Address(), Primitives.Bool(), Primitives.Fixed(), Primitives.Ufixed(), Primitives.BytesM(), Primitives.Function(), Primitives.Bytes(), Primitives.String(), Primitives.Tuple());
const Type = fc.memo(n => n === 0
? Primitive()
: // we cap this at 3 so that fast-check doesn't blow the stack
fc.oneof(Primitive(), ArrayFixed(n > 3 ? 3 : n), ArrayDynamic(n)));
const ArrayFixed = fc.memo(n => fc
.tuple(Type(n - 1), fc.integer({ min: 1, max: 256 }))
.map(([type, length]) => `${type}[${length}]`));
const ArrayDynamic = fc.memo(n => Type(n - 1).map(type => `${type}[]`));
const reservedWords = new Set([
"Error",
"Panic",
"_",
"abi",
"abstract",
"addmod",
"address",
"after",
"alias",
"anonymous",
"apply",
"as",
"assembly",
"assert",
"auto",
"block",
"blockhash",
"bool",
"break",
"byte",
"bytes",
"calldata",
"case",
"catch",
"constant",
"constructor",
"continue",
"contract",
"copyof",
"days",
"default",
"define",
"delete",
"ecrecover",
"else",
"emit",
"enum",
"error",
"ether",
"event",
"external",
"fallback",
"false",
"final",
"finney",
"fixed",
"for",
"from",
"function",
"gasleft",
"gwei",
"hours",
"if",
"immutable",
"implements",
"import",
"in",
"indexed",
"inline",
"int",
"interface",
"internal",
"is",
"keccak256",
"let",
"library",
"log0",
"log1",
"log2",
"log3",
"log4",
"macro",
"mapping",
"match",
"memory",
"minutes",
"modifier",
"msg",
"mulmod",
"mutable",
"new",
"now",
"null",
"of",
"override",
"partial",
"payable",
"pragma",
"private",
"promise",
"public",
"pure",
"receive",
"reference",
"relocatable",
"require",
"return",
"returns",
"revert",
"ripemd160",
"sealed",
"seconds",
"selfdestruct",
"sha256",
"sha3",
"sizeof",
"static",
"storage",
"string",
"struct",
"suicide",
"super",
"supports",
"switch",
"szabo",
"this",
"throw",
"true",
"try",
"tx",
"type",
"typedef",
"typeof",
"ufixed",
"uint",
"unchecked",
"using",
"var",
"view",
"virtual",
"weeks",
"wei",
"while",
"years"
]);
const Name = (wordTypes = ["noun"], transform = change_case_1.camelCase) => {
const wordArbitraries = wordTypes.map(wordType => fc.constantFrom(...wordLists_1.default[wordType]));
const wordsArbitrary = fc.tuple(...wordArbitraries);
const nameArbitrary = wordsArbitrary.map(words => transform(words.join(" ")));
return nameArbitrary.filter(word => !reservedWords.has(word));
};
const ParameterName = () => fc.oneof({ arbitrary: Name(["noun"]), weight: 9 }, { arbitrary: fc.constant(""), weight: 1 });
const EventName = () => Name(["verb", "noun"], change_case_1.pascalCase);
const ErrorName = () => Name(["noun", "noun"], change_case_1.pascalCase);
const FunctionName = () => Name(["verb", "noun"]);
const TypeRecord = () => Type().chain(type => type.startsWith("tuple")
? fc.record({
type: fc.constant(type),
components: fc
.array((0, exports.Parameter)().filter(({ name }) => name !== ""), { minLength: 1, maxLength: 5 })
.filter(items => {
const names = items
.map(({ name }) => name)
.filter(name => name !== "");
return names.length === new Set(names).size;
})
})
: fc.record({
type: fc.constant(type)
}));
//# sourceMappingURL=arbitrary.js.map
/***/ }),
/***/ 7651:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
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.Arbitrary = void 0;
__exportStar(__webpack_require__(51931), exports);
__exportStar(__webpack_require__(57794), exports);
__exportStar(__webpack_require__(14230), exports);
__exportStar(__webpack_require__(4924), exports);
const Arbitrary = __importStar(__webpack_require__(90670));
exports.Arbitrary = Arbitrary;
//# sourceMappingURL=index.js.map
/***/ }),
/***/ 57794:
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.normalizeEntry = exports.normalize = void 0;
const normalize = (looseAbi) => looseAbi.map(exports.normalizeEntry);
exports.normalize = normalize;
const normalizeEntry = (looseEntry) => {
if (looseEntry.type === "event" || looseEntry.type === "error") {
// nothing gets normalized for events or errors right now
return looseEntry;
}
const entry = Object.assign(Object.assign(Object.assign({}, looseEntry), normalizeStateMutability(looseEntry)), { type: looseEntry.type || "function" });
if (entry.type === "function") {
entry.outputs = entry.outputs || [];
}
delete entry.payable;
delete entry.constant;
return entry;
};
exports.normalizeEntry = normalizeEntry;
const normalizeStateMutability = ({ stateMutability, payable, constant }) => {
if (stateMutability) {
return { stateMutability };
}
return {
stateMutability: payable ? "payable" : constant ? "view" : "nonpayable"
};
};
//# sourceMappingURL=normalize.js.map
/***/ }),
/***/ 4924:
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.parseParameterList = exports.parseSignature = exports.parseEventSignature = exports.parseErrorSignature = exports.parseFunctionSignature = void 0;
function parseFunctionSignature(signature) {
const { name, inputs } = parseSignature(signature);
return {
type: "function",
name,
inputs,
outputs: [],
stateMutability: "nonpayable"
};
}
exports.parseFunctionSignature = parseFunctionSignature;
function parseErrorSignature(signature) {
const { name, inputs } = parseSignature(signature);
return {
type: "error",
name,
inputs
};
}
exports.parseErrorSignature = parseErrorSignature;
function parseEventSignature(signature) {
const { name, inputs } = parseSignature(signature);
return {
type: "event",
name,
inputs: inputs.map(parameter => (Object.assign(Object.assign({}, parameter), { indexed: false }))),
anonymous: false
};
}
exports.parseEventSignature = parseEventSignature;
function parseSignature(signature) {
let openParenIndex = signature.indexOf("(");
if (openParenIndex === -1) {
openParenIndex = signature.length; //set to end of string if not found
}
const parameterList = signature.slice(openParenIndex);
const name = signature.slice(0, openParenIndex);
const inputs = parseParameterList(parameterList);
return {
name,
inputs
};
}
exports.parseSignature = parseSignature;
function parseParameterList(parameterList) {
const { parameters, remaining } = parseParameterListWithRemainder(parameterList);
if (remaining !== "") {
throw new Error(`Parameter list had extra text ${remaining} afterwards`);
}
return parameters;
}
exports.parseParameterList = parseParameterList;
function parseParameterListWithRemainder(parameterList) {
if (parameterList === "") {
throw new Error("Parameter list is missing");
}
if (parameterList[0] !== "(") {
throw new Error(`Parameter list ${parameterList} doesn't begin with parenthesis"`);
}
if (parameterList[1] === ")") {
//due to the appraoch we take below, we need to handle the case of an empty
//parameter list as a special case. a more proper parser wouldn't need to do
//this, but, this is easier. :P
return { parameters: [], remaining: parameterList.slice(2) };
}
let remaining = parameterList.slice(1); //cut off opening parenthesis
let parameters = [];
//now: we process parameters one by one. note we CANNOT split on comma!!
//that approach will break down if there are any tuples.
while (true) {
if (remaining[0] === "(") {
//tuple or tuple array case
let components;
({ parameters: components, remaining } =
parseParameterListWithRemainder(remaining));
//now we have the components, but there might be an array suffix
//copypaste warning, this is copypasted from the simple case below!
const match = remaining.match(/[,)]/); //find next comma or close paren
const nextTerminatorIndex = match === null || match === void 0 ? void 0 : match.index;
if (nextTerminatorIndex === undefined) {
//if there is none, throw
throw new Error("Unmatched open parenthesis");
}
const final = remaining[nextTerminatorIndex] === ")";
const arraySuffix = remaining.slice(0, nextTerminatorIndex);
if (!arraySuffix.match(/(\[\d*\])*/)) {
//here at least it's pretty straightforward to check whether the array
//suffix is valid or not
throw new Error(`Invalid array suffix ${arraySuffix}`);
}
parameters.push({
name: "",
type: "tuple" + arraySuffix,
components
});
remaining = remaining.slice(nextTerminatorIndex + 1);
if (final) {
//if we hit a ")", close this group
return { parameters, remaining };
}
}
else {
//simple case (including arrays w/o tuples)
const match = remaining.match(/[,)]/); //find next comma or close paren
const nextTerminatorIndex = match === null || match === void 0 ? void 0 : match.index;
if (nextTerminatorIndex === undefined) {
//if there is none, throw
throw new Error("Unmatched open parenthesis");
}
const final = remaining[nextTerminatorIndex] === ")";
const parameterString = remaining.slice(0, nextTerminatorIndex);
if (!parameterString.match(/[a-zA-Z]([a-zA-Z\d])*(\[\d*\])*/)) {
//we're not going to try to fully validate the type here...
//we're just going to check that it looks vaguely correct, sorry
throw new Error(`Malformed type ${parameterString}`);
}
parameters.push({
name: "",
type: parameterString
});
remaining = remaining.slice(nextTerminatorIndex + 1);
if (final) {
//if we hit a ")", close this group
return { parameters, remaining };
}
}
}
}
//# sourceMappingURL=parse.js.map
/***/ }),
/***/ 14230:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.abiSelector = exports.abiTypeSignature = exports.abiTupleSignature = exports.abiSignature = exports.ShortSelectorSize = void 0;
const web3_utils_1 = __webpack_require__(18269);
exports.ShortSelectorSize = 4;
//NOTE: this function returns the written out SIGNATURE, not the SELECTOR
function abiSignature(abiEntry) {
return abiEntry.name + abiTupleSignature(abiEntry.inputs);
}
exports.abiSignature = abiSignature;
function abiTupleSignature(parameters) {
const components = parameters.map(abiTypeSignature);
return "(" + components.join(",") + ")";
}
exports.abiTupleSignature = abiTupleSignature;
function abiTypeSignature(parameter) {
const tupleMatch = parameter.type.match(/^tuple(.*)/);
if (tupleMatch === null) {
//does not start with "tuple"
return parameter.type;
}
else {
const tail = tupleMatch[1]; //everything after "tuple"
const tupleSignature = abiTupleSignature(parameter.components); //it won't be undefined
return tupleSignature + tail;
}
}
exports.abiTypeSignature = abiTypeSignature;
function abiSelector(abiEntry) {
const signature = abiSignature(abiEntry);
//NOTE: web3's soliditySha3 has a problem if the empty
//string is passed in. Fortunately, that should never happen here.
const hash = (0, web3_utils_1.soliditySha3)({ type: "string", value: signature });
switch (abiEntry.type) {
case "event":
return hash;
case "function":
case "error":
return hash.slice(0, 2 + 2 * exports.ShortSelectorSize); //arithmetic to account for hex string
}
}
exports.abiSelector = abiSelector;
//# sourceMappingURL=signature.js.map
/***/ }),
/***/ 51931:
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
//# sourceMappingURL=types.js.map
/***/ }),
/***/ 94147:
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports["default"] = {
noun: [
"driver",
"protocol",
"bandwidth",
"panel",
"microchip",
"program",
"port",
"card",
"array",
"interface",
"system",
"sensor",
"firewall",
"pixel",
"alarm",
"feed",
"monitor",
"application",
"transmitter",
"bus",
"circuit",
"capacitor",
"matrix"
],
verb: [
"bypass",
"hack",
"override",
"compress",
"copy",
"navigate",
"index",
"connect",
"generate",
"quantify",
"calculate",
"synthesize",
"input",
"transmit",
"program",
"reboot",
"parse",
"handle",
"circumvent",
"cross",
"fumble",
"tweak",
"swap"
]
};
//# sourceMappingURL=wordLists.js.map
/***/ }),
/***/ 51325:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getEventAllocations = exports.getReturndataAllocations = exports.getCalldataAllocations = exports.abiSizeInfo = exports.getAbiAllocations = exports.FallbackOutputAllocation = exports.Utils = void 0;
const debug_1 = __importDefault(__webpack_require__(15158));
const debug = (0, debug_1.default)("codec:abi-data:allocate");
exports.Utils = __importStar(__webpack_require__(65700));
const Import = __importStar(__webpack_require__(53137));
const AbiDataUtils = __importStar(__webpack_require__(85941));
const web3_utils_1 = __importDefault(__webpack_require__(18269));
const Evm = __importStar(__webpack_require__(49218));
const Common = __importStar(__webpack_require__(99987));
const Conversion = __importStar(__webpack_require__(52714));
const Ast = __importStar(__webpack_require__(14442));
const import_1 = __webpack_require__(60013);
const Format = __importStar(__webpack_require__(29965));
const partition_1 = __importDefault(__webpack_require__(43174));
exports.FallbackOutputAllocation = {
kind: "returnmessage",
selector: new Uint8Array(),
allocationMode: "full"
};
function getAbiAllocations(userDefinedTypes) {
let allocations = {};
for (const dataType of Object.values(userDefinedTypes)) {
if (dataType.typeClass === "struct") {
try {
allocations = allocateStruct(dataType, userDefinedTypes, allocations);
}
catch (_) {
//if allocation fails... oh well, allocation fails, we do nothing and just move on :P
//note: a better way of handling this would probably be to *mark* it
//as failed rather than throwing an exception as that would lead to less
//recomputation, but this is simpler and I don't think the recomputation
//should really be a problem
}
}
}
return allocations;
}
exports.getAbiAllocations = getAbiAllocations;
function allocateStruct(dataType, userDefinedTypes, existingAllocations) {
//NOTE: dataType here should be a *stored* type!
//it is up to the caller to take care of this
return allocateMembers(dataType.id, dataType.memberTypes, userDefinedTypes, existingAllocations);
}
//note: we will still allocate circular structs, even though they're not allowed in the abi, because it's
//not worth the effort to detect them. However on mappings or internal functions, we'll vomit (allocate null)
function allocateMembers(parentId, members, userDefinedTypes, existingAllocations, start = 0) {
let dynamic = false;
//note that we will mutate the start argument also!
//don't allocate things that have already been allocated
if (parentId in existingAllocations) {
return existingAllocations;
}
let allocations = Object.assign({}, existingAllocations); //otherwise, we'll be adding to this, so we better clone
let memberAllocations = [];
for (const member of members) {
let length;
let dynamicMember;
({
size: length,
dynamic: dynamicMember,
allocations
} = abiSizeAndAllocate(member.type, userDefinedTypes, allocations));
//vomit on illegal types in calldata -- note the short-circuit!
if (length === undefined) {
allocations[parentId] = null;
return allocations;
}
let pointer = {
location: "abi",
start,
length
};
memberAllocations.push({
name: member.name,
type: member.type,
pointer
});
start += length;
dynamic = dynamic || dynamicMember;
}
allocations[parentId] = {
members: memberAllocations,
length: dynamic ? Evm.Utils.WORD_SIZE : start,
dynamic
};
return allocations;
}
//first return value is the actual size.
//second return value is whether the type is dynamic
//both will be undefined if type is a mapping or internal function
//third return value is resulting allocations, INCLUDING the ones passed in
function abiSizeAndAllocate(dataType, userDefinedTypes, existingAllocations) {
switch (dataType.typeClass) {
case "bool":
case "address":
case "contract":
case "int":
case "uint":
case "fixed":
case "ufixed":
case "enum":
case "userDefinedValueType":
return {
size: Evm.Utils.WORD_SIZE,
dynamic: false,
allocations: existingAllocations
};
case "string":
return {
size: Evm.Utils.WORD_SIZE,
dynamic: true,
allocations: existingAllocations
};
case "bytes":
return {
size: Evm.Utils.WORD_SIZE,
dynamic: dataType.kind === "dynamic",
allocations: existingAllocations
};
case "mapping":
return {
allocations: existingAllocations
};
case "function":
switch (dataType.visibility) {
case "external":
return {
size: Evm.Utils.WORD_SIZE,
dynamic: false,
allocations: existingAllocations
};
case "internal":
return {
allocations: existingAllocations
};
}
case "array": {
switch (dataType.kind) {
case "dynamic":
return {
size: Evm.Utils.WORD_SIZE,
dynamic: true,
allocations: existingAllocations
};
case "static":
if (dataType.length.isZero()) {
//arrays of length 0 are static regardless of base type
return {
size: 0,
dynamic: false,
allocations: existingAllocations
};
}
const { size: baseSize, dynamic, allocations } = abiSizeAndAllocate(dataType.baseType, userDefinedTypes, existingAllocations);
return {
//WARNING! The use of toNumber() here may throw an exception!
//I'm judging this OK since if you have arrays that large we have bigger problems :P
size: dataType.length.toNumber() * baseSize,
dynamic,
allocations
};
}
}
case "struct": {
let allocations = existingAllocations;
let allocation = allocations[dataType.id];
if (allocation === undefined) {
//if we don't find an allocation, we'll have to do the allocation ourselves
const storedType = (userDefinedTypes[dataType.id]);
if (!storedType) {
throw new Common.UnknownUserDefinedTypeError(dataType.id, Format.Types.typeString(dataType));
}
allocations = allocateStruct(storedType, userDefinedTypes, existingAllocations);
allocation = allocations[storedType.id];
}
//having found our allocation, if it's not null, we can just look up its size and dynamicity
if (allocation !== null) {
return {
size: allocation.length,
dynamic: allocation.dynamic,
allocations
};
}
//if it is null, this type doesn't go in the abi
else {
return {
allocations
};
}
}
case "tuple": {
//Warning! Yucky wasteful recomputation here!
let size = 0;
let dynamic = false;
//note that we don't just invoke allocateStruct here!
//why not? because it has no ID to store the result in!
//and we can't use a fake like -1 because there might be a recursive call to it,
//and then the results would overwrite each other
//I mean, we could do some hashing thing or something, but I think it's easier to just
//copy the logic in this one case (sorry)
for (let member of dataType.memberTypes) {
let { size: memberSize, dynamic: memberDynamic } = abiSizeAndAllocate(member.type, userDefinedTypes, existingAllocations);
size += memberSize;
dynamic = dynamic || memberDynamic;
}
return { size, dynamic, allocations: existingAllocations };
}
}
}
//assumes you've already done allocation! don't use if you haven't!
/**
* @protected
*/
function abiSizeInfo(dataType, allocations) {
let { size, dynamic } = abiSizeAndAllocate(dataType, null, allocations);
//the above line should work fine... as long as allocation is already done!
//the middle argument, userDefinedTypes, is only needed during allocation
//again, this function is only for use if allocation is done, so it's safe to pass null here
return { size, dynamic };
}
exports.abiSizeInfo = abiSizeInfo;
//allocates an external call
//NOTE: returns just a single allocation; assumes primary allocation is already complete!
//NOTE: returns undefined if attempting to allocate a constructor but we don't have the
//bytecode for the constructor
function allocateCalldataAndReturndata(abiEntry, contractNode, referenceDeclarations, userDefinedTypes, abiAllocations, compilationId, compiler, constructorContext, deployedContext) {
//first: determine the corresponding function node
//(simultaneously: determine the offset)
let node = undefined;
let inputParametersFull;
let outputParametersFull;
let inputParametersAbi;
let outputParametersAbi;
let offset; //refers to INPUT offset; output offset is always 0
debug("allocating calldata and returndata");
switch (abiEntry.type) {
case "constructor":
if (!constructorContext) {
return undefined;
}
let rawLength = constructorContext.binary.length;
offset = (rawLength - 2) / 2; //number of bytes in 0x-prefixed bytestring
//for a constructor, we only want to search the particular contract
if (contractNode) {
node = contractNode.nodes.find(functionNode => AbiDataUtils.definitionMatchesAbi(
//note this needn't actually be a function node, but then it will
//return false (well, unless it's a getter node!)
abiEntry, functionNode, referenceDeclarations));
}
//if we can't find it, we'll handle this below
break;
case "function":
offset = Evm.Utils.SELECTOR_SIZE;
//search through base contracts, from most derived (left) to most base (right)
if (contractNode) {
const linearizedBaseContracts = contractNode.linearizedBaseContracts;
debug("linearized: %O", linearizedBaseContracts);
node = findNodeAndContract(linearizedBaseContracts, referenceDeclarations, functionNode => AbiDataUtils.definitionMatchesAbi(abiEntry, functionNode, referenceDeclarations), contractNode).node; //may be undefined! that's OK!
debug("found node: %o", Boolean(node));
}
break;
}
//now: get the parameters (both full-mode & ABI)
if (node) {
switch (node.nodeType) {
case "FunctionDefinition":
//normal case
inputParametersFull = node.parameters.parameters;
outputParametersFull = node.returnParameters.parameters; //this exists even for constructors!
break;
case "VariableDeclaration":
//getter case
({ inputs: inputParametersFull, outputs: outputParametersFull } =
Ast.Utils.getterParameters(node, referenceDeclarations));
break;
}
}
else {
inputParametersFull = undefined;
outputParametersFull = undefined;
}
inputParametersAbi = abiEntry.inputs;
switch (abiEntry.type) {
case "function":
outputParametersAbi = abiEntry.outputs;
break;
case "constructor":
//we just leave this empty for constructors
outputParametersAbi = [];
break;
}
//now: do the allocation!
let { allocation: abiAllocationInput, mode: inputMode } = allocateDataArguments(inputParametersFull, inputParametersAbi, userDefinedTypes, abiAllocations, compilationId, compiler, offset);
let { allocation: abiAllocationOutput, mode: outputMode } = allocateDataArguments(outputParametersFull, outputParametersAbi, userDefinedTypes, abiAllocations, compilationId, compiler
//note no offset
);
debug("modes: %s in, %s out", inputMode, outputMode);
//finally: transform the allocation appropriately
let inputArgumentsAllocation = abiAllocationInput.members.map(member => (Object.assign(Object.assign({}, member), { pointer: {
location: "calldata",
start: member.pointer.start,
length: member.pointer.length
} })));
let outputArgumentsAllocation = abiAllocationOutput.members.map(member => (Object.assign(Object.assign({}, member), { pointer: {
location: "returndata",
start: member.pointer.start,
length: member.pointer.length
} })));
let inputsAllocation = {
abi: abiEntry,
offset,
arguments: inputArgumentsAllocation,
allocationMode: inputMode
};
let outputsAllocation;
switch (abiEntry.type) {
case "function":
outputsAllocation = {
selector: new Uint8Array(),
arguments: outputArgumentsAllocation,
allocationMode: outputMode,
kind: "return"
};
break;
case "constructor":
outputsAllocation = constructorOutputAllocation(deployedContext, contractNode, referenceDeclarations, outputMode);
break;
}
return {
input: inputsAllocation,
output: outputsAllocation
}; //TS chokes on this for some reason
}
//note: allocateEvent doesn't use this because it needs additional
//handling for indexed parameters (maybe these can be unified in
//the future though?)
function allocateDataArguments(fullModeParameters, abiParameters, userDefinedTypes, abiAllocations, compilationId, compiler, offset = 0) {
let allocationMode = fullModeParameters ? "full" : "abi"; //can degrade
let parameterTypes;
let abiAllocation;
if (allocationMode === "full") {
let id = "-1"; //fake ID that doesn't matter
parameterTypes = fullModeParameters.map(parameter => ({
name: parameter.name,
type: Ast.Import.definitionToType(parameter, compilationId, compiler) //if node is defined, compiler had also better be!
}));
debug("parameterTypes: %O", parameterTypes);
//now: perform the allocation!
try {
abiAllocation = allocateMembers(id, parameterTypes, userDefinedTypes, abiAllocations, offset)[id];
}
catch (_a) {
//if something goes wrong, switch to ABI mdoe
debug("falling back to ABI due to exception!");
allocationMode = "abi";
}
}
if (allocationMode === "abi") {
//THIS IS DELIBERATELY NOT AN ELSE
//this is the ABI case. we end up here EITHER
//if node doesn't exist, OR if something went wrong
//during allocation
let id = "-1"; //fake irrelevant ID
parameterTypes = abiParameters.map(parameter => ({
name: parameter.name,
type: Import.abiParameterToType(parameter)
}));
abiAllocation = allocateMembers(id, parameterTypes, userDefinedTypes, abiAllocations, offset)[id];
}
return { allocation: abiAllocation, mode: allocationMode };
}
//allocates an event
//NOTE: returns just a single allocation; assumes primary allocation is already complete!
function allocateEvent(abiEntry, eventNode, contractNode, referenceDeclarations, userDefinedTypes, abiAllocations, compilationId, compiler) {
let parameterTypes;
let nodeId;
let id;
//first: determine the corresponding event node
//if we're doing inheritance processing, we search through base contracts,
//from most derived (right) to most base (left)
//if we're not doing inheritance processing (i.e. if eventNode was passed),
//we search through *all* contracts, plus the top level! even though we
//know the event node already, we still need to know where it's defined
let node = undefined;
let definedInNode = undefined;
let definedIn = undefined;
let allocationMode = "full"; //degrade to abi as needed
debug("allocating ABI: %O", abiEntry);
if (contractNode) {
if (eventNode) {
node = eventNode; //we already know this one!
//note: we don't use findNodeAndContract here because it's meant for searching
//through a list of base contracts, that's not really what's going on here
//(we don't need all its code here anyway)
definedInNode = Object.values(referenceDeclarations).find(possibleContractNode => possibleContractNode.nodeType === "ContractDefinition" &&
possibleContractNode.nodes.some((possibleEventNode) => possibleEventNode.id === node.id));
if (definedInNode &&
definedInNode.contractKind === "library" &&
definedInNode.id !== contractNode.id) {
//skip library events! (unless this is the library they're from)
//those are always considered in-play no matter what,
//so we don't want to handle them here or we'd end up with them appearing twice
return undefined;
}
//if we failed to find what it's in... presumably it was defined at the file level.
//leave definedInNode undefined; it'll be handled below.
}
else {
//first: check same contract for the event
node = contractNode.nodes.find(eventNode => AbiDataUtils.definitionMatchesAbi(
//note this needn't actually be an event node, but then it will
//return false
abiEntry, eventNode, referenceDeclarations));
//if we found the node, great! If not...
if (node) {
definedInNode = contractNode;
}
else {
debug("didn't find node in base contract...");
//let's search for the node among the base contracts.
//but if we find it...
//[note: the following code is overcomplicated; it was used
//when we were trying to get the actual node, it's overcomplicated
//now that we're just determining its presence. oh well]
let linearizedBaseContractsMinusSelf = contractNode.linearizedBaseContracts.slice();
linearizedBaseContractsMinusSelf.shift(); //remove self
debug("checking contracts: %o", linearizedBaseContractsMinusSelf);
node = findNodeAndContract(linearizedBaseContractsMinusSelf, referenceDeclarations, eventNode => AbiDataUtils.definitionMatchesAbi(
//note this needn't actually be a event node, but then it will return false
abiEntry, eventNode, referenceDeclarations)
//don't pass deriveContractNode here, we're not checking the contract itself
).node; //may be undefined! that's OK!
if (node) {
//...if we find the node in an ancestor, we
//deliberately *don't* allocate! instead such cases
//will be handled during a later combination step
debug("bailing out for later handling!");
debug("ABI: %O", abiEntry);
return undefined;
}
}
}
}
//otherwise, leave node undefined
if (node) {
debug("found node");
//if we found the node, let's also turn it into a type
if (definedInNode) {
definedIn = (Ast.Import.definitionToStoredType(definedInNode, compilationId, compiler)); //can skip reference declarations argument here
}
else {
definedIn = null; //for file-level events, once they exist
}
//...and set the ID
id = (0, import_1.makeTypeId)(node.id, compilationId);
}
else {
//if no node, have to fall back into ABI mode
debug("falling back to ABI because no node");
allocationMode = "abi";
}
//now: construct the list of parameter types, attaching indexedness info
//and overall position (for later reconstruction)
let indexed;
let nonIndexed;
let abiAllocation; //the untransformed allocation for the non-indexed parameters
if (allocationMode === "full") {
nodeId = node.id.toString();
let parameters = node.parameters.parameters;
parameterTypes = parameters.map(definition => ({
//note: if node is defined, compiler had better be defined, too!
type: Ast.Import.definitionToType(definition, compilationId, compiler),
name: definition.name,
indexed: definition.indexed
}));
//now: split the list of parameters into indexed and non-indexed
[indexed, nonIndexed] = (0, partition_1.default)(parameterTypes, (parameter) => parameter.indexed);
try {
//now: perform the allocation for the non-indexed parameters!
abiAllocation = allocateMembers(nodeId, nonIndexed, userDefinedTypes, abiAllocations)[nodeId]; //note the implicit conversion from EventParameterInfo to NameTypePair
}
catch (_a) {
allocationMode = "abi";
}
}
if (allocationMode === "abi") {
//THIS IS DELIBERATELY NOT AN ELSE
nodeId = "-1"; //fake irrelevant ID
parameterTypes = abiEntry.inputs.map(abiParameter => ({
type: Import.abiParameterToType(abiParameter),
name: abiParameter.name,
indexed: abiParameter.indexed
}));
//now: split the list of parameters into indexed and non-indexed
[indexed, nonIndexed] = (0, partition_1.default)(parameterTypes, (parameter) => parameter.indexed);
//now: perform the allocation for the non-indexed parameters!
abiAllocation = allocateMembers(nodeId, nonIndexed, userDefinedTypes, abiAllocations)[nodeId]; //note the implicit conversion from EventParameterInfo to NameTypePair
}
//now: transform the result appropriately
const nonIndexedArgumentsAllocation = abiAllocation.members.map(member => (Object.assign(Object.assign({}, member), { pointer: {
location: "eventdata",
start: member.pointer.start,
length: member.pointer.length
} })));
//now: allocate the indexed parameters
const startingTopic = abiEntry.anonymous ? 0 : 1; //if not anonymous, selector takes up topic 0
const indexedArgumentsAllocation = indexed.map(({ type, name }, position) => ({
type,
name,
pointer: {
location: "eventtopic",
topic: startingTopic + position
}
}));
//finally: weave these back together
let argumentsAllocation = [];
for (let parameter of parameterTypes) {
let arrayToGrabFrom = parameter.indexed
? indexedArgumentsAllocation
: nonIndexedArgumentsAllocation;
argumentsAllocation.push(arrayToGrabFrom.shift()); //note that push and shift both modify!
}
//...and return
return {
abi: abiEntry,
contextHash: undefined,
definedIn,
id,
arguments: argumentsAllocation,
allocationMode,
anonymous: abiEntry.anonymous
};
}
function allocateError(abiEntry, errorNode, referenceDeclarations, userDefinedTypes, abiAllocations, compilationId, compiler) {
//first: if we got passed just a node & no abi entry,
let id = undefined;
let definedIn = undefined;
let parametersFull = undefined;
const parametersAbi = abiEntry.inputs;
if (errorNode) {
//first, set parametersFull
parametersFull = errorNode.parameters.parameters;
//now, set id
id = (0, import_1.makeTypeId)(errorNode.id, compilationId);
//now, set definedIn
let contractNode = null;