From e3eedac99604bab6a4a79a0e8ccdae2d5d0c6ecf Mon Sep 17 00:00:00 2001 From: felix2feng Date: Thu, 23 Apr 2020 00:54:00 -0700 Subject: [PATCH 1/3] Complete sorthash --- .gitignore | 5 +- contracts/lib/AddressArrayUtils.sol | 19 ++++++ contracts/lib/HashUtils.sol | 17 ++++++ contracts/mocks/ArrayAddressUtilsMock.sol | 12 ++++ contracts/mocks/HashUtilsMock.sol | 10 ++++ package.json | 2 +- test/contracts/lib/addressArrayUtils.spec.ts | 63 ++++++++++++++++++++ test/contracts/lib/hashUtils.spec.ts | 52 ++++++++++++++++ utils/contracts.ts | 2 + utils/helpers/libraryMockHelper.ts | 23 +++++++ 10 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 contracts/lib/HashUtils.sol create mode 100644 contracts/mocks/ArrayAddressUtilsMock.sol create mode 100644 contracts/mocks/HashUtilsMock.sol create mode 100644 test/contracts/lib/addressArrayUtils.spec.ts create mode 100644 test/contracts/lib/hashUtils.spec.ts diff --git a/.gitignore b/.gitignore index 1ea003cd2..d6f98ab0f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /artifacts/ts /build /dist +/node_modules /transpiled /types/generated/* coverage.json @@ -30,5 +31,5 @@ yarn-error.log* # Snapshot blockchain/ -# Outputs -deployments/outputs.ts \ No newline at end of file +# Ouputs +deployments/outputs.ts diff --git a/contracts/lib/AddressArrayUtils.sol b/contracts/lib/AddressArrayUtils.sol index 4e8e19195..5d2a4ba33 100644 --- a/contracts/lib/AddressArrayUtils.sol +++ b/contracts/lib/AddressArrayUtils.sol @@ -211,4 +211,23 @@ library AddressArrayUtils { } return true; } + + /** + * Naive implementation of sort + * @return Returns a sorted array + */ + function sort(address[] memory A) internal pure returns (address[] memory) { + uint256 length = A.length; + address[] memory newAddresses = A; + for (uint256 i = 0; i < length; i++) { + for (uint256 j = i + 1; j < length; j++) { + if (newAddresses[i] > newAddresses[j]) { + address temp = newAddresses[i]; + newAddresses[i] = newAddresses[j]; + newAddresses[j] = temp; + } + } + } + return newAddresses; + } } diff --git a/contracts/lib/HashUtils.sol b/contracts/lib/HashUtils.sol new file mode 100644 index 000000000..1f5498b20 --- /dev/null +++ b/contracts/lib/HashUtils.sol @@ -0,0 +1,17 @@ +pragma solidity 0.5.7; + +import { AddressArrayUtils } from "./AddressArrayUtils.sol"; + + +library HashUtils { + using AddressArrayUtils for address[]; + + /** + * Sorts the address array and hashes it using the keccak256 hashing algorithm. + * @param a The input array + * @return Returns bytes32 hash + */ + function sortHash(address[] memory a) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(a.sort())); + } +} diff --git a/contracts/mocks/ArrayAddressUtilsMock.sol b/contracts/mocks/ArrayAddressUtilsMock.sol new file mode 100644 index 000000000..1460c61b8 --- /dev/null +++ b/contracts/mocks/ArrayAddressUtilsMock.sol @@ -0,0 +1,12 @@ +pragma solidity 0.5.7; + +import { AddressArrayUtils } from "../lib/AddressArrayUtils.sol"; + + +contract AddressArrayUtilsMock { + using AddressArrayUtils for address[]; + + function testSort(address[] calldata _addressArray) external pure returns(address[] memory) { + return _addressArray.sort(); + } +} \ No newline at end of file diff --git a/contracts/mocks/HashUtilsMock.sol b/contracts/mocks/HashUtilsMock.sol new file mode 100644 index 000000000..c87a29f10 --- /dev/null +++ b/contracts/mocks/HashUtilsMock.sol @@ -0,0 +1,10 @@ +pragma solidity 0.5.7; + +import { HashUtils } from "../lib/HashUtils.sol"; + + +contract HashUtilsMock { + function testSortHash(address[] calldata _addressArray) external pure returns(bytes32) { + return HashUtils.sortHash(_addressArray); + } +} \ No newline at end of file diff --git a/package.json b/package.json index e1b79fb91..3ce99abd4 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "test-nocompile": "yarn transpile && yarn truffle-test-contracts", "test-continuous": "yarn deploy-development && yarn test", "transpile": "tsc", - "truffle-test-contracts": "truffle test `find transpiled/test/contracts/lib -name '*.spec.js'`", + "truffle-test-contracts": "truffle test `find transpiled/test/contracts/lib -name 'hashUtils.spec.js'`", "prepublishOnly": "yarn dist", "flatten": "truffle-flattener contracts/managers/SocialTradingManager.sol" }, diff --git a/test/contracts/lib/addressArrayUtils.spec.ts b/test/contracts/lib/addressArrayUtils.spec.ts new file mode 100644 index 000000000..aa1f923d4 --- /dev/null +++ b/test/contracts/lib/addressArrayUtils.spec.ts @@ -0,0 +1,63 @@ +require('module-alias/register'); + +import * as _ from 'lodash'; +import * as chai from 'chai'; +import { Address } from 'set-protocol-utils'; + + +import ChaiSetup from '@utils/chaiSetup'; +import { BigNumberSetup } from '@utils/bigNumberSetup'; +import { AddressArrayUtilsMockContract } from '@utils/contracts'; + +import { LibraryMockHelper } from '@utils/helpers/libraryMockHelper'; + +BigNumberSetup.configure(); +ChaiSetup.configure(); +const { expect } = chai; + + +contract('AddressArrayUtils', accounts => { + const [ownerAccount] = accounts; + const libraryMockHelper = new LibraryMockHelper(ownerAccount); + + let addressArrayUtils: AddressArrayUtilsMockContract; + + beforeEach(async () => { + addressArrayUtils = await libraryMockHelper.deployAddressArrayUtilsMockAsync(); + }); + + describe('#testSort', async () => { + let subjectToSort: Address[]; + + beforeEach(async () => { + subjectToSort = [ + '0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb', + '0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84', + '0x5409ED021D9299bf6814279A6A1411A7e866A631', + ]; + }); + + async function subject(): Promise { + return addressArrayUtils.testSort.callAsync(subjectToSort); + } + + it('returns the sorted items', async () => { + const sortedResult = await subject(); + + const expectedResult = _.sortBy(subjectToSort); + + expect(JSON.stringify(sortedResult)).to.equal(JSON.stringify(expectedResult)); + }); + + describe('when the input is empty', async () => { + beforeEach(async () => { + subjectToSort = []; + }); + + it('returns an empty array', async () => { + const sortedResult = await subject(); + expect(JSON.stringify(sortedResult)).to.equal(JSON.stringify([])); + }); + }); + }); +}); diff --git a/test/contracts/lib/hashUtils.spec.ts b/test/contracts/lib/hashUtils.spec.ts new file mode 100644 index 000000000..9e89e2250 --- /dev/null +++ b/test/contracts/lib/hashUtils.spec.ts @@ -0,0 +1,52 @@ +require('module-alias/register'); + +import * as _ from 'lodash'; +import * as chai from 'chai'; +import { Address } from 'set-protocol-utils'; + + +import ChaiSetup from '@utils/chaiSetup'; +import { BigNumberSetup } from '@utils/bigNumberSetup'; +import { HashUtilsMockContract } from '@utils/contracts'; +import { getWeb3 } from '@utils/web3Helper'; + +import { LibraryMockHelper } from '@utils/helpers/libraryMockHelper'; + +BigNumberSetup.configure(); +ChaiSetup.configure(); +const { expect } = chai; +const web3 = getWeb3(); + +contract('HashUtils', accounts => { + const [ownerAccount] = accounts; + const libraryMockHelper = new LibraryMockHelper(ownerAccount); + + let hashUtils: HashUtilsMockContract; + + beforeEach(async () => { + hashUtils = await libraryMockHelper.deployHashUtilsMockAsync(); + }); + + describe('#testSortHash', async () => { + let subjectAddressArray: Address[]; + + beforeEach(async () => { + subjectAddressArray = [ + '0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84', + '0x5409ED021D9299bf6814279A6A1411A7e866A631', + ]; + }); + + async function subject(): Promise { + return hashUtils.testSortHash.callAsync(subjectAddressArray); + } + + it('returns the correct hash', async () => { + const result = await subject(); + + const sorted = _.sortBy(subjectAddressArray); + const expected = web3.utils.soliditySha3({ t: 'address', v: sorted }); + expect(JSON.stringify(result)).to.equal(JSON.stringify(expected)); + }); + }); +}); diff --git a/utils/contracts.ts b/utils/contracts.ts index 44963bfbc..aaf91c09f 100644 --- a/utils/contracts.ts +++ b/utils/contracts.ts @@ -1,3 +1,4 @@ +export { AddressArrayUtilsMockContract } from '../types/generated/address_array_utils_mock'; export { BaseContract } from '../types/base_contract'; export { BoundsLibraryMockContract } from '../types/generated/bounds_library_mock'; export { AddressToAddressWhiteListContract } from '../types/generated/address_to_address_white_list'; @@ -7,6 +8,7 @@ export { Bytes32LibraryMockContract } from '../types/generated/bytes32_library_m export { CommonMathMockContract } from '../types/generated/common_math_mock'; export { CommonValidationsLibraryMockContract } from '../types/generated/common_validations_library_mock'; export { CompoundUtilsMockContract } from '../types/generated/compound_utils_mock'; +export { HashUtilsMockContract } from '../types/generated/hash_utils_mock'; export { LimitOneUpgradeMockContract } from '../types/generated/limit_one_upgrade_mock'; export { OracleWhiteListContract } from '../types/generated/oracle_white_list'; export { ScaleValidationsMockContract } from '../types/generated/scale_validations_mock'; diff --git a/utils/helpers/libraryMockHelper.ts b/utils/helpers/libraryMockHelper.ts index 23f509923..7e8b54a95 100644 --- a/utils/helpers/libraryMockHelper.ts +++ b/utils/helpers/libraryMockHelper.ts @@ -1,11 +1,13 @@ import { Address } from 'set-protocol-utils'; import * as _ from 'lodash'; import { + AddressArrayUtilsMockContract, BoundsLibraryMockContract, Bytes32LibraryMockContract, CommonValidationsLibraryMockContract, CommonMathMockContract, CompoundUtilsMockContract, + HashUtilsMockContract, ScaleValidationsMockContract } from '../contracts'; import { BigNumber } from 'bignumber.js'; @@ -17,6 +19,7 @@ import { } from '../web3Helper'; import { ZERO } from '../constants'; +const AddressArrayUtilsMock = importArtifactsFromSource('AddressArrayUtilsMock'); const BoundsLibraryMock = importArtifactsFromSource('BoundsLibraryMock'); const Bytes32Library = importArtifactsFromSource('Bytes32Library'); const Bytes32LibraryMock = importArtifactsFromSource('Bytes32LibraryMock'); @@ -24,6 +27,7 @@ const CommonMathMock = importArtifactsFromSource('CommonMathMock'); const CommonValidationsLibrary = importArtifactsFromSource('CommonValidationsLibrary'); const CommonValidationsLibraryMock = importArtifactsFromSource('CommonValidationsLibraryMock'); const CompoundUtilsMock = importArtifactsFromSource('CompoundUtilsMock'); +const HashUtilsMock = importArtifactsFromSource('HashUtilsMock'); const ScaleValidationsMock = importArtifactsFromSource('ScaleValidationsMock'); @@ -36,6 +40,17 @@ export class LibraryMockHelper { /* ============ Deployment ============ */ + public async deployAddressArrayUtilsMockAsync( + from: Address = this._contractOwnerAddress + ): Promise { + const addressArrayUtils = await AddressArrayUtilsMock.new(txnFrom(from)); + + return new AddressArrayUtilsMockContract( + getContractInstance(addressArrayUtils), + txnFrom(from), + ); + } + public async deployCommonValidationsLibraryAsync( from: Address = this._contractOwnerAddress ): Promise { @@ -85,6 +100,14 @@ export class LibraryMockHelper { return new ScaleValidationsMockContract(getContractInstance(scaleValidationsMockContract), txnFrom(from)); } + public async deployHashUtilsMockAsync( + from: Address = this._contractOwnerAddress + ): Promise { + const hashUtilsMockContract = await HashUtilsMock.new(txnFrom(from)); + + return new HashUtilsMockContract(getContractInstance(hashUtilsMockContract), txnFrom(from)); + } + public async deployBoundsLibraryMockAsync( minBound: BigNumber, maxBound: BigNumber, From 6abb3069ee8ef0de78070eb6addf6bcc3f008c21 Mon Sep 17 00:00:00 2001 From: felix2feng Date: Thu, 23 Apr 2020 00:55:07 -0700 Subject: [PATCH 2/3] Tweaks --- .gitignore | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index d6f98ab0f..f3600b004 100644 --- a/.gitignore +++ b/.gitignore @@ -31,5 +31,5 @@ yarn-error.log* # Snapshot blockchain/ -# Ouputs +# Outputs deployments/outputs.ts diff --git a/package.json b/package.json index 3ce99abd4..e1b79fb91 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "test-nocompile": "yarn transpile && yarn truffle-test-contracts", "test-continuous": "yarn deploy-development && yarn test", "transpile": "tsc", - "truffle-test-contracts": "truffle test `find transpiled/test/contracts/lib -name 'hashUtils.spec.js'`", + "truffle-test-contracts": "truffle test `find transpiled/test/contracts/lib -name '*.spec.js'`", "prepublishOnly": "yarn dist", "flatten": "truffle-flattener contracts/managers/SocialTradingManager.sol" }, From fb7e767a18cdce146662ddd936af792091e8c9b5 Mon Sep 17 00:00:00 2001 From: felix2feng Date: Thu, 23 Apr 2020 00:55:32 -0700 Subject: [PATCH 3/3] Bump to 1.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e1b79fb91..f9b3ea34e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "set-protocol-contract-utils", - "version": "1.0.2", + "version": "1.0.3", "main": "dist/artifacts/index.js", "typings": "dist/typings/artifacts/index.d.ts", "files": [