Skip to content

Commit

Permalink
Merge pull request #3522 from ethereum/abiCoderParamFormatting
Browse files Browse the repository at this point in the history
Update AbiCoder param formatting
  • Loading branch information
ryanio authored May 18, 2020
2 parents 16c44d4 + d13c468 commit ba7677d
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 56 deletions.
126 changes: 70 additions & 56 deletions packages/web3-eth-abi/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,74 +99,88 @@ ABICoder.prototype.encodeParameter = function (type, param) {
* @return {String} encoded list of params
*/
ABICoder.prototype.encodeParameters = function (types, params) {
const bytesToEvenLength = (param) => {
if (Buffer.isBuffer(param)) {
param = utils.toHex(param)
const paramTypeBytes = new RegExp(/^bytes([0-9]*)$/);
const paramTypeBytesArray = new RegExp(/^bytes([0-9]*)\[\]$/);
const paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
const paramTypeNumberArray = new RegExp(/^(u?int)([0-9]*)\[\]$/);

params = params.map(function (param, index) {
let type = types[index]
if (typeof type === 'object' && type.type) {
// We may get a named type of shape {name, type}
type = type.type
}
// bitwise AND operator returns true if odd
if (param.length & 1) {
return '0x0' + param.substring(2)
}
return param
}
const bytes32ToFixedLength = (param) => {
if (Buffer.isBuffer(param)) {
param = utils.toHex(param)
}
if (param.length < 66) { // 66 includes `0x`
return utils.rightPad(param, 64)

// Format BN to string
if (utils.isBN(param) || utils.isBigNumber(param)) {
return param.toString(10);
}
return param
}

return ethersAbiCoder.encode(
this.mapTypes(types),
params.map(function (param, index) {
let type = types[index]
if (typeof type === 'object' && type.type) {
// We may get a named type of shape {name, type}
type = type.type
// Handle some formatting of params for backwards compatability with Ethers V4
const formatParam = (type, param) => {
if (type.match(paramTypeBytesArray) || type.match(paramTypeNumberArray)) {
return param.map(p => formatParam(type.replace('[]', ''), p))
}

// Format BN to string
if (utils.isBN(param) || utils.isBigNumber(param)) {
return param.toString(10);
// Format correct width for u?int[0-9]*
let match = type.match(paramTypeNumber);
if (match) {
let size = parseInt(match[2] || "256");
if (size / 8 < param.length) {
// pad to correct bit width
param = utils.leftPad(param, size);
}
}

// Convert odd-length bytes to even
if (type === 'bytes') {
param = bytesToEvenLength(param)
} else if (type === 'bytes[]') {
param = param.map(p => bytesToEvenLength(p))
}
// Format correct length for bytes[0-9]+
match = type.match(paramTypeBytes);
if (match) {
if (Buffer.isBuffer(param)) {
param = utils.toHex(param);
}

// Convert bytes32 to fixed length
if (type === 'bytes32') {
param = bytes32ToFixedLength(param)
} else if (type === 'bytes32[]') {
param = param.map(p => bytes32ToFixedLength(p))
// format to correct length
let size = parseInt(match[1]);
if (size) {
let maxSize = size * 2;
if (param.substring(0, 2) === '0x') {
maxSize += 2;
}
if (param.length < maxSize) {
// pad to correct length
param = utils.rightPad(param, size * 2)
}
}

// format odd-length bytes to even-length
if (param.length % 2 === 1) {
param = '0x0' + param.substring(2)
}
}

// Format tuples for above rules
if (typeof type === 'string' && type.includes('tuple')) {
const coder = ethersAbiCoder._getCoder(ParamType.from(type));
const modifyParams = (coder, param) => {
coder.coders.forEach((c, i) => {
if (c.name === 'bytes') {
param[i] = bytesToEvenLength(param[i])
} else if (c.name === 'bytes32') {
param[i] = bytes32ToFixedLength(param[i])
} else if (c.name === 'tuple') {
modifyParams(c, param[i])
}
})
}
modifyParams(coder, param)
return param
}

param = formatParam(type, param)

// Format tuples for above rules
if (typeof type === 'string' && type.includes('tuple')) {
const coder = ethersAbiCoder._getCoder(ParamType.from(type));
const modifyParams = (coder, param) => {
coder.coders.forEach((c, i) => {
if (c.name === 'tuple') {
modifyParams(c, param[i])
} else {
param[i] = formatParam(c.name, param[i])
}
})
}
modifyParams(coder, param)
}

return param;
})
);
return param;
})
return ethersAbiCoder.encode(this.mapTypes(types), params);
};

/**
Expand Down
15 changes: 15 additions & 0 deletions test/abi.encodeParameter.js
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,21 @@ describe('lib/solidity/coder', function () {
values: [Buffer.from('cool'), Buffer.from('beans')],
expected: '00000000000000000000000000000000000000000000000000000000000000406265616e730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004636f6f6c00000000000000000000000000000000000000000000000000000000'
});
test({
types: ['bytes6', 'bytes12'],
values: [Buffer.from('super'), Buffer.from('man')],
expected: '73757065720000000000000000000000000000000000000000000000000000006d616e0000000000000000000000000000000000000000000000000000000000'
});
test({
types: ['bytes12[]'],
values: [[Buffer.from('buff'), Buffer.from('man')]],
expected: '0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000262756666000000000000000000000000000000000000000000000000000000006d616e0000000000000000000000000000000000000000000000000000000000'
});
test({
types: ['uint16', 'uint24'],
values: ['10000', '100000'],
expected: '000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000186a0'
});
});
});

Expand Down

0 comments on commit ba7677d

Please sign in to comment.