diff options
author | Fabio Berger <me@fabioberger.com> | 2018-08-15 05:21:47 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-08-15 05:21:47 +0800 |
commit | 2f2582a0da3095d61a99ef09744dc0995677558e (patch) | |
tree | 54989565919038c42d495dafb7426d4148605e84 /packages/utils | |
parent | 8169155a6547fb0283cd0f5362aad3c0b173b00b (diff) | |
parent | fadd292ecf367e42154856509d0ea0c20b23f2f1 (diff) | |
download | dexon-sol-tools-2f2582a0da3095d61a99ef09744dc0995677558e.tar dexon-sol-tools-2f2582a0da3095d61a99ef09744dc0995677558e.tar.gz dexon-sol-tools-2f2582a0da3095d61a99ef09744dc0995677558e.tar.bz2 dexon-sol-tools-2f2582a0da3095d61a99ef09744dc0995677558e.tar.lz dexon-sol-tools-2f2582a0da3095d61a99ef09744dc0995677558e.tar.xz dexon-sol-tools-2f2582a0da3095d61a99ef09744dc0995677558e.tar.zst dexon-sol-tools-2f2582a0da3095d61a99ef09744dc0995677558e.zip |
Merge development
Diffstat (limited to 'packages/utils')
-rw-r--r-- | packages/utils/CHANGELOG.json | 3 | ||||
-rw-r--r-- | packages/utils/CHANGELOG.md | 10 | ||||
-rw-r--r-- | packages/utils/coverage/.gitkeep | 0 | ||||
-rw-r--r-- | packages/utils/package.json | 28 | ||||
-rw-r--r-- | packages/utils/src/abi_utils.ts | 153 | ||||
-rw-r--r-- | packages/utils/test/abi_utils_test.ts | 19 | ||||
-rw-r--r-- | packages/utils/tsconfig.json | 2 |
7 files changed, 200 insertions, 15 deletions
diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 8abfbaf43..3e3e1125b 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -6,7 +6,8 @@ "note": "Increased BigNumber decimal precision from 20 to 78", "pr": 807 } - ] + ], + "timestamp": 1534210131 }, { "timestamp": 1532619515, diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index afd30080d..bdde2687d 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.5 - _August 13, 2018_ + + * Increased BigNumber decimal precision from 20 to 78 (#807) + ## v1.0.4 - _July 26, 2018_ * Dependencies updated @@ -21,7 +25,7 @@ CHANGELOG * Add `AbortController` polyfill to `fetchAsync` (#903) -## v1.0.0 - _July 20, 2018_ +## v1.0.0 - _July 19, 2018_ * Add `fetchAsync` which adds a default timeout to all requests (#874) @@ -39,7 +43,7 @@ CHANGELOG * Dependencies updated -## v0.7.0 - _June 1, 2018_ +## v0.7.0 - _May 31, 2018_ * Incorrect publish that was unpublished @@ -47,7 +51,7 @@ CHANGELOG * Dependencies updated -## v0.6.1 - _May 5, 2018_ +## v0.6.1 - _May 4, 2018_ * Dependencies updated diff --git a/packages/utils/coverage/.gitkeep b/packages/utils/coverage/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/packages/utils/coverage/.gitkeep diff --git a/packages/utils/package.json b/packages/utils/package.json index b2ce9a17e..bccd03ae6 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,17 +1,22 @@ { "name": "@0xproject/utils", - "version": "1.0.4", + "version": "1.0.5", "engines": { "node": ">=6.12" }, "description": "0x TS utils", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "main": "lib/src/index.js", + "types": "lib/src/index.d.ts", "scripts": { "watch_without_deps": "tsc -w", "build": "tsc", "clean": "shx rm -rf lib", - "lint": "tslint --project ." + "lint": "tslint --project .", + "test": "yarn run_mocha", + "test:circleci": "yarn test:coverage", + "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", + "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", + "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info" }, "license": "Apache-2.0", "repository": { @@ -23,28 +28,31 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/utils/README.md", "devDependencies": { - "@0xproject/tslint-config": "^1.0.4", + "@0xproject/tslint-config": "^1.0.5", "@types/lodash": "4.14.104", + "@types/mocha": "^2.2.42", + "chai": "^4.0.1", "copyfiles": "^1.2.0", "make-promises-safe": "^1.1.0", + "mocha": "^4.1.0", "npm-run-all": "^4.1.2", "shx": "^0.2.2", "tslint": "5.11.0", - "typescript": "2.7.1" + "typescript": "2.9.2" }, "dependencies": { - "@0xproject/types": "^1.0.1-rc.3", - "@0xproject/typescript-typings": "^1.0.3", + "@0xproject/types": "^1.0.1-rc.4", + "@0xproject/typescript-typings": "^1.0.4", "@types/node": "^8.0.53", "abortcontroller-polyfill": "^1.1.9", "bignumber.js": "~4.1.0", "detect-node": "2.0.3", - "ethereum-types": "^1.0.3", + "ethereum-types": "^1.0.4", "ethereumjs-util": "^5.1.1", "ethers": "3.0.22", "isomorphic-fetch": "^2.2.1", "js-sha3": "^0.7.0", - "lodash": "^4.17.4" + "lodash": "^4.17.5" }, "publishConfig": { "access": "public" diff --git a/packages/utils/src/abi_utils.ts b/packages/utils/src/abi_utils.ts index 421dd405c..c9b70966c 100644 --- a/packages/utils/src/abi_utils.ts +++ b/packages/utils/src/abi_utils.ts @@ -1,7 +1,160 @@ import { AbiDefinition, AbiType, ContractAbi, DataItem, MethodAbi } from 'ethereum-types'; +import * as ethers from 'ethers'; import * as _ from 'lodash'; +import { BigNumber } from './configured_bignumber'; + +// Note(albrow): This function is unexported in ethers.js. Copying it here for +// now. +// Source: https://github.com/ethers-io/ethers.js/blob/884593ab76004a808bf8097e9753fb5f8dcc3067/contracts/interface.js#L30 +function parseEthersParams(params: DataItem[]): { names: ethers.ParamName[]; types: string[] } { + const names: ethers.ParamName[] = []; + const types: string[] = []; + + params.forEach((param: DataItem) => { + if (param.components != null) { + let suffix = ''; + const arrayBracket = param.type.indexOf('['); + if (arrayBracket >= 0) { + suffix = param.type.substring(arrayBracket); + } + + const result = parseEthersParams(param.components); + names.push({ name: param.name || null, names: result.names }); + types.push('tuple(' + result.types.join(',') + ')' + suffix); + } else { + names.push(param.name || null); + types.push(param.type); + } + }); + + return { + names, + types, + }; +} + +// returns true if x is equal to y and false otherwise. Performs some minimal +// type conversion and data massaging for x and y, depending on type. name and +// type should typically be derived from parseEthersParams. +function isAbiDataEqual(name: ethers.ParamName, type: string, x: any, y: any): boolean { + if (_.isUndefined(x) && _.isUndefined(y)) { + return true; + } else if (_.isUndefined(x) && !_.isUndefined(y)) { + return false; + } else if (!_.isUndefined(x) && _.isUndefined(y)) { + return false; + } + if (_.endsWith(type, '[]')) { + // For array types, we iterate through the elements and check each one + // individually. Strangely, name does not need to be changed in this + // case. + if (x.length !== y.length) { + return false; + } + const newType = _.trimEnd(type, '[]'); + for (let i = 0; i < x.length; i++) { + if (!isAbiDataEqual(name, newType, x[i], y[i])) { + return false; + } + } + return true; + } + if (_.startsWith(type, 'tuple(')) { + if (_.isString(name)) { + throw new Error('Internal error: type was tuple but names was a string'); + } else if (_.isNull(name)) { + throw new Error('Internal error: type was tuple but names was null'); + } + // For tuples, we iterate through the underlying values and check each + // one individually. + const types = splitTupleTypes(type); + if (types.length !== name.names.length) { + throw new Error( + `Internal error: parameter types/names length mismatch (${types.length} != ${name.names.length})`, + ); + } + for (let i = 0; i < types.length; i++) { + // For tuples, name is an object with a names property that is an + // array. As an example, for orders, name looks like: + // + // { + // name: 'orders', + // names: [ + // 'makerAddress', + // // ... + // 'takerAssetData' + // ] + // } + // + const nestedName = _.isString(name.names[i]) + ? (name.names[i] as string) + : ((name.names[i] as ethers.NestedParamName).name as string); + if (!isAbiDataEqual(name.names[i], types[i], x[nestedName], y[nestedName])) { + return false; + } + } + return true; + } else if (type === 'address' || type === 'bytes') { + // HACK(albrow): ethers.js returns the checksummed address even when + // initially passed in a non-checksummed address. To account for that, + // we convert to lowercase before comparing. + return _.isEqual(_.toLower(x), _.toLower(y)); + } else if (_.startsWith(type, 'uint') || _.startsWith(type, 'int')) { + return new BigNumber(x).eq(new BigNumber(y)); + } + return _.isEqual(x, y); +} + +// splitTupleTypes splits a tuple type string (of the form `tuple(X)` where X is +// any other type or list of types) into its component types. It works with +// nested tuples, so, e.g., `tuple(tuple(uint256,address),bytes32)` will yield: +// `['tuple(uint256,address)', 'bytes32']`. It expects exactly one tuple type as +// an argument (not an array). +function splitTupleTypes(type: string): string[] { + if (_.endsWith(type, '[]')) { + throw new Error('Internal error: array types are not supported'); + } else if (!_.startsWith(type, 'tuple(')) { + throw new Error('Internal error: expected tuple type but got non-tuple type: ' + type); + } + // Trim the outtermost tuple(). + const trimmedType = type.substring('tuple('.length, type.length - 1); + const types: string[] = []; + let currToken = ''; + let parenCount = 0; + // Tokenize the type string while keeping track of parentheses. + for (const char of trimmedType) { + switch (char) { + case '(': + parenCount += 1; + currToken += char; + break; + case ')': + parenCount -= 1; + currToken += char; + break; + case ',': + if (parenCount === 0) { + types.push(currToken); + currToken = ''; + break; + } else { + currToken += char; + break; + } + default: + currToken += char; + break; + } + } + types.push(currToken); + return types; +} + export const abiUtils = { + parseEthersParams, + isAbiDataEqual, + splitTupleTypes, parseFunctionParam(param: DataItem): string { if (param.type === 'tuple') { // Parse out tuple types into {type_1, type_2, ..., type_N} diff --git a/packages/utils/test/abi_utils_test.ts b/packages/utils/test/abi_utils_test.ts new file mode 100644 index 000000000..0ebee64c4 --- /dev/null +++ b/packages/utils/test/abi_utils_test.ts @@ -0,0 +1,19 @@ +import * as chai from 'chai'; +import 'mocha'; + +import { abiUtils } from '../src'; + +const expect = chai.expect; + +describe('abiUtils', () => { + describe('splitTupleTypes', () => { + it('handles basic types', () => { + const got = abiUtils.splitTupleTypes('tuple(bytes,uint256,address)'); + expect(got).to.deep.equal(['bytes', 'uint256', 'address']); + }); + it('handles nested tuple types', () => { + const got = abiUtils.splitTupleTypes('tuple(tuple(bytes,uint256),address)'); + expect(got).to.deep.equal(['tuple(bytes,uint256)', 'address']); + }); + }); +}); diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json index c56d255d5..8b4cd47a2 100644 --- a/packages/utils/tsconfig.json +++ b/packages/utils/tsconfig.json @@ -3,5 +3,5 @@ "compilerOptions": { "outDir": "lib" }, - "include": ["./src/**/*"] + "include": ["src/**/*", "test/**/*"] } |