diff --git a/remix-lib/src/execution/txFormat.js b/remix-lib/src/execution/txFormat.js index cb59e41033..4048c85c13 100644 --- a/remix-lib/src/execution/txFormat.js +++ b/remix-lib/src/execution/txFormat.js @@ -186,14 +186,12 @@ module.exports = { callback('Error encoding arguments: ' + e) return } - if (!isConstructor || funArgs.length > 0) { - try { - data = helper.encodeParams(funAbi, funArgs) - dataHex = data.toString('hex') - } catch (e) { - callback('Error encoding arguments: ' + e) - return - } + try { + data = helper.encodeParams(funAbi, funArgs) + dataHex = data.toString('hex') + } catch (e) { + callback('Error encoding arguments: ' + e) + return } if (data.slice(0, 9) === 'undefined') { dataHex = data.slice(9) @@ -366,7 +364,7 @@ module.exports = { var outputTypes = [] for (i = 0; i < fnabi.outputs.length; i++) { var type = fnabi.outputs[i].type - outputTypes.push(type === 'tuple' ? helper.makeFullTupleTypeDefinition(fnabi.outputs[i]) : type) + outputTypes.push(type.indexOf('tuple') === 0 ? helper.makeFullTupleTypeDefinition(fnabi.outputs[i]) : type) } if (!response.length) response = new Uint8Array(32 * fnabi.outputs.length) // ensuring the data is at least filled by 0 cause `AbiCoder` throws if there's not engouh data diff --git a/remix-lib/src/execution/txHelper.js b/remix-lib/src/execution/txHelper.js index 2540c8d00d..49bfd244f3 100644 --- a/remix-lib/src/execution/txHelper.js +++ b/remix-lib/src/execution/txHelper.js @@ -3,9 +3,9 @@ var ethers = require('ethers') module.exports = { makeFullTupleTypeDefinition: function (typeDef) { - if (typeDef && typeDef.type === 'tuple' && typeDef.components) { + if (typeDef && typeDef.type.indexOf('tuple') === 0 && typeDef.components) { var innerTypes = typeDef.components.map((innerType) => innerType.type) - return 'tuple(' + innerTypes.join(',') + ')' + return `tuple(${innerTypes.join(',')})${this.extractSize(typeDef.type)}` } return typeDef.type }, @@ -15,7 +15,7 @@ module.exports = { if (funABI.inputs && funABI.inputs.length) { for (var i = 0; i < funABI.inputs.length; i++) { var type = funABI.inputs[i].type - types.push(type === 'tuple' ? this.makeFullTupleTypeDefinition(funABI.inputs[i]) : type) + types.push(type.indexOf('tuple') === 0 ? this.makeFullTupleTypeDefinition(funABI.inputs[i]) : type) if (args.length < types.length) { args.push('') } @@ -85,10 +85,23 @@ module.exports = { return serialized }, + extractSize: function (type) { + var size = type.match(/([a-zA-Z0-9])(\[.*\])/) + return size ? size[2] : '' + }, + getFunction: function (abi, fnName) { for (var i = 0; i < abi.length; i++) { var fn = abi[i] - if (fn.type === 'function' && fnName === fn.name + '(' + fn.inputs.map((value) => { return value.type }).join(',') + ')') { + if (fn.type === 'function' && fnName === fn.name + '(' + fn.inputs.map((value) => { + if (value.components) { + // we extract the size (if array) and append it later + var size = this.extractSize(value.type) + return `(${value.components.map((value) => { return value.type }).join(',')})${size}` + } else { + return value.type + } + }).join(',') + ')') { return fn } } diff --git a/remix-lib/src/execution/txListener.js b/remix-lib/src/execution/txListener.js index 8864e76294..ec2e713e85 100644 --- a/remix-lib/src/execution/txListener.js +++ b/remix-lib/src/execution/txListener.js @@ -330,7 +330,7 @@ class TxListener { var inputTypes = [] for (var i = 0; i < abi.inputs.length; i++) { var type = abi.inputs[i].type - inputTypes.push(type === 'tuple' ? txHelper.makeFullTupleTypeDefinition(abi.inputs[i]) : type) + inputTypes.push(type.indexOf('tuple') === 0 ? txHelper.makeFullTupleTypeDefinition(abi.inputs[i]) : type) } var abiCoder = new ethers.utils.AbiCoder() var decoded = abiCoder.decode(inputTypes, data) diff --git a/remix-lib/test/tests.js b/remix-lib/test/tests.js index 4e2030d630..0528c2cf1a 100644 --- a/remix-lib/test/tests.js +++ b/remix-lib/test/tests.js @@ -3,6 +3,7 @@ require('./eventManager.js') require('./sourceMappingDecoder.js') require('./util.js') require('./txFormat.js') +require('./txHelper.js') require('./traceManager.js') require('./codeManager.js') diff --git a/remix-lib/test/txFormat.js b/remix-lib/test/txFormat.js index 4deab4cf57..cb542cf6ad 100644 --- a/remix-lib/test/txFormat.js +++ b/remix-lib/test/txFormat.js @@ -184,6 +184,35 @@ tape('test abiEncoderV2', function (t) { }) }) +tape('test abiEncoderV2 array of tuple', function (t) { + t.test('(abiEncoderV2)', function (st) { + /* + { + "685e37ad": "addStructs((uint256,string))", + "e5cb65f9": "addStructs((uint256,string)[])" + } + */ + st.plan(2) + + var output = compiler.compileStandardWrapper(compilerInput(abiEncoderV2ArrayOfTuple)) + output = JSON.parse(output) + var contract = output.contracts['test.sol']['test'] + txFormat.encodeParams('[34, "test"]', contract.abi[0], (error, encoded) => { + console.log(error) + var decoded = txFormat.decodeResponse(util.hexToIntArray(encoded.dataHex), contract.abi[0]) + console.log(decoded) + st.equal(decoded[0], 'tuple(uint256,string): _strucmts 34,test') + }) + + txFormat.encodeParams('[[34, "test"], [123, "test2"]]', contract.abi[1], (error, encoded) => { + console.log(error) + var decoded = txFormat.decodeResponse(util.hexToIntArray(encoded.dataHex), contract.abi[1]) + console.log(decoded) + st.equal(decoded[0], 'tuple(uint256,string)[]: strucmts 34,test,123,test2') + }) + }) +}) + var uintContract = `contract uintContractTest { uint _tp; address _ap; @@ -251,3 +280,21 @@ contract test { return mm; } }` + +var abiEncoderV2ArrayOfTuple = `pragma experimental ABIEncoderV2; +contract test { + + struct MyStruct {uint256 num; string _string;} + + constructor (MyStruct[] _structs, string _str) { + + } + + function addStructs(MyStruct[] _structs) public returns (MyStruct[] strucmts) { + strucmts = _structs; + } + + function addStructs(MyStruct _structs) public returns (MyStruct _strucmts) { + _strucmts = _structs; + } +}` diff --git a/remix-lib/test/txHelper.js b/remix-lib/test/txHelper.js new file mode 100644 index 0000000000..7122be43b3 --- /dev/null +++ b/remix-lib/test/txHelper.js @@ -0,0 +1,147 @@ +'use strict' +var tape = require('tape') +var txHelper = require('../src/execution/txHelper') + +tape('getFunction', function (st) { + st.plan(5) + var fn = txHelper.getFunction(JSON.parse(abi), 'o((address,uint256))') + st.equal(fn.name, 'o') + + fn = txHelper.getFunction(JSON.parse(abi), 'i(bytes32)') + st.equal(fn.name, 'i') + + fn = txHelper.getFunction(JSON.parse(abi), 'o1(string,(address,uint256),int256,int256[][3],(address,uint256)[3][])') + st.equal(fn.name, 'o1') + + fn = txHelper.getConstructorInterface(JSON.parse(abi)) + st.equal(fn.type, 'constructor') + + fn = txHelper.getFallbackInterface(JSON.parse(abi)) + st.equal(fn.type, 'fallback') +}) + +var abi = `[ + { + "constant": false, + "inputs": [ + { + "name": "_param", + "type": "bytes32" + } + ], + "name": "i", + "outputs": [ + { + "name": "_t", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_g", + "type": "string" + }, + { + "components": [ + { + "name": "addr", + "type": "address" + }, + { + "name": "age", + "type": "uint256" + } + ], + "name": "_p", + "type": "tuple" + }, + { + "name": "_pg", + "type": "int256" + }, + { + "name": "", + "type": "int256[][3]" + }, + { + "components": [ + { + "name": "addr", + "type": "address" + }, + { + "name": "age", + "type": "uint256" + } + ], + "name": "", + "type": "tuple[3][]" + } + ], + "name": "o1", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "addr", + "type": "address" + }, + { + "name": "age", + "type": "uint256" + } + ], + "name": "_p", + "type": "tuple" + } + ], + "name": "o", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_g", + "type": "bytes32" + }, + { + "components": [ + { + "name": "addr", + "type": "address" + }, + { + "name": "age", + "type": "uint256" + } + ], + "name": "u", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "payable": false, + "stateMutability": "nonpayable", + "type": "fallback" + } +]`