Skip to content
This repository has been archived by the owner on Oct 30, 2024. It is now read-only.

Rebase on master "solidity pack arrays" + fixes #82

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 69 additions & 45 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,61 +447,85 @@ ABI.stringify = function (types, values) {
return ret
}

ABI.solidityHexValue = function (type, value, bitsize) {
// pass in bitsize = null if use default bitsize
var size, num
if (isArray(type)) {
var subType = type.replace(/\[.*?\]/, '')
if (!isArray(subType)) {
var arraySize = parseTypeArray(type)
if (arraySize !== 'dynamic' && arraySize !== 0 && value.length > arraySize) {
throw new Error('Elements exceed array size: ' + arraySize)
}
}
var arrayValues = value.map(function (v) {
return ABI.solidityHexValue(subType, v, 256)
})
return Buffer.concat(arrayValues)
} else if (type === 'bytes') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, did also some line-by-line review of the isArray part here and some manual tests. Since this is already some reviewed write-up of an existing PR and solid new tests are added and passing I think I can give this a go. Thanks @djrtwo for submitting and @alex-forshtat-tbk for putting this along the finish line. 😄

return value
} else if (type === 'string') {
return Buffer.from(value, 'utf8')
} else if (type === 'bool') {
bitsize = bitsize || 8
var padding = Array((bitsize) / 4).join('0')
return Buffer.from(value ? padding + '1' : padding + '0', 'hex')
} else if (type === 'address') {
var bytesize = 20
if (bitsize) {
bytesize = bitsize / 8
}
return utils.setLengthLeft(value, bytesize)
} else if (type.startsWith('bytes')) {
size = parseTypeN(type)
if (size < 1 || size > 32) {
throw new Error('Invalid bytes<N> width: ' + size)
}

return utils.setLengthRight(value, size)
} else if (type.startsWith('uint')) {
size = parseTypeN(type)
if ((size % 8) || (size < 8) || (size > 256)) {
throw new Error('Invalid uint<N> width: ' + size)
}

num = parseNumber(value)
if (num.bitLength() > size) {
throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength())
}

bitsize = bitsize || size
return num.toArrayLike(Buffer, 'be', bitsize / 8)
} else if (type.startsWith('int')) {
size = parseTypeN(type)
if ((size % 8) || (size < 8) || (size > 256)) {
throw new Error('Invalid int<N> width: ' + size)
}

num = parseNumber(value)
if (num.bitLength() > size) {
throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength())
}

bitsize = bitsize || size
return num.toTwos(size).toArrayLike(Buffer, 'be', bitsize / 8)
} else {
// FIXME: support all other types
throw new Error('Unsupported or invalid type: ' + type)
}
}

ABI.solidityPack = function (types, values) {
if (types.length !== values.length) {
throw new Error('Number of types are not matching the values')
}

var size, num
var ret = []

for (var i = 0; i < types.length; i++) {
var type = elementaryName(types[i])
var value = values[i]

if (type === 'bytes') {
ret.push(value)
} else if (type === 'string') {
ret.push(Buffer.from(value, 'utf8'))
} else if (type === 'bool') {
ret.push(Buffer.from(value ? '01' : '00', 'hex'))
} else if (type === 'address') {
ret.push(utils.setLengthLeft(value, 20))
} else if (type.startsWith('bytes')) {
size = parseTypeN(type)
if (size < 1 || size > 32) {
throw new Error('Invalid bytes<N> width: ' + size)
}

ret.push(utils.setLengthRight(value, size))
} else if (type.startsWith('uint')) {
size = parseTypeN(type)
if ((size % 8) || (size < 8) || (size > 256)) {
throw new Error('Invalid uint<N> width: ' + size)
}

num = parseNumber(value)
if (num.bitLength() > size) {
throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength())
}

ret.push(num.toArrayLike(Buffer, 'be', size / 8))
} else if (type.startsWith('int')) {
size = parseTypeN(type)
if ((size % 8) || (size < 8) || (size > 256)) {
throw new Error('Invalid int<N> width: ' + size)
}

num = parseNumber(value)
if (num.bitLength() > size) {
throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength())
}

ret.push(num.toTwos(size).toArrayLike(Buffer, 'be', size / 8))
} else {
// FIXME: support all other types
throw new Error('Unsupported or invalid type: ' + type)
}
ret.push(ABI.solidityHexValue(type, value, null))
}

return Buffer.concat(ret)
Expand Down
55 changes: 55 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,61 @@ describe('solidity tight packing multiple arguments', function () {
})
})

describe('solidity tight packing uint32[]', function () {
it('should equal', function () {
var a = abi.solidityPack(
['uint32[]'],
[[8, 9]]
)
var b = '00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009'
assert.strict.equal(a.toString('hex'), b.toString('hex'))
})
})

describe('solidity tight packing bool[][]', function () {
it('should equal', function () {
let a = abi.solidityPack(
['bool[][]'],
[[[true, false], [false, true]]]
)
let b = '0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'
assert.strict.equal(a.toString('hex'), b.toString('hex'))
})
})

describe('solidity tight packing address[]', function () {
it('should equal', function () {
let a = abi.solidityPack(
['address[]'],
[[new BN('43989fb883ba8111221e89123897538475893837', 16)]]
)
let b = '00000000000000000000000043989fb883ba8111221e89123897538475893837'
assert.strict.equal(a.toString('hex'), b.toString('hex'))
})
})

describe('solidity tight packing uint32[2]', function () {
it('should equal', function () {
let a = abi.solidityPack(
['uint32[2]'],
[[11, 12]]
)
let b = '000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c'
assert.strict.equal(a.toString('hex'), b.toString('hex'))
})
})

describe('solidity tight packing uint32[2] with wrong array length', function () {
it('should throw', function () {
assert.throws(function () {
abi.solidityPack(
['uint32[2]'],
[[11, 12, 13]]
)
})
})
})

describe('solidity tight packing sha3', function () {
it('should equal', function () {
var a = abi.soliditySHA3(
Expand Down