ethereumjs-abi
Advanced tools
Comparing version 0.4.0 to 0.5.0
154
lib/index.js
@@ -1,30 +0,43 @@ | ||
require('es6-shim'); | ||
const utils = require('ethereumjs-util'); | ||
const BN = require('bn.js'); | ||
const Common = require('./common.js'); | ||
const utf8 = require('utf8'); | ||
var ABI = function() { | ||
this.common = new Common(); | ||
}; | ||
// Encode as 256bit two's complement | ||
function toTwos(num) { | ||
if (num.isNeg()) | ||
num = new BN('10000000000000000000000000000000000000000000000000000000000000000', 16).isub(num.iabs()); | ||
// FIXME: the proper way would be if we have bitwise negation: num.iinv().iaddn(1) | ||
return num; | ||
} | ||
// Convert from short to canonical names | ||
// FIXME: optimise or make this nicer? | ||
function elementaryName(name) { | ||
if (name.startsWith('int[')) | ||
return 'int256' + name.slice(4); | ||
else if (name === 'int') | ||
return 'int256'; | ||
if (name.startsWith('uint[')) | ||
return 'uint256' + name.slice(4); | ||
else if (name === 'uint') | ||
return 'uint256'; | ||
else if (name.startsWith('real[')) | ||
return 'real128x128' + name.slice(4); | ||
else if (name === 'real') | ||
return 'real128x128'; | ||
else if (name.startsWith('ureal[')) | ||
return 'ureal128x128' + name.slice(4); | ||
else if (name === 'ureal') | ||
return 'ureal128x128'; | ||
return name; | ||
}; | ||
// Decode from 256bit two's complement | ||
function fromTwos(num) { | ||
if (num.testn(255)) // top bit set => negative number | ||
num = new BN('10000000000000000000000000000000000000000000000000000000000000000', 16).isub(num).ineg(); | ||
// FIXME: the proper way would be if we have bitwise negation: num.iinv().iaddn(1) | ||
return num; | ||
} | ||
function eventID(name, types) { | ||
// FIXME: use node.js util.format? | ||
var sig = name + '(' + types.map(elementaryName).join(',') + ')'; | ||
return utils.sha3(new Buffer(sig)); | ||
}; | ||
function methodID(name, types) { | ||
return eventID(name, types).slice(0, 4); | ||
}; | ||
// Parse N from type<N> | ||
function parseTypeN(type) { | ||
return parseInt(/^\D+(\d+)$/.exec(type)[1]); | ||
return parseInt(/^\D+(\d+)$/.exec(type)[1], 10); | ||
} | ||
@@ -35,3 +48,3 @@ | ||
var tmp = /^\D+(\d+)x(\d+)$/.exec(type); | ||
return [ parseInt(tmp[1]), parseInt(tmp[2]) ]; | ||
return [ parseInt(tmp[1], 10), parseInt(tmp[2], 10) ]; | ||
} | ||
@@ -45,3 +58,3 @@ | ||
else | ||
return parseInt(tmp); | ||
return parseInt(tmp, 10); | ||
} | ||
@@ -86,3 +99,3 @@ | ||
throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength()); | ||
return new Buffer(num.toArray('be', 32)); | ||
return num.toBuffer('be', 32); | ||
} else if (type.startsWith('int')) { | ||
@@ -95,3 +108,3 @@ var size = parseTypeN(type); | ||
throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength()); | ||
return new Buffer(toTwos(num).toArray('be', 32)); | ||
return num.toTwos(256).toBuffer('be', 32); | ||
} | ||
@@ -145,3 +158,3 @@ // FIXME: support ureal<N>x<M> and real<N>x<M> | ||
throw new Error('Invalid uint<N> width: ' + size); | ||
var num = fromTwos(new BN(arg.slice(0, 32), 16, 'be')); | ||
var num = new BN(arg.slice(0, 32), 16, 'be').fromTwos(256); | ||
if (num.bitLength() > size) | ||
@@ -165,3 +178,3 @@ throw new Error('Decoded uint exceeds width: ' + size + ' vs ' + num.bitLength()); | ||
// @args an array of the appropriate values | ||
ABI.prototype.rawEncode = function(name, types, args) { | ||
ABI.rawEncode = function(name, types, args) { | ||
var output = new Buffer(0); | ||
@@ -179,3 +192,3 @@ var data = new Buffer(0); | ||
if (name !== null) | ||
pushOutput(this.common.methodID(name, types)); | ||
pushOutput(methodID(name, types)); | ||
@@ -185,3 +198,3 @@ const headLength = 32 * types.length; | ||
for (var i in types) { | ||
var type = this.common.elementaryName(types[i]); | ||
var type = elementaryName(types[i]); | ||
var arg = args[i]; | ||
@@ -203,11 +216,11 @@ var cur = encodeSingle(type, arg); | ||
ABI.prototype.rawEncodeResponse = function(types, args) { | ||
return this.rawEncode(null, types, args); | ||
ABI.rawEncodeResponse = function(types, args) { | ||
return ABI.rawEncode(null, types, args); | ||
}; | ||
ABI.prototype.encode = function(abiDefinition, request, args) { | ||
ABI.encode = function(abiDefinition, request, args) { | ||
throw new Error('Not implemented'); | ||
}; | ||
ABI.prototype.rawDecode = function(name, intypes, outtypes, data) { | ||
ABI.rawDecode = function(name, intypes, outtypes, data) { | ||
var ret = []; | ||
@@ -219,3 +232,3 @@ | ||
if (name !== null) { | ||
if (this.common.methodID(name, intypes).toString('hex') !== data.slice(0, 4).toString('hex')) | ||
if (methodID(name, intypes).toString('hex') !== data.slice(0, 4).toString('hex')) | ||
throw new Error('Invalid method signature'); | ||
@@ -227,3 +240,3 @@ data = data.slice(4); | ||
for (var i in outtypes) { | ||
var type = this.common.elementaryName(outtypes[i]); | ||
var type = elementaryName(outtypes[i]); | ||
var cur = data.slice(offset, offset + 32); | ||
@@ -250,7 +263,7 @@ | ||
ABI.prototype.decode = function(abiDefinition, request, data) { | ||
ABI.decode = function(abiDefinition, request, data) { | ||
throw new Error('Not implemented'); | ||
}; | ||
ABI.prototype.solidityPack = function(types, values) { | ||
ABI.solidityPack = function(types, values) { | ||
if (types.length !== values.length) { | ||
@@ -261,3 +274,3 @@ throw new Error('Number of types are not matching the values'); | ||
for (var i = 0; i < types.length; i++) { | ||
var type = this.common.elementaryName(types[i]); | ||
var type = elementaryName(types[i]); | ||
var value = values[i]; | ||
@@ -284,3 +297,3 @@ if (type === 'bytes') { | ||
throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength()); | ||
ret.push(utils.pad(num, size / 8)); | ||
ret.push(num.toBuffer('be', size / 8)); | ||
} else if (type.startsWith('int')) { | ||
@@ -293,3 +306,3 @@ var size = parseTypeN(type); | ||
throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength()); | ||
ret.push(utils.pad(toTwos(num), size / 8)); | ||
ret.push(num.toTwos(size).toBuffer('be', size / 8)); | ||
} else { | ||
@@ -303,14 +316,69 @@ // FIXME: support all other types | ||
ABI.prototype.soliditySHA3 = function(types, values) { | ||
return utils.sha3(this.solidityPack(types, values)); | ||
ABI.soliditySHA3 = function(types, values) { | ||
return utils.sha3(ABI.solidityPack(types, values)); | ||
}; | ||
ABI.prototype.soliditySHA256 = function(types, values) { | ||
return utils.sha256(this.solidityPack(types, values)); | ||
ABI.soliditySHA256 = function(types, values) { | ||
return utils.sha256(ABI.solidityPack(types, values)); | ||
}; | ||
ABI.prototype.solidityRIPEMD160 = function(types, values) { | ||
return utils.ripemd160(this.solidityPack(types, values), true); | ||
ABI.solidityRIPEMD160 = function(types, values) { | ||
return utils.ripemd160(ABI.solidityPack(types, values), true); | ||
}; | ||
// Serpent's users are familiar with this encoding | ||
// - s: string | ||
// - b: bytes | ||
// - b<N>: bytes<N> | ||
// - i: int256 | ||
// - a: int256[] | ||
function isNumeric(c) { | ||
// FIXME: is this correct? Seems to work | ||
return (c >= '0') && (c <= '9'); | ||
} | ||
ABI.fromSerpent = function(sig) { | ||
var ret = []; | ||
for (var i = 0; i < sig.length; i++) { | ||
var type = sig[i]; | ||
if (type === 's' || type === 'b') { | ||
var tmp = 'bytes'; | ||
var j = i + 1; | ||
while ((j < sig.length) && isNumeric(sig[j])) { | ||
tmp += sig[j] - '0'; | ||
j++; | ||
} | ||
i = j - 1; | ||
ret.push(tmp); | ||
} else if (type === 'i') { | ||
ret.push('int256'); | ||
} else if (type === 'a') { | ||
ret.push('int256[]'); | ||
} else { | ||
throw new Error('Unsupported or invalid type: ' + type); | ||
} | ||
} | ||
return ret; | ||
}; | ||
ABI.toSerpent = function(types) { | ||
var ret = []; | ||
for (var i = 0; i < types.length; i++) { | ||
var type = types[i]; | ||
if (type === 'bytes' || type === 'string') { | ||
ret.push('s'); | ||
} else if (type.startsWith('bytes')) { | ||
ret.push('b' + parseTypeN(type)); | ||
} else if (type === 'int256') { | ||
ret.push('i'); | ||
} else if (type === 'int256[]') { | ||
ret.push('a'); | ||
} else { | ||
throw new Error('Unsupported or invalid type: ' + type); | ||
} | ||
} | ||
return ret.join(''); | ||
}; | ||
module.exports = ABI; |
{ | ||
"name": "ethereumjs-abi", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "Decoder and encoder for the Ethereum ABI", | ||
"main": "index.js", | ||
"dependencies": { | ||
"bn.js": "^4.5.2", | ||
"es6-shim": "^0.33.6", | ||
"bn.js": "^4.10.0", | ||
"ethereumjs-util": "^2.3.1", | ||
@@ -10,0 +9,0 @@ "utf8": "2.1.1" |
@@ -13,4 +13,3 @@ # ethereumjs-abi | ||
```js | ||
var ABI = require('ethereumjs-abi') | ||
var abi = new ABI() | ||
var abi = require('ethereumjs-abi') | ||
@@ -32,4 +31,3 @@ // returns the encoded binary (as a Buffer) data to be sent | ||
```js | ||
var ABI = require('ethereumjs-abi') | ||
var abi = new ABI() | ||
var abi = require('ethereumjs-abi') | ||
@@ -39,5 +37,5 @@ // need to have the ABI definition in JSON as per specification | ||
var encoded = ABI.encode(tokenAbi, "balanceOf(uint256 address)", [ "0x0000000000000000000000000000000000000000" ]) | ||
var encoded = abi.encode(tokenAbi, "balanceOf(uint256 address)", [ "0x0000000000000000000000000000000000000000" ]) | ||
var decoded = ABI.decode(tokenAbi, "balanceOf(uint256 address)", data) | ||
var decoded = abi.decode(tokenAbi, "balanceOf(uint256 address)", data) | ||
``` | ||
@@ -65,4 +63,3 @@ | ||
```js | ||
var ABI = require('ethereumjs-abi') | ||
var abi = new ABI() | ||
var abi = require('ethereumjs-abi') | ||
var BN = require('bn.js') | ||
@@ -83,2 +80,35 @@ | ||
#### Using Serpent types | ||
Serpent uses a different notation for the types, even though it will serialize to the same ABI. | ||
We provide two helpers to convert between these notations: | ||
* ```fromSerpent```: convert a Serpent notation to the ABI notation | ||
* ```toSerpent```: the other way around | ||
Example usage: | ||
```js | ||
abi.fromSerpent('s') // [ 'string' ] | ||
abi.fromSerpent('b') // [ 'bytes' ] | ||
abi.fromSerpent('i') // [ 'int256' ] | ||
abi.fromSerpent('a') // [ 'int256[]' ] | ||
abi.fromSerpent('b8') // [ 'bytes8' ] | ||
abi.fromSerpent('b8i') // [ 'bytes8', 'int256' ] | ||
abi.toSerpent([ 'string' ]) // 's' | ||
abi.toSerpent([ 'int256' ]) // 'i' | ||
abi.toSerpent([ 'int256[]' ]) // 'a' | ||
abi.toSerpent([ 'bytes' ]) // 'b' | ||
abi.toSerpent([ 'bytes8' ]) // 'b8' | ||
abi.toSerpent([ 'bytes8', 'int256' ]) // 'b8i' | ||
``` | ||
It is to be used in conjunction with ```rawEncode``` and ```rawDecode```: | ||
```js | ||
var encoded = abi.rawEncode("balanceOf", abi.fromSerpent("i"), [ "0x0000000000000000000000000000000000000000" ]) | ||
var decoded = abi.rawDecode("balanceOf", abi.fromSerpent("i"), abi.fromSerpent("i"), data) | ||
``` | ||
## Contributing | ||
@@ -85,0 +115,0 @@ |
var assert = require('assert'); | ||
var ABI = require('../index.js'); | ||
var abi = new ABI(); | ||
var abi = require('../index.js'); | ||
var BN = require('bn.js'); | ||
@@ -16,2 +15,3 @@ | ||
/* | ||
describe('official test vector 2 (encoding)', function() { | ||
@@ -24,2 +24,3 @@ it('should equal', function() { | ||
}); | ||
*/ | ||
@@ -380,1 +381,50 @@ describe('official test vector 3 (encoding)', function() { | ||
}); | ||
describe('solidity tight packing with small ints', function() { | ||
it('should equal', function() { | ||
var a = abi.soliditySHA3( | ||
[ 'address', 'address', 'int64', 'uint192' ], | ||
[ new BN('43989fb883ba8111221e89123897538475893837', 16), 0, 10000, 1448075779 ] | ||
); | ||
var b = '1c34bbd3d419c05d028a9f13a81a1212e33cb21f4b96ce1310442911c62c6986'; | ||
assert.equal(a.toString('hex'), b.toString('hex')); | ||
}); | ||
}); | ||
describe('converting from serpent types', function() { | ||
it('should equal', function() { | ||
assert.deepEqual(abi.fromSerpent('s'), [ 'bytes' ]); | ||
assert.deepEqual(abi.fromSerpent('b'), [ 'bytes' ]); | ||
assert.deepEqual(abi.fromSerpent('i'), [ 'int256' ]); | ||
assert.deepEqual(abi.fromSerpent('a'), [ 'int256[]' ]); | ||
assert.deepEqual(abi.fromSerpent('b8'), [ 'bytes8' ]); | ||
assert.deepEqual(abi.fromSerpent('b8i'), [ 'bytes8', 'int256' ]); | ||
assert.deepEqual(abi.fromSerpent('b32'), [ 'bytes32' ]); | ||
assert.deepEqual(abi.fromSerpent('b32i'), [ 'bytes32', 'int256' ]); | ||
assert.deepEqual(abi.fromSerpent('sbb8ib8a'), [ 'bytes', 'bytes', 'bytes8', 'int256', 'bytes8', 'int256[]' ]); | ||
assert.throws(function() { | ||
abi.fromSerpent('i8'); | ||
}); | ||
assert.throws(function() { | ||
abi.fromSerpent('x'); | ||
}); | ||
}); | ||
}); | ||
describe('converting to serpent types', function() { | ||
it('should equal', function() { | ||
assert.equal(abi.toSerpent([ 'string' ]), 's'); | ||
assert.equal(abi.toSerpent([ 'int256' ]), 'i'); | ||
assert.equal(abi.toSerpent([ 'int256[]' ]), 'a'); | ||
assert.equal(abi.toSerpent([ 'bytes' ]), 's'); | ||
assert.equal(abi.toSerpent([ 'bytes8' ]), 'b8'); | ||
assert.equal(abi.toSerpent([ 'bytes32' ]), 'b32'); | ||
assert.equal(abi.toSerpent([ 'string', 'bytes', 'bytes8', 'int256', 'bytes8', 'int256[]' ]), 'ssb8ib8a'); | ||
assert.throws(function() { | ||
abi.toSerpent('int8'); | ||
}); | ||
assert.throws(function() { | ||
abi.toSerpent('bool'); | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3
723
135
36992
7
- Removedes6-shim@^0.33.6
- Removedes6-shim@0.33.13(transitive)
Updatedbn.js@^4.10.0