Added ABI v2 coder to Interface (still experimental in Solidity though).
This commit is contained in:
parent
54c19dfb5a
commit
fe3ac0e94d
@ -58,8 +58,27 @@ function getKeys(params, key, allowEmpty) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function coderNumber(size, signed) {
|
||||
var coderNull = {
|
||||
name: 'null',
|
||||
type: '',
|
||||
encode: function(value) {
|
||||
return utils.arrayify([]);
|
||||
},
|
||||
decode: function(data, offset) {
|
||||
if (offset > data.length) { throw new Error('invalid null'); }
|
||||
return {
|
||||
consumed: 0,
|
||||
value: undefined
|
||||
}
|
||||
},
|
||||
dynamic: false
|
||||
};
|
||||
|
||||
function coderNumber(size, signed) {
|
||||
var name = ((signed ? 'int': 'uint') + size);
|
||||
return {
|
||||
name: name,
|
||||
type: name,
|
||||
encode: function(value) {
|
||||
value = utils.bigNumberify(value).toTwos(size * 8).maskn(size * 8);
|
||||
//value = value.toTwos(size * 8).maskn(size * 8);
|
||||
@ -89,6 +108,8 @@ function coderNumber(size, signed) {
|
||||
var uint256Coder = coderNumber(32, false);
|
||||
|
||||
var coderBoolean = {
|
||||
name: 'boolean',
|
||||
type: 'boolean',
|
||||
encode: function(value) {
|
||||
return uint256Coder.encode(value ? 1: 0);
|
||||
},
|
||||
@ -102,7 +123,10 @@ var coderBoolean = {
|
||||
}
|
||||
|
||||
function coderFixedBytes(length) {
|
||||
var name = ('bytes' + length);
|
||||
return {
|
||||
name: name,
|
||||
type: name,
|
||||
encode: function(value) {
|
||||
value = utils.arrayify(value);
|
||||
if (length === 32) { return value; }
|
||||
@ -123,6 +147,8 @@ function coderFixedBytes(length) {
|
||||
}
|
||||
|
||||
var coderAddress = {
|
||||
name: 'address',
|
||||
type: 'address',
|
||||
encode: function(value) {
|
||||
value = utils.arrayify(utils.getAddress(value));
|
||||
var result = new Uint8Array(32);
|
||||
@ -163,6 +189,8 @@ function _decodeDynamicBytes(data, offset) {
|
||||
}
|
||||
|
||||
var coderDynamicBytes = {
|
||||
name: 'bytes',
|
||||
type: 'bytes',
|
||||
encode: function(value) {
|
||||
return _encodeDynamicBytes(utils.arrayify(value));
|
||||
},
|
||||
@ -175,6 +203,8 @@ var coderDynamicBytes = {
|
||||
};
|
||||
|
||||
var coderString = {
|
||||
name: 'string',
|
||||
type: 'string',
|
||||
encode: function(value) {
|
||||
return _encodeDynamicBytes(utils.toUtf8Bytes(value));
|
||||
},
|
||||
@ -186,24 +216,106 @@ var coderString = {
|
||||
dynamic: true
|
||||
};
|
||||
|
||||
function coderArray(coder, length) {
|
||||
function alignSize(size) {
|
||||
return parseInt(32 * Math.ceil(size / 32));
|
||||
}
|
||||
|
||||
function pack(coders, values) {
|
||||
var parts = [];
|
||||
|
||||
coders.forEach(function(coder, index) {
|
||||
parts.push({ dynamic: coder.dynamic, value: coder.encode(values[index]) });
|
||||
})
|
||||
|
||||
var staticSize = 0, dynamicSize = 0;
|
||||
parts.forEach(function(part, index) {
|
||||
if (part.dynamic) {
|
||||
staticSize += 32;
|
||||
dynamicSize += alignSize(part.value.length);
|
||||
} else {
|
||||
staticSize += alignSize(part.value.length);
|
||||
}
|
||||
});
|
||||
|
||||
var offset = 0, dynamicOffset = staticSize;
|
||||
var data = new Uint8Array(staticSize + dynamicSize);
|
||||
|
||||
parts.forEach(function(part, index) {
|
||||
if (part.dynamic) {
|
||||
//uint256Coder.encode(dynamicOffset).copy(data, offset);
|
||||
data.set(uint256Coder.encode(dynamicOffset), offset);
|
||||
offset += 32;
|
||||
|
||||
//part.value.copy(data, dynamicOffset); @TODO
|
||||
data.set(part.value, dynamicOffset);
|
||||
dynamicOffset += alignSize(part.value.length);
|
||||
} else {
|
||||
//part.value.copy(data, offset); @TODO
|
||||
data.set(part.value, offset);
|
||||
offset += alignSize(part.value.length);
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
function unpack(coders, data, offset) {
|
||||
var baseOffset = offset;
|
||||
var consumed = 0;
|
||||
var value = [];
|
||||
|
||||
coders.forEach(function(coder) {
|
||||
if (coder.dynamic) {
|
||||
var dynamicOffset = uint256Coder.decode(data, offset);
|
||||
var result = coder.decode(data, baseOffset + dynamicOffset.value.toNumber());
|
||||
// The dynamic part is leap-frogged somewhere else; doesn't count towards size
|
||||
result.consumed = dynamicOffset.consumed;
|
||||
} else {
|
||||
var result = coder.decode(data, offset);
|
||||
}
|
||||
|
||||
if (result.value != undefined) {
|
||||
value.push(result.value);
|
||||
}
|
||||
|
||||
offset += result.consumed;
|
||||
consumed += result.consumed;
|
||||
});
|
||||
|
||||
return {
|
||||
value: value,
|
||||
consumed: consumed
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function coderArray(coder, length) {
|
||||
var type = (coder.type + '[' + (length >= 0 ? length: '') + ']');
|
||||
|
||||
return {
|
||||
coder: coder,
|
||||
length: length,
|
||||
name: 'array',
|
||||
type: type,
|
||||
encode: function(value) {
|
||||
if (!Array.isArray(value)) { throwError('invalid array'); }
|
||||
|
||||
var count = length;
|
||||
|
||||
var result = new Uint8Array(0);
|
||||
if (length === -1) {
|
||||
length = value.length;
|
||||
result = uint256Coder.encode(length);
|
||||
if (count === -1) {
|
||||
count = value.length;
|
||||
result = uint256Coder.encode(count);
|
||||
}
|
||||
|
||||
if (length !== value.length) { throwError('size mismatch'); }
|
||||
if (count !== value.length) { throwError('size mismatch'); }
|
||||
|
||||
value.forEach(function(value) {
|
||||
result = utils.concat([result, coder.encode(value)]);
|
||||
});
|
||||
var coders = [];
|
||||
value.forEach(function(value) { coders.push(coder); });
|
||||
|
||||
return result;
|
||||
return utils.concat([result, pack(coders, value)]);
|
||||
},
|
||||
decode: function(data, offset) {
|
||||
// @TODO:
|
||||
@ -211,98 +323,142 @@ function coderArray(coder, length) {
|
||||
|
||||
var consumed = 0;
|
||||
|
||||
var result;
|
||||
if (length === -1) {
|
||||
result = uint256Coder.decode(data, offset);
|
||||
length = result.value.toNumber();
|
||||
consumed += result.consumed;
|
||||
offset += result.consumed;
|
||||
var count = length;
|
||||
|
||||
if (count === -1) {
|
||||
var decodedLength = uint256Coder.decode(data, offset);
|
||||
count = decodedLength.value.toNumber();
|
||||
consumed += decodedLength.consumed;
|
||||
offset += decodedLength.consumed;
|
||||
}
|
||||
|
||||
var value = [];
|
||||
var coders = [];
|
||||
for (var i = 0; i < count; i++) { coders.push(coder); }
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var result = coder.decode(data, offset);
|
||||
consumed += result.consumed;
|
||||
offset += result.consumed;
|
||||
value.push(result.value);
|
||||
var result = unpack(coders, data, offset);
|
||||
result.consumed += consumed;
|
||||
return result;
|
||||
},
|
||||
dynamic: (length === -1 || coder.dynamic)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function coderTuple(coders) {
|
||||
var dynamic = false;
|
||||
var types = [];
|
||||
coders.forEach(function(coder) {
|
||||
if (coder.dynamic) { dynamic = true; }
|
||||
types.push(coder.type);
|
||||
});
|
||||
|
||||
var type = ('tuple(' + types.join(',') + ')');
|
||||
|
||||
return {
|
||||
consumed: consumed,
|
||||
value: value,
|
||||
coders: coders,
|
||||
name: 'tuple',
|
||||
type: type,
|
||||
encode: function(value) {
|
||||
|
||||
if (coders.length !== coders.length) {
|
||||
throwError('types/values mismatch', { type: type, values: values });
|
||||
}
|
||||
|
||||
return pack(coders, value);
|
||||
},
|
||||
dynamic: (length === -1)
|
||||
}
|
||||
decode: function(data, offset) {
|
||||
return unpack(coders, data, offset);
|
||||
},
|
||||
dynamic: dynamic
|
||||
};
|
||||
}
|
||||
|
||||
// Break the type up into [staticType][staticArray]*[dynamicArray]? | [dynamicType] and
|
||||
// build the coder up from its parts
|
||||
var paramTypePart = new RegExp(/^((u?int|bytes)([0-9]*)|(address|bool|string)|(\[([0-9]*)\]))/);
|
||||
function getTypes(coders) {
|
||||
var type = coderTuple(coders).type;
|
||||
return type.substring(6, type.length - 1);
|
||||
}
|
||||
|
||||
function splitNesting(value) {
|
||||
var result = [];
|
||||
var accum = '';
|
||||
var depth = 0;
|
||||
for (var offset = 0; offset < value.length; offset++) {
|
||||
var c = value[offset];
|
||||
if (c === ',' && depth === 0) {
|
||||
result.push(accum);
|
||||
accum = '';
|
||||
} else {
|
||||
accum += c;
|
||||
if (c === '(') {
|
||||
depth++;
|
||||
} else if (c === ')') {
|
||||
depth--;
|
||||
if (depth === -1) {
|
||||
throw new Error('unbalanced parenthsis');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push(accum);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var paramTypeBytes = new RegExp(/^bytes([0-9]*)$/);
|
||||
var paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
|
||||
var paramTypeArray = new RegExp(/^(.*)\[([0-9]*)\]$/);
|
||||
var paramTypeSimple = {
|
||||
address: coderAddress,
|
||||
bool: coderBoolean,
|
||||
string: coderString,
|
||||
bytes: coderDynamicBytes,
|
||||
};
|
||||
|
||||
function getParamCoder(type) {
|
||||
var coder = null;
|
||||
while (type) {
|
||||
var part = type.match(paramTypePart);
|
||||
if (!part) { throwError('invalid type', { type: type }); }
|
||||
type = type.substring(part[0].length);
|
||||
|
||||
var prefix = (part[2] || part[4] || part[5]);
|
||||
switch (prefix) {
|
||||
case 'int': case 'uint':
|
||||
if (coder) { throwError('invalid type', { type: type }); }
|
||||
var size = parseInt(part[3] || 256);
|
||||
var coder = paramTypeSimple[type];
|
||||
if (coder) { return coder; }
|
||||
|
||||
var match = type.match(paramTypeNumber);
|
||||
if (match) {
|
||||
var size = parseInt(match[2] || 256);
|
||||
if (size === 0 || size > 256 || (size % 8) !== 0) {
|
||||
throwError('invalid type', { type: type });
|
||||
}
|
||||
coder = coderNumber(size / 8, (prefix === 'int'));
|
||||
break;
|
||||
return coderNumber(size / 8, (match[1] === 'int'));
|
||||
}
|
||||
|
||||
case 'bool':
|
||||
if (coder) { throwError('invalid type', { type: type }); }
|
||||
coder = coderBoolean;
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
if (coder) { throwError('invalid type', { type: type }); }
|
||||
coder = coderString;
|
||||
break;
|
||||
|
||||
case 'bytes':
|
||||
if (coder) { throwError('invalid type', { type: type }); }
|
||||
if (part[3]) {
|
||||
var size = parseInt(part[3]);
|
||||
var match = type.match(paramTypeBytes);
|
||||
if (match) {
|
||||
var size = parseInt(match[1]);
|
||||
if (size === 0 || size > 32) {
|
||||
throwError('invalid type ' + type);
|
||||
}
|
||||
coder = coderFixedBytes(size);
|
||||
} else {
|
||||
coder = coderDynamicBytes;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'address':
|
||||
if (coder) { throwError('invalid type', { type: type }); }
|
||||
coder = coderAddress;
|
||||
break;
|
||||
|
||||
case '[]':
|
||||
if (!coder || coder.dynamic) { throwError('invalid type', { type: type }); }
|
||||
coder = coderArray(coder, -1);
|
||||
break;
|
||||
|
||||
// "[0-9+]"
|
||||
default:
|
||||
if (!coder || coder.dynamic) { throwError('invalid type', { type: type }); }
|
||||
var size = parseInt(part[6]);
|
||||
coder = coderArray(coder, size);
|
||||
}
|
||||
return coderFixedBytes(size);
|
||||
}
|
||||
|
||||
if (!coder) { throwError('invalid type'); }
|
||||
return coder;
|
||||
var match = type.match(paramTypeArray);
|
||||
if (match) {
|
||||
var size = parseInt(match[2] || -1);
|
||||
return coderArray(getParamCoder(match[1]), size);
|
||||
}
|
||||
|
||||
if (type.substring(0, 6) === 'tuple(' && type.substring(type.length - 1) === ')') {
|
||||
var coders = [];
|
||||
splitNesting(type.substring(6, type.length - 1)).forEach(function(type) {
|
||||
coders.push(getParamCoder(type));
|
||||
});
|
||||
return coderTuple(coders);
|
||||
}
|
||||
|
||||
if (type === '') {
|
||||
return coderNull;
|
||||
}
|
||||
|
||||
throwError('invalid type', { type: type });
|
||||
}
|
||||
|
||||
|
||||
function populateDescription(object, items) {
|
||||
for (var key in items) {
|
||||
utils.defineProperty(object, key, items[key]);
|
||||
@ -525,46 +681,12 @@ function Interface(abi) {
|
||||
utils.defineProperty(Interface, 'encodeParams', function(types, values) {
|
||||
if (types.length !== values.length) { throwError('types/values mismatch', {types: types, values: values}); }
|
||||
|
||||
var parts = [];
|
||||
|
||||
types.forEach(function(type, index) {
|
||||
var coder = getParamCoder(type);
|
||||
parts.push({dynamic: coder.dynamic, value: coder.encode(values[index])});
|
||||
})
|
||||
|
||||
function alignSize(size) {
|
||||
return parseInt(32 * Math.ceil(size / 32));
|
||||
}
|
||||
|
||||
var staticSize = 0, dynamicSize = 0;
|
||||
parts.forEach(function(part) {
|
||||
if (part.dynamic) {
|
||||
staticSize += 32;
|
||||
dynamicSize += alignSize(part.value.length);
|
||||
} else {
|
||||
staticSize += alignSize(part.value.length);
|
||||
}
|
||||
var coders = [];
|
||||
types.forEach(function(type) {
|
||||
coders.push(getParamCoder(type));
|
||||
});
|
||||
|
||||
var offset = 0, dynamicOffset = staticSize;
|
||||
var data = new Uint8Array(staticSize + dynamicSize);
|
||||
|
||||
parts.forEach(function(part, index) {
|
||||
if (part.dynamic) {
|
||||
//uint256Coder.encode(dynamicOffset).copy(data, offset);
|
||||
data.set(uint256Coder.encode(dynamicOffset), offset);
|
||||
offset += 32;
|
||||
|
||||
//part.value.copy(data, dynamicOffset); @TODO
|
||||
data.set(part.value, dynamicOffset);
|
||||
dynamicOffset += alignSize(part.value.length);
|
||||
} else {
|
||||
//part.value.copy(data, offset); @TODO
|
||||
data.set(part.value, offset);
|
||||
offset += alignSize(part.value.length);
|
||||
}
|
||||
});
|
||||
return utils.hexlify(data);
|
||||
return utils.hexlify(coderTuple(coders).encode(values));
|
||||
});
|
||||
|
||||
|
||||
@ -580,47 +702,35 @@ utils.defineProperty(Interface, 'decodeParams', function(names, types, data) {
|
||||
}
|
||||
|
||||
data = utils.arrayify(data);
|
||||
|
||||
var coders = [];
|
||||
types.forEach(function(type) {
|
||||
coders.push(getParamCoder(type));
|
||||
});
|
||||
|
||||
var result = coderTuple(coders).decode(data, 0);
|
||||
|
||||
// @TODO: Move this into coderTuple
|
||||
var values = new Result();
|
||||
|
||||
var offset = 0;
|
||||
types.forEach(function(type, index) {
|
||||
var coder = getParamCoder(type);
|
||||
if (coder.dynamic) {
|
||||
var dynamicOffset = uint256Coder.decode(data, offset);
|
||||
var result = coder.decode(data, dynamicOffset.value.toNumber());
|
||||
offset += dynamicOffset.consumed;
|
||||
} else {
|
||||
var result = coder.decode(data, offset);
|
||||
offset += result.consumed;
|
||||
}
|
||||
|
||||
// Add indexed parameter
|
||||
values[index] = result.value;
|
||||
|
||||
// Add named parameters
|
||||
if (names[index]) {
|
||||
coders.forEach(function(coder, index) {
|
||||
values[index] = result.value[index];
|
||||
if (names && names[index]) {
|
||||
var name = names[index];
|
||||
|
||||
// We reserve length to make the Result object arrayish
|
||||
if (name === 'length') {
|
||||
console.log('WARNING: result length renamed to _length');
|
||||
name = '_length';
|
||||
}
|
||||
|
||||
if (values[name] == null) {
|
||||
values[name] = result.value;
|
||||
values[name] = values[index];
|
||||
} else {
|
||||
console.log('WARNING: duplicate value - ' + name);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
values.length = types.length;
|
||||
|
||||
return values;
|
||||
});
|
||||
|
||||
//utils.defineProperty(Interface, 'getDeployTransaction', function(bytecode) {
|
||||
//});
|
||||
|
||||
module.exports = Interface;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ethers-contracts",
|
||||
"version": "2.1.3",
|
||||
"version": "2.1.4",
|
||||
"description": "Contract and Interface (ABI) library for Ethereum.",
|
||||
"bugs": {
|
||||
"url": "http://github.com/ethers-io/ethers.js/issues",
|
||||
|
@ -1,19 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
// See: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
|
||||
|
||||
var crypto = require('crypto');
|
||||
var fs = require('fs');
|
||||
|
||||
var BN = require('bn.js');
|
||||
var solc = require('solc');
|
||||
var ethereumVm = require('ethereumjs-vm');
|
||||
var ethereumUtil = require('ethereumjs-util');
|
||||
var promiseRationing = require('promise-rationing');
|
||||
var Web3 = require('web3');
|
||||
|
||||
var contracts = require('../../contracts/index.js');
|
||||
|
||||
var bigNumber = require('../../utils/bignumber.js');
|
||||
var convert = require('../../utils/convert.js');
|
||||
var getAddress = require('../../utils/address.js').getAddress;
|
||||
var arrayify = require('../../utils/convert').arrayify;
|
||||
|
||||
var utils = require('./utils.js');
|
||||
var utils = require('../utils.js');
|
||||
|
||||
|
||||
function addLog(message) {
|
||||
fs.appendFileSync('make-contract-interface.log', message + '\n');
|
||||
}
|
||||
|
||||
function id(text) {
|
||||
return crypto.createHash('sha256').update(text).digest().toString('hex').substring(0, 10).toUpperCase();
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', function(reason, p){
|
||||
console.log('Error: Unhandled promise rejection');
|
||||
console.log(reason);
|
||||
});
|
||||
|
||||
|
||||
|
||||
var compile = (function() {
|
||||
var soljson = require('../soljson.js');
|
||||
var _compile = soljson.cwrap("compileJSONCallback", "string", ["string", "number", "number"]);
|
||||
|
||||
function compile(source) {
|
||||
return JSON.parse(_compile(JSON.stringify({sources: { "demo.sol": source }}), 0));
|
||||
}
|
||||
compile.version = JSON.parse(compile('contract Foo { }').contracts['demo.sol:Foo'].metadata).compiler.version
|
||||
return compile;
|
||||
})();
|
||||
|
||||
// Create the indent given a tabstop
|
||||
function indent(tabs) {
|
||||
@ -23,6 +49,18 @@ function indent(tabs) {
|
||||
}
|
||||
|
||||
function recursiveHexlify(object) {
|
||||
if (object.type === 'tuple') {
|
||||
var result = [];
|
||||
object.value.forEach(function(object) {
|
||||
result.push(recursiveHexlify(object));
|
||||
});
|
||||
return {type: 'tuple', value: result};
|
||||
}
|
||||
|
||||
if (object.type && object.value != null) {
|
||||
object = object.value;
|
||||
}
|
||||
|
||||
if (typeof(object) === 'number') {
|
||||
object = new BN(object);
|
||||
}
|
||||
@ -45,12 +83,13 @@ function recursiveHexlify(object) {
|
||||
|
||||
} else if (Buffer.isBuffer(object)) {
|
||||
return {type: 'buffer', value: utils.hexlify(object)};
|
||||
|
||||
}
|
||||
|
||||
throw new Error('unsupported type - ' + object + ' ' + typeof(object));
|
||||
}
|
||||
|
||||
var web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545'));
|
||||
var web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8549'));
|
||||
|
||||
/**
|
||||
*
|
||||
@ -62,108 +101,210 @@ function getValue(value) {
|
||||
} else if (BN.isBN(value)) {
|
||||
value = value.toString(10);
|
||||
} else if (typeof(value) !== 'string' && typeof(value) !== 'number' && typeof(value) !== 'boolean') {
|
||||
console.dir(value, { depth: null });
|
||||
throw new Error('invalid type - ' + value + ' ' + typeof(value));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function createContractOutput(types, values) {
|
||||
var source = 'contract Test {\n';
|
||||
source += ' function test() constant returns (' + types.join(', ') + ') {\n';
|
||||
|
||||
var returns = [];
|
||||
for (var i = 0; i < types.length; i++) {
|
||||
var name = String.fromCharCode(97 + i);
|
||||
|
||||
|
||||
function getName(depth) {
|
||||
return String.fromCharCode(97 + depth);
|
||||
}
|
||||
|
||||
function getStructName(types) {
|
||||
return 'Struct' + id('struct(' + types.join(',') + ')');
|
||||
}
|
||||
|
||||
function getStructSource(types) {
|
||||
var source = '';
|
||||
types.forEach(function(type, index) {
|
||||
var name = getName(index);
|
||||
source += indent(2) + type + ' ' + name + ';\n';
|
||||
});
|
||||
return (indent(1) + 'struct ' + getStructName(types) + ' {\n' + source + indent(1) + '}\n');
|
||||
}
|
||||
|
||||
function populate(name, value, depth, info) {
|
||||
|
||||
value.localName = name;
|
||||
if (value.type === 'tuple') {
|
||||
info.pragmas['experimental ABIEncoderV2'] = true;
|
||||
|
||||
var source = '';
|
||||
var types = [];
|
||||
value.value.forEach(function(value, index) {
|
||||
var localName = getName(index);
|
||||
populate(name + '.' + localName, value, depth + 1, info);
|
||||
|
||||
types.push(value.name);
|
||||
});
|
||||
|
||||
if (!value.struct) {
|
||||
value.struct = getStructSource(types);
|
||||
}
|
||||
|
||||
info.structs[value.struct] = true;
|
||||
|
||||
} else if (Array.isArray(value.value)) {
|
||||
|
||||
if (value.type.substring(value.type.length - 2) === '[]') {
|
||||
info.inits.push(indent(2) + value.localName + ' = new ' + value.name + '(' + value.value.length + ');\n');
|
||||
value.dynamic = true;
|
||||
}
|
||||
|
||||
value.value.forEach(function(value, index) {
|
||||
populate(name + '[' + String(index) + ']', value, depth + 1, info);
|
||||
|
||||
if (value.dynamic) {
|
||||
info.pragmas['experimental ABIEncoderV2'] = true;
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
if (value.type === 'string' || value.type === 'bytes') {
|
||||
value.dynamic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createContractSource(values, info, comments) {
|
||||
|
||||
var pragmas = { 'solidity ^0.4.18': true };
|
||||
|
||||
var _getName = -1;
|
||||
var getName = function() {
|
||||
_getName++;
|
||||
return String.fromCharCode(97 + parseInt(_getName / 26)) + String.fromCharCode(97 + (_getName % 26));
|
||||
}
|
||||
|
||||
var source = '';
|
||||
|
||||
var returnTypes = [];
|
||||
values.forEach(function(value, index) {
|
||||
returnTypes.push(value.name + ' ' + value.localName);
|
||||
});
|
||||
|
||||
var temp = false;
|
||||
|
||||
function dumpValue(value) {
|
||||
|
||||
// Tuple
|
||||
if (value.type === 'tuple') {
|
||||
value.value.forEach(function(value) {
|
||||
dumpValue(value);
|
||||
});
|
||||
|
||||
// Array type; do a deep copy
|
||||
if (types[i].indexOf('[') >= 0) {
|
||||
|
||||
// Each count (or optionally empty) array type
|
||||
var arrays = types[i].match(/\[[0-9]*\]/g);
|
||||
|
||||
// Allocate the space (only dynamic arrays require new)
|
||||
source += indent(2) + types[i] + ' memory ' + name;
|
||||
if (arrays[arrays.length - 1] === '[]') {
|
||||
source += ' = new ' + types[i] + '(' + values[i].length+ ')';
|
||||
}
|
||||
source +=';\n';
|
||||
|
||||
var baseType = types[i].substring(0, types[i].indexOf('['));
|
||||
|
||||
function recursiveSet(item, indices) {
|
||||
if (Array.isArray(item)) {
|
||||
item.forEach(function(item, index) {
|
||||
var i = indices.slice();
|
||||
i.unshift(index);
|
||||
recursiveSet(item, i);
|
||||
} else if (value.type.indexOf('[') >= 0) {
|
||||
value.value.forEach(function(value) {
|
||||
dumpValue(value);
|
||||
});
|
||||
} else {
|
||||
var loc = '';
|
||||
indices.forEach(function(index) {
|
||||
loc = '[' + index + ']' + loc;
|
||||
})
|
||||
|
||||
item = getValue(item);
|
||||
|
||||
//if (item instanceof BN) { item = item.toString(10); }
|
||||
source += indent(2) + name + loc + ' = ' + baseType + '(' + item + ');\n';
|
||||
}
|
||||
}
|
||||
recursiveSet(values[i], []);
|
||||
|
||||
// Dynamic type: bytes
|
||||
} else if (types[i] === 'bytes') {
|
||||
source += indent(2) + 'bytes memory ' + name + ' = new bytes(' + values[i].length + ');\n';
|
||||
} else if (value.type === 'bytes') {
|
||||
if (!temp) {
|
||||
source += indent(2) + 'bytes memory temp ';
|
||||
temp = true;
|
||||
} else {
|
||||
source += indent(2) + 'temp ';
|
||||
}
|
||||
source += '= new bytes(' + value.value.length + ');\n';
|
||||
|
||||
source += indent(2) + value.localName + ' = temp;\n';
|
||||
source += indent(2) + 'assembly {\n'
|
||||
source += indent(3) + 'mstore(' + name + ', ' + values[i].length + ')\n';
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
source += indent(3) + 'mstore8(add(' + name + ', ' + (32 + j) + '), ' + values[i][j] + ')\n';
|
||||
source += indent(3) + 'mstore(temp, ' + value.value.length + ')\n';
|
||||
for (var i = 0; i < value.value.length; i++) {
|
||||
source += indent(3) + 'mstore8(add(temp, ' + (32 + i) + '), ' + value.value[i] + ')\n';
|
||||
}
|
||||
source += indent(2) + '}\n'
|
||||
/*
|
||||
var value = '';
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
value += '\\' + 'x' + values[i].slice(j, j + 1).toString('hex');
|
||||
}
|
||||
source += ' bytes memory ' + name + ' = "' + value + '";\n';
|
||||
*/
|
||||
|
||||
// Dynamic type: string
|
||||
} else if (types[i] === 'string') {
|
||||
source += ' string memory ' + name + ' = "' + values[i] + '";\n';
|
||||
} else if (value.type === 'string') {
|
||||
source += indent(2) + value.localName + ' = "' + value.value + '";\n';
|
||||
|
||||
// Static type; just use the stack
|
||||
} else {
|
||||
var value = getValue(values[i]);
|
||||
source += ' ' + types[i] + ' ' + name + ' = ' + types[i] + '(' + value + ');\n';
|
||||
var v = value.value;
|
||||
if (Buffer.isBuffer(v)) { v = '0x' + v.toString('hex'); }
|
||||
source += indent(2) + value.localName + ' = ' + value.type + '(' + v + ');\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Track the name to return
|
||||
returns.push(name);
|
||||
// Recursively (if necessary) set the parameter value
|
||||
values.forEach(function(value) {
|
||||
dumpValue(value);
|
||||
});
|
||||
|
||||
// Pragmas
|
||||
var sourcePragma = '';
|
||||
Object.keys(info.pragmas).forEach(function(pragma) {
|
||||
sourcePragma += 'pragma ' + pragma + ';\n';
|
||||
});
|
||||
if (sourcePragma.length) { sourcePragma += '\n'; }
|
||||
|
||||
// Structs
|
||||
var sourceStructs = '';
|
||||
Object.keys(info.structs).forEach(function(struct) {
|
||||
sourceStructs += struct + '\n';
|
||||
});
|
||||
|
||||
// Initialization code
|
||||
var sourceInit = '';
|
||||
info.inits.forEach(function(init) {
|
||||
sourceInit += init;
|
||||
});
|
||||
if (sourceInit.length) { sourceInit += '\n'; }
|
||||
|
||||
var sourceComments = '';
|
||||
comments.forEach(function(comment) { sourceComments += '// ' + comment + '\n'; });
|
||||
if (sourceComments.length) { sourceComments += ' \n'; }
|
||||
|
||||
return [
|
||||
sourceComments,
|
||||
sourcePragma,
|
||||
'contract Test {\n',
|
||||
sourceStructs,
|
||||
(indent(1) + 'function test() pure returns (' + returnTypes.join(', ') + ') {\n'),
|
||||
sourceInit,
|
||||
source,
|
||||
(indent(1) + '}\n'),
|
||||
'}\n',
|
||||
].join('');
|
||||
}
|
||||
|
||||
// Return the values
|
||||
source += ' return (' + returns.join(', ') + ');\n';
|
||||
|
||||
source += ' }\n';
|
||||
source += '}\n';
|
||||
|
||||
function compileContract(source, ignoreErrors) {
|
||||
try {
|
||||
var contract = solc.compile(source, 0);
|
||||
contract = contract.contracts.Test;
|
||||
var contracts = compile(source);
|
||||
contracts.errors.forEach(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
var contract = contracts.contracts['demo.sol:Test'];
|
||||
if (!contract && ignoreErrors) {
|
||||
addLog(source);
|
||||
contracts.errors.forEach(function(error) {
|
||||
addLog(error);
|
||||
});
|
||||
addLog('======');
|
||||
return null;
|
||||
}
|
||||
contract.sourceCode = source;
|
||||
contract.version = JSON.parse(contract.metadata).compiler.version;
|
||||
return contract;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.log('Failed to compile ========');
|
||||
console.log({types: types, values: values, contract: contract});
|
||||
//console.log({types: types, values: values, contract: contract});
|
||||
console.log(source);
|
||||
console.log('========');
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
var Address = '0xbe764deeec446f1c6e9d4c891b0f87148a2f9a00';
|
||||
//var Address = '0xbe764deeec446f1c6e9d4c891b0f87148a2f9a00';
|
||||
|
||||
var Output = [];
|
||||
//var Output = [];
|
||||
|
||||
function web3Promise(method, params) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
@ -196,12 +337,50 @@ function sendTransaction(transaction) {
|
||||
});
|
||||
}
|
||||
|
||||
function makeTests() {
|
||||
|
||||
function _check(name, types, values, normalizedValues) {
|
||||
if (!normalizedValues) { normalizedValues = values; }
|
||||
function _check(name, values, info) {
|
||||
var test = JSON.stringify(values);
|
||||
|
||||
// Recursively augment the values
|
||||
if (!info.inits) { info.inits = []; }
|
||||
if (!info.structs) { info.structs = { }; }
|
||||
if (!info.pragmas) { info.pragmas = { }; }
|
||||
info.pragmas[ 'solidity ^0.4.18'] = true;
|
||||
|
||||
values.forEach(function(value, index) {
|
||||
populate('r' + index, value, 0, info)
|
||||
});
|
||||
|
||||
function getTypes(result, value) {
|
||||
value.forEach(function(value) {
|
||||
if (value.type === 'tuple') {
|
||||
result.push('tuple(' + getTypes([], value.value).join(',') + ')');
|
||||
} else {
|
||||
result.push(value.type);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
var types = getTypes([], values);
|
||||
|
||||
var source = createContractSource(values, info, [
|
||||
('Test: ' + name),
|
||||
('Comnpiler: ' + compile.version),
|
||||
test
|
||||
]);
|
||||
// MOO
|
||||
//console.log(source);
|
||||
//return Promise.resolve();
|
||||
|
||||
var contract = compileContract(source, true);
|
||||
if (!contract) {
|
||||
console.log('Skipping:', test)
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (!contract) { throw new Error('invalid version'); }
|
||||
|
||||
var contract = createContractOutput(types, values);
|
||||
var transaction = { data: '0x' + contract.bytecode };
|
||||
|
||||
return sendTransaction(transaction).then(function(hash) {
|
||||
@ -242,17 +421,30 @@ function makeTests() {
|
||||
source: contract.sourceCode,
|
||||
types: JSON.stringify(types),
|
||||
values: JSON.stringify(recursiveHexlify(values)),
|
||||
normalizedValues: JSON.stringify(recursiveHexlify(normalizedValues)),
|
||||
version: contract.version,
|
||||
// normalizedValues: JSON.stringify(recursiveHexlify(normalizedValues)),
|
||||
};
|
||||
|
||||
return output;
|
||||
});
|
||||
}
|
||||
|
||||
function makeTests() {
|
||||
|
||||
var promiseFuncs = [];
|
||||
|
||||
function check(name, types, values, normalizedValues) {
|
||||
if (normalizedValues == null) { normalizedValues = values; }
|
||||
promiseFuncs.push(function(resolve, reject) {
|
||||
_check(name, types, values, normalizedValues).then(function(result) {
|
||||
var test = [ ];
|
||||
types.forEach(function(type, index) {
|
||||
test.push({
|
||||
type: type,
|
||||
normalizedValue: normalizedValues[index],
|
||||
value: values[index]
|
||||
});
|
||||
});
|
||||
_check(name, test).then(function(result) {
|
||||
resolve(result);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
@ -260,6 +452,7 @@ function makeTests() {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Test cases: https://github.com/ethereum/solidity.js/blob/master/test/coder.decodeParam.js
|
||||
check('sol-1', ['int'], [new BN(1)]);
|
||||
check('sol-2', ['int'], [new BN(16)]);
|
||||
@ -517,7 +710,7 @@ function makeTests() {
|
||||
for (var j = 0; j < count; j++) {
|
||||
var type = randomTypeValue('type-' + i + '-' + j);
|
||||
types.push(type.type);
|
||||
var value = type.value();
|
||||
var value = type.value('test-' + j);
|
||||
values.push(value.value);
|
||||
normalized.push(value.normalized);
|
||||
}
|
||||
@ -528,10 +721,323 @@ function makeTests() {
|
||||
// check('', ['uint8[4][]'], [ [] ]);
|
||||
|
||||
promiseRationing.all(promiseFuncs, 100).then(function(result) {
|
||||
utils.saveTestcase('contract-interface', result);
|
||||
//utils.saveTestcase('contract-interface', result);
|
||||
}, function(error) {
|
||||
console.log('ERROR', error);
|
||||
});
|
||||
}
|
||||
|
||||
makeTests();
|
||||
function makeTestsAbi2() {
|
||||
|
||||
var promiseFuncs = [];
|
||||
|
||||
function check(name, values, info) {
|
||||
promiseFuncs.push(function(resolve, reject) {
|
||||
_check(name, values, info).then(function(result) {
|
||||
resolve(result);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var address = '0x0123456789012345678901234567890123456789';
|
||||
var longText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||
|
||||
/*
|
||||
// Some hand-coded (white-box test cases)
|
||||
check('abi2-basic-test', [
|
||||
{ type: 'address', value: '0x1234567890123456789012345678901234567890' }
|
||||
]);
|
||||
|
||||
check('abi2-empty', [
|
||||
{ type: 'tuple', value: [ ] },
|
||||
]);
|
||||
|
||||
check('abi2-deeper', [
|
||||
{ type: 'tuple', value: [
|
||||
{ type: 'tuple', value: [
|
||||
{ type: 'uint256', value: 0x22222222222 }
|
||||
] }
|
||||
] }
|
||||
]);
|
||||
|
||||
check('abi2-same-struct', [
|
||||
{ type: 'tuple', value: [
|
||||
{ type: 'uint256', value: 18 },
|
||||
{ type: 'int256', value: -18 },
|
||||
] },
|
||||
{ type: 'tuple', value: [
|
||||
{ type: 'uint256', value: 18 },
|
||||
{ type: 'int256', value: -18 },
|
||||
] },
|
||||
{ type: 'tuple', value: [
|
||||
{ type: 'tuple', value: [
|
||||
{ type: 'tuple', value: [
|
||||
{ type: 'uint256', value: 18 },
|
||||
{ type: 'int256', value: -18 },
|
||||
] },
|
||||
] }
|
||||
] },
|
||||
]);
|
||||
|
||||
check('abi2-dynamic', [
|
||||
{ type: 'uint256[]', value: [
|
||||
{ type: 'uint256', value: 0x123456 },
|
||||
{ type: 'uint256', value: 0x789abc },
|
||||
] }
|
||||
]);
|
||||
|
||||
check('abi2-nested-dynamic', [
|
||||
{ type: 'uint256[][]', value: [
|
||||
{ type: 'uint256[]', value: [
|
||||
{ type: 'uint256', value: 0x123456 },
|
||||
{ type: 'uint256', value: 0x789abc },
|
||||
{ type: 'uint256', value: 0xdef123 },
|
||||
] },
|
||||
{ type: 'uint256[]', value: [
|
||||
{ type: 'uint256', value: 0x666666 },
|
||||
] },
|
||||
] }
|
||||
]);
|
||||
|
||||
check('abi2-string-array', [
|
||||
{ type: 'string[]', value: [
|
||||
{ type: 'string', value: "Hello" },
|
||||
{ type: 'string', value: "World" },
|
||||
] }
|
||||
]);
|
||||
|
||||
|
||||
|
||||
check('abi2-single', [
|
||||
{ name: 'StructA', type: 'tuple', value: [
|
||||
{ type: 'uint256', value: 0x11111111111 }
|
||||
] },
|
||||
]);
|
||||
|
||||
check('abi2-pair', [
|
||||
{ name: 'StructA', type: 'tuple', value: [
|
||||
{ type: 'address', value: address },
|
||||
{ type: 'uint256', value: 0x22222222222 }
|
||||
] },
|
||||
]);
|
||||
|
||||
check('abi2-deeper', [
|
||||
{ name: 'StructA', type: 'tuple', value: [
|
||||
{ name: 'StructB', type: 'tuple', value: [
|
||||
{ type: 'uint256', value: 0x22222222222 }
|
||||
] }
|
||||
] }
|
||||
]);
|
||||
|
||||
check('abi2-very-deep', [
|
||||
{ name: 'StructA', type: 'tuple', value: [
|
||||
{ type: 'address', value: address },
|
||||
{ name: 'StructB', type: 'tuple', value: [
|
||||
{ type: 'uint32', value: 45 },
|
||||
{ type: 'uint32', value: 46 },
|
||||
{ name: 'StructC', type: 'tuple', value: [
|
||||
{ type: 'uint32', value: 45 },
|
||||
{ type: 'uint256', value: 0x22222222222 },
|
||||
{ type: 'tuple', name: 'StructD', value: [
|
||||
{ type: 'bool', value: true }
|
||||
] }
|
||||
] }
|
||||
] },
|
||||
{ type: 'uint256', value: 0x55559876 },
|
||||
] }
|
||||
]);
|
||||
|
||||
check('abi2-string', [
|
||||
{ type: 'tuple', name: 'StructA', value: [
|
||||
{ type: 'string', value: "Hello World" }
|
||||
] }
|
||||
]);
|
||||
|
||||
check('abi2-empty-string', [
|
||||
{ type: 'tuple', name: 'StructA', value: [
|
||||
{ type: 'string', value: "" }
|
||||
] }
|
||||
]);
|
||||
|
||||
check('abi2-long-string', [
|
||||
{ type: 'tuple', name: 'StructA', value: [
|
||||
{ type: 'string', value: longText }
|
||||
] }
|
||||
]);
|
||||
*/
|
||||
|
||||
// Procedurally generated test cases (handles some black-box testing)
|
||||
|
||||
function randomTestPart(seed, info) {
|
||||
switch (utils.randomNumber(seed + '-type', 0, 7)) {
|
||||
case 0:
|
||||
return {
|
||||
type: 'address',
|
||||
name: 'address',
|
||||
value: function(extra) {
|
||||
return {
|
||||
type: 'address',
|
||||
name: 'address',
|
||||
value: getAddress(utils.randomHexString(seed + '-address-' + extra, 20, 20))
|
||||
}
|
||||
}
|
||||
};
|
||||
case 1:
|
||||
var sign = (utils.randomNumber(seed + '-number-sign', 0, 2) == 0);
|
||||
var type = ((sign ? '': 'u') + 'int');
|
||||
var size = utils.randomNumber(seed + '-number-size', 0, 33) * 8;
|
||||
if (size !== 0) {
|
||||
type += String(size);
|
||||
} else {
|
||||
size = 256;
|
||||
}
|
||||
|
||||
return {
|
||||
type: type,
|
||||
name: type,
|
||||
value: function(extra) {
|
||||
var value = new BN(utils.randomHexString(seed + '-number-value-' + extra, 1, size / 8).substring(2), 16);
|
||||
if (sign) {
|
||||
var signBit = (new BN(1)).shln(size - 1);
|
||||
if (!signBit.and(value).isZero()) {
|
||||
value = value.maskn(size - 1).mul(new BN(-1));
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: type,
|
||||
name: type,
|
||||
value: value
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
return {
|
||||
type: 'bytes',
|
||||
name: 'bytes',
|
||||
value: function(extra) {
|
||||
return {
|
||||
type: 'bytes',
|
||||
name: 'bytes',
|
||||
value: new Buffer(utils.randomBytes(seed + '-bytes-' + extra, 0, 64))
|
||||
}
|
||||
}
|
||||
};
|
||||
case 3:
|
||||
return {
|
||||
type: 'string',
|
||||
name: 'string',
|
||||
value: function(extra) {
|
||||
return {
|
||||
type: 'string',
|
||||
name: 'string',
|
||||
value: longText.substring(0, utils.randomNumber(seed + '-string-' + extra, 0, longText.length))
|
||||
}
|
||||
}
|
||||
};
|
||||
case 4:
|
||||
var count = utils.randomNumber(seed + '-bytes-count', 1, 33);
|
||||
return {
|
||||
type: 'bytes' + String(count),
|
||||
name: 'bytes' + String(count),
|
||||
value: function(extra) {
|
||||
return {
|
||||
type: 'bytes' + String(count),
|
||||
name: 'bytes' + String(count),
|
||||
value: new Buffer(utils.randomBytes(seed + '-bytes-value-' + extra, count, count))
|
||||
};
|
||||
}
|
||||
};
|
||||
case 5:
|
||||
var subtype = randomTestPart(seed + '-array-subtype', info);
|
||||
var count = utils.randomNumber(seed + '-array-count', 0, 4);
|
||||
var size = String(count);
|
||||
if (count === 0) {
|
||||
count = utils.randomNumber(seed + '-array-size', 0, 4);
|
||||
size = '';
|
||||
}
|
||||
|
||||
var type = subtype.type + '[' + size + ']';
|
||||
var name = (subtype.name + '[' + size + ']');
|
||||
|
||||
return {
|
||||
type: type,
|
||||
name: name,
|
||||
value: function() {
|
||||
var result = [];
|
||||
for (var i = 0; i < count; i++) {
|
||||
result.push(subtype.value('-array-value-' + i));
|
||||
}
|
||||
return {
|
||||
type: type,
|
||||
name: name,
|
||||
value: result
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
case 6:
|
||||
var subtypes = [];
|
||||
var subtypeTypes = [];
|
||||
var subtypeNames = [];
|
||||
var count = utils.randomNumber(seed + '-tuple-size', 1, 4);
|
||||
for (var i = 0; i < count; i++) {
|
||||
var subtype = randomTestPart(seed + '-tuple-subtype-' + i, info);
|
||||
subtypes.push(subtype);
|
||||
subtypeTypes.push(subtype.type);
|
||||
subtypeNames.push(subtype.name);
|
||||
}
|
||||
|
||||
var type = 'tuple(' + subtypeTypes.join(',') + ')';
|
||||
var name = getStructName(subtypeNames);
|
||||
var struct = getStructSource(subtypeNames);
|
||||
info.structs[struct] = true;
|
||||
|
||||
return {
|
||||
type: type,
|
||||
name: name,
|
||||
struct: struct,
|
||||
value: function(extra) {
|
||||
var result = [];
|
||||
subtypes.forEach(function(subtype) {
|
||||
result.push(subtype.value(seed + '-tuple-subvalue-' + i));
|
||||
});
|
||||
return {
|
||||
type: 'tuple',
|
||||
name: name,
|
||||
struct: struct,
|
||||
value: result
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
default:
|
||||
throw new Error('invalid case');
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2000; i++) {
|
||||
//i = 917
|
||||
var test = [];
|
||||
var info = { pragmas: { 'experimental ABIEncoderV2': true }, structs: {} };
|
||||
var count = utils.randomNumber('count-' + i, 1, 5);
|
||||
for (var j = 0; j < count; j++) {
|
||||
var part = randomTestPart('test-' + i + '-' + j, info)
|
||||
test.push(part.value('part-' + j));
|
||||
}
|
||||
console.dir(test, { depth: null });
|
||||
check('random-' + i, test, info);
|
||||
//break;
|
||||
}
|
||||
|
||||
promiseRationing.all(promiseFuncs, 20).then(function(result) {
|
||||
result = result.filter(function(item) { return !!item; } );
|
||||
utils.saveTests('contract-interface-abi2', result);
|
||||
}, function(error) {
|
||||
console.log('ERROR', error);
|
||||
});
|
||||
}
|
||||
|
||||
//makeTests();
|
||||
makeTestsAbi2();
|
||||
|
@ -75,7 +75,7 @@ function equals(a, b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (testWeb3) {
|
||||
if (testWeb3 || true) {
|
||||
if (a.match && a.match(/^0x[0-9A-Fa-f]{40}$/)) { a = a.toLowerCase(); }
|
||||
if (b.match && b.match(/^0x[0-9A-Fa-f]{40}$/)) { b = b.toLowerCase(); }
|
||||
}
|
||||
@ -126,6 +126,9 @@ function getValues(object, format) {
|
||||
}
|
||||
return utils.arrayify(object.value);
|
||||
|
||||
case 'tuple':
|
||||
return getValues(object.value, format);
|
||||
|
||||
default:
|
||||
throw new Error('invalid type - ' + object.type);
|
||||
}
|
||||
@ -207,14 +210,9 @@ describe('Contract Interface ABI Decoding', function() {
|
||||
assert.ok(false, 'This testcase seems to fail');
|
||||
|
||||
} else {
|
||||
//console.log(result);
|
||||
var resultBuffer = new Buffer(result.substring(2), 'hex');
|
||||
var valuesEthereumLib = getValues(JSON.parse(test.normalizedValues), FORMAT_ETHEREUM_LIB);
|
||||
//console.log('V', valuesEthereumLib);
|
||||
var ethereumLibDecoded = ethereumLibCoder.rawDecode(types, resultBuffer);
|
||||
//console.log('R', types, ethereumLibDecoded);
|
||||
//console.log('E', valuesEthereumLib, ethereumLibDecoded, equals(valuesEthereumLib, ethereumLibDecoded));
|
||||
//console.log(ethereumLibDecoded);
|
||||
assert.ok(equals(valuesEthereumLib, ethereumLibDecoded),
|
||||
'ethereum-lib decoded data - ' + title);
|
||||
}
|
||||
@ -223,3 +221,52 @@ describe('Contract Interface ABI Decoding', function() {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('Contract Interface ABI v2 Decoding', function() {
|
||||
var Interface = require('../contracts/index.js').Interface;
|
||||
|
||||
var tests = utils.loadTests('contract-interface-abi2');
|
||||
tests.forEach(function(test) {
|
||||
var values = getValues(JSON.parse(test.values));
|
||||
var types = JSON.parse(test.types);
|
||||
var result = test.result;
|
||||
var title = test.name + ' => (' + test.types + ') = (' + test.values + ')';
|
||||
|
||||
it(('decodes parameters - ' + test.name + ' - ' + test.types), function() {
|
||||
var decoded = Interface.decodeParams(types, result);
|
||||
var decodedArray = Array.prototype.slice.call(decoded);
|
||||
|
||||
assert.ok(equals(values, decodedArray), 'decoded parameters - ' + title);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Contract Interface ABI v2 Encoding', function() {
|
||||
var Interface = require('../contracts/index.js').Interface;
|
||||
|
||||
var tests = utils.loadTests('contract-interface-abi2');
|
||||
tests.forEach(function(test) {
|
||||
var values = getValues(JSON.parse(test.values));
|
||||
var types = JSON.parse(test.types);
|
||||
var expected = test.result;
|
||||
var title = test.name + ' => (' + test.types + ') = (' + test.value + ')';
|
||||
|
||||
it(('encodes parameters - ' + test.name + ' - ' + test.types), function() {
|
||||
var encoded = Interface.encodeParams(types, values);
|
||||
|
||||
/*
|
||||
console.log('Actual:');
|
||||
for (var i = 2; i < encoded.length; i += 64) {
|
||||
console.log(' ', encoded.substring(i, i + 64));
|
||||
}
|
||||
|
||||
console.log('Expected:');
|
||||
for (var i = 2; i < expected.length; i += 64) {
|
||||
console.log(' ', expected.substring(i, i + 64));
|
||||
}
|
||||
*/
|
||||
|
||||
assert.equal(encoded, expected, 'decoded parameters - ' + title);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
BIN
tests/tests/contract-interface-abi2.json.gz
Normal file
BIN
tests/tests/contract-interface-abi2.json.gz
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user