Added named parameters to tuple encoding.

This commit is contained in:
Richard Moore 2018-01-10 19:20:18 -05:00
parent 9aeb309d9d
commit eddf93b200
No known key found for this signature in database
GPG Key ID: 525F70A6FCABC295
3 changed files with 62 additions and 36 deletions

View File

@ -262,11 +262,27 @@ function alignSize(size) {
} }
function pack(coders, values) { function pack(coders, values) {
if (Array.isArray(values)) {
if (coders.length !== values.length) {
throwError('types/values mismatch', { type: type, values: values });
}
} else if (values && typeof(values) === 'object') {
var arrayValues = [];
coders.forEach(function(coder) {
arrayValues.push(values[coder.localName]);
});
values = arrayValues;
} else {
throwError('invalid value', { type: 'tuple', values: values });
}
var parts = []; var parts = [];
coders.forEach(function(coder, index) { coders.forEach(function(coder, index) {
parts.push({ dynamic: coder.dynamic, value: coder.encode(values[index]) }); parts.push({ dynamic: coder.dynamic, value: coder.encode(values[index]) });
}) });
var staticSize = 0, dynamicSize = 0; var staticSize = 0, dynamicSize = 0;
parts.forEach(function(part, index) { parts.forEach(function(part, index) {
@ -414,17 +430,6 @@ function coderTuple(coders, localName) {
name: 'tuple', name: 'tuple',
type: type, type: type,
encode: function(value) { encode: function(value) {
if (Array.isArray(value)) {
if (coders.length !== value.length) {
throwError('types/values mismatch', { type: type, values: values });
}
// @TODO: If receiving an object, and we have names, create the array
} else {
throwError('invalid value', { type: types, values: values });
}
return pack(coders, value); return pack(coders, value);
}, },
decode: function(data, offset) { decode: function(data, offset) {
@ -572,29 +577,27 @@ function Interface(abi) {
switch (method.type) { switch (method.type) {
case 'constructor': case 'constructor':
var func = (function() { var func = (function() {
// @TODO: Move to parseParams var inputParams = parseParams(method.inputs);
var inputTypes = getKeys(method.inputs, 'type');
var func = function(bytecode) { var func = function(bytecode) {
if (!utils.isHexString(bytecode)) { if (!utils.isHexString(bytecode)) {
throwError('invalid bytecode', {input: bytecode}); throwError('invalid bytecode', { input: bytecode });
} }
var params = Array.prototype.slice.call(arguments, 1); var params = Array.prototype.slice.call(arguments, 1);
if (params.length < inputTypes.length) { if (params.length < inputParams.types.length) {
throwError('missing parameter'); throwError('missing parameter');
} else if (params.length > inputTypes.length) { } else if (params.length > inputParams.types.length) {
throwError('too many parameters'); throwError('too many parameters');
} }
var result = { var result = {
bytecode: bytecode + Interface.encodeParams(inputTypes, params).substring(2), bytecode: bytecode + Interface.encodeParams(inputParams.names, inputParams.types, params).substring(2),
} }
return populateDescription(new DeployDescription(), result); return populateDescription(new DeployDescription(), result);
} }
// @TODO: Move to parseParams defineFrozen(func, 'inputs', inputParams);
defineFrozen(func, 'inputs', getKeys(method.inputs, 'name'));
return func; return func;
})(); })();
@ -608,7 +611,6 @@ function Interface(abi) {
var inputParams = parseParams(method.inputs); var inputParams = parseParams(method.inputs);
var outputParams = parseParams(method.outputs); var outputParams = parseParams(method.outputs);
var inputTypes = inputParams.types;
if (method.constant) { if (method.constant) {
var outputTypes = outputParams.types; var outputTypes = outputParams.types;
var outputNames = outputParams.names; var outputNames = outputParams.names;
@ -628,13 +630,13 @@ function Interface(abi) {
var params = Array.prototype.slice.call(arguments, 0); var params = Array.prototype.slice.call(arguments, 0);
if (params.length < inputTypes.length) { if (params.length < inputParams.types.length) {
throwError('missing parameter'); throwError('missing parameter');
} else if (params.length > inputTypes.length) { } else if (params.length > inputParams.types.length) {
throwError('too many parameters'); throwError('too many parameters');
} }
result.data = sighash + Interface.encodeParams(inputTypes, params).substring(2); result.data = sighash + Interface.encodeParams(inputParams.names, inputParams.types, params).substring(2);
if (method.constant) { if (method.constant) {
result.parse = function(data) { result.parse = function(data) {
return Interface.decodeParams( return Interface.decodeParams(
@ -649,9 +651,8 @@ function Interface(abi) {
return populateDescription(new TransactionDescription(), result); return populateDescription(new TransactionDescription(), result);
} }
// @TODO: Move the paraseParams defineFrozen(func, 'inputs', inputParams);
defineFrozen(func, 'inputs', getKeys(method.inputs, 'name')); defineFrozen(func, 'outputs', outputParams);
defineFrozen(func, 'outputs', getKeys(method.outputs, 'name'));
utils.defineProperty(func, 'signature', signature); utils.defineProperty(func, 'signature', signature);
utils.defineProperty(func, 'sighash', sighash); utils.defineProperty(func, 'sighash', sighash);
@ -803,12 +804,20 @@ function Interface(abi) {
} }
utils.defineProperty(Interface, 'encodeParams', function(types, values) { utils.defineProperty(Interface, 'encodeParams', function(names, types, values) {
// Names is optional, so shift over all the parameters if not provided
if (arguments.length < 3) {
values = types;
types = names;
names = null;
}
if (types.length !== values.length) { throwError('types/values mismatch', {types: types, values: values}); } if (types.length !== values.length) { throwError('types/values mismatch', {types: types, values: values}); }
var coders = []; var coders = [];
types.forEach(function(type) { types.forEach(function(type, index) {
coders.push(getParamCoder(type)); coders.push(getParamCoder(type, (names ? names[index]: undefined)));
}); });
return utils.hexlify(coderTuple(coders).encode(values)); return utils.hexlify(coderTuple(coders).encode(values));

View File

@ -1,6 +1,6 @@
{ {
"name": "ethers-contracts", "name": "ethers-contracts",
"version": "2.1.10", "version": "2.2.0",
"description": "Contract and Interface (ABI) library for Ethereum.", "description": "Contract and Interface (ABI) library for Ethereum.",
"bugs": { "bugs": {
"url": "http://github.com/ethers-io/ethers.js/issues", "url": "http://github.com/ethers-io/ethers.js/issues",

View File

@ -95,11 +95,11 @@ function equals(actual, expected) {
} }
function getValues(object, format) { function getValues(object, format, named) {
if (Array.isArray(object)) { if (Array.isArray(object)) {
var result = []; var result = [];
object.forEach(function(object) { object.forEach(function(object) {
result.push(getValues(object, format)); result.push(getValues(object, format, named));
}); });
return result; return result;
} }
@ -137,7 +137,15 @@ function getValues(object, format) {
return utils.arrayify(object.value); return utils.arrayify(object.value);
case 'tuple': case 'tuple':
return getValues(object.value, format); var result = getValues(object.value, format, named);
if (named) {
var namedResult = {};
result.forEach(function(value, index) {
namedResult['r' + String(index)] = value;
});
return namedResult;
}
return result;
default: default:
throw new Error('invalid type - ' + object.type); throw new Error('invalid type - ' + object.type);
@ -236,6 +244,7 @@ describe('Contract Interface ABI v2 Decoding', function() {
var Interface = require('../contracts/index.js').Interface; var Interface = require('../contracts/index.js').Interface;
var tests = utils.loadTests('contract-interface-abi2'); var tests = utils.loadTests('contract-interface-abi2');
tests.forEach(function(test) { tests.forEach(function(test) {
var values = getValues(JSON.parse(test.values)); var values = getValues(JSON.parse(test.values));
var types = JSON.parse(test.types); var types = JSON.parse(test.types);
@ -255,17 +264,24 @@ describe('Contract Interface ABI v2 Encoding', function() {
var tests = utils.loadTests('contract-interface-abi2'); var tests = utils.loadTests('contract-interface-abi2');
tests.forEach(function(test) { tests.forEach(function(test) {
var values = getValues(JSON.parse(test.values)); var values = getValues(JSON.parse(test.values));
var namedValues = getValues(JSON.parse(test.values), undefined, true);
var types = JSON.parse(test.types); var types = JSON.parse(test.types);
var expected = test.result; var expected = test.result;
var title = test.name + ' => (' + test.types + ') = (' + test.value + ')'; var title = test.name + ' => (' + test.types + ') = (' + test.value + ')';
it(('encodes parameters - ' + test.name + ' - ' + test.types), function() { it(('encodes ABIv2 parameters - ' + test.name + ' - ' + test.types), function() {
var encoded = Interface.encodeParams(types, values); var encoded = Interface.encodeParams(types, values);
assert.equal(encoded, expected, 'decoded parameters - ' + title); assert.equal(encoded, expected, 'encoded positional parameters - ' + title);
var contractInterface = new Interface(test.interface);
var outputNames = contractInterface.functions.test.outputs.names;
var namedEncoded = Interface.encodeParams(outputNames, types, values);
assert.equal(namedEncoded, expected, 'encoded named parameters - ' + title);
}); });
}); });
}); });
/*
describe('Contract Interface ABI v2 Named Decoding', function() { describe('Contract Interface ABI v2 Named Decoding', function() {
var Interface = require('../contracts').Interface; var Interface = require('../contracts').Interface;
@ -283,6 +299,7 @@ describe('Contract Interface ABI v2 Named Decoding', function() {
//console.dir(decoded, { depth: null }); //console.dir(decoded, { depth: null });
}); });
}); });
*/
describe('Test Contract Events', function() { describe('Test Contract Events', function() {
var Interface = require('../contracts').Interface; var Interface = require('../contracts').Interface;