diff options
Diffstat (limited to 'packages/contracts')
19 files changed, 448 insertions, 271 deletions
diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 51850ec69..410735359 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -30,9 +30,9 @@ }, "config": { "abis": - "../migrations/src/artifacts/@(DummyERC20Token|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|TestAssetProxyDispatcher|ERC20Proxy|ERC721Proxy|DummyERC721Token|LibBytes|Authorizable).json", + "../migrations/src/artifacts/@(DummyERC20Token|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|TestAssetProxyDispatcher|TestLibs|TestSignatureValidator|ERC20Proxy|ERC721Proxy|DummyERC721Token|LibBytes|Authorizable).json", "contracts": - "Exchange,DummyERC20Token,ZRXToken,WETH9,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,TokenRegistry,TestAssetProxyDispatcher,ERC20Proxy,ERC721Proxy,DummyERC721Token,LibBytes,Authorizable" + "Exchange,DummyERC20Token,ZRXToken,WETH9,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,TokenRegistry,TestAssetProxyDispatcher,TestLibs,TestSignatureValidator,ERC20Proxy,ERC721Proxy,DummyERC721Token,LibBytes,Authorizable" }, "repository": { "type": "git", diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol index 47f9e7a4d..e78446b99 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol @@ -29,8 +29,8 @@ import "./MixinTransactions.sol"; contract Exchange is MixinWrapperFunctions, MixinExchangeCore, - MixinSignatureValidator, MixinSettlement, + MixinSignatureValidator, MixinTransactions, MixinAssetProxyDispatcher { diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/ISigner.sol b/packages/contracts/src/contracts/current/protocol/Exchange/ISigner.sol index de3989a96..d5641a09f 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/ISigner.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/ISigner.sol @@ -23,7 +23,8 @@ contract ISigner { function isValidSignature( bytes32 hash, - bytes memory signature) - public view + bytes signature) + external + view returns (bool isValid); } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibMath.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibMath.sol new file mode 100644 index 000000000..3b553ebbc --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibMath.sol @@ -0,0 +1,71 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.21; +pragma experimental ABIEncoderV2; + +import "../../utils/SafeMath/SafeMath.sol"; + +contract LibMath is SafeMath { + + /// @dev Calculates partial value given a numerator and denominator. + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to calculate partial of. + /// @return Partial value of target. + function getPartialAmount( + uint256 numerator, + uint256 denominator, + uint256 target) + internal + pure + returns (uint256 partialAmount) + { + partialAmount = safeDiv( + safeMul(numerator, target), + denominator + ); + return partialAmount; + } + + /// @dev Checks if rounding error > 0.1%. + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to multiply with numerator/denominator. + /// @return Rounding error is present. + function isRoundingError( + uint256 numerator, + uint256 denominator, + uint256 target) + internal + pure + returns (bool isError) + { + uint256 remainder = mulmod(target, numerator, denominator); + if (remainder == 0) { + return false; // No rounding error. + } + + uint256 errPercentageTimes1000000 = safeDiv( + safeMul(remainder, 1000000), + safeMul(numerator, target) + ); + isError = errPercentageTimes1000000 > 1000; + return isError; + } +} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol index a98d7cbeb..df974e177 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol @@ -55,8 +55,9 @@ contract LibOrder { /// @dev Calculates Keccak-256 hash of the order. /// @param order The order structure. /// @return Keccak-256 EIP712 hash of the order. - function getOrderHash(Order order) - public view + function getOrderHash(Order memory order) + internal + view returns (bytes32 orderHash) { // TODO: EIP712 is not finalized yet diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol index b60a1cb08..b03bf464a 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -78,7 +78,8 @@ contract MixinAssetProxyDispatcher is /// @param assetProxyId Id of the asset proxy. /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. function getAssetProxy(uint8 assetProxyId) - external view + external + view returns (address) { IAssetProxy assetProxy = assetProxies[assetProxyId]; diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol index af114930b..4ca271b2a 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol @@ -19,11 +19,10 @@ pragma solidity ^0.4.21; pragma experimental ABIEncoderV2; -import "../../utils/SafeMath/SafeMath.sol"; import "./LibFillResults.sol"; import "./LibOrder.sol"; import "./LibErrors.sol"; -import "./LibPartialAmount.sol"; +import "./LibMath.sol"; import "./mixins/MExchangeCore.sol"; import "./mixins/MSettlement.sol"; import "./mixins/MSignatureValidator.sol"; @@ -33,11 +32,10 @@ import "./mixins/MTransactions.sol"; /// @dev Consumes MSettlement /// @dev Consumes MSignatureValidator contract MixinExchangeCore is - SafeMath, LibOrder, LibFillResults, LibErrors, - LibPartialAmount, + LibMath, MExchangeCore, MSettlement, MSignatureValidator, @@ -219,28 +217,6 @@ contract MixinExchangeCore is emit CancelUpTo(msg.sender, newMakerEpoch); } - /// @dev Checks if rounding error > 0.1%. - /// @param numerator Numerator. - /// @param denominator Denominator. - /// @param target Value to multiply with numerator/denominator. - /// @return Rounding error is present. - function isRoundingError(uint256 numerator, uint256 denominator, uint256 target) - public pure - returns (bool isError) - { - uint256 remainder = mulmod(target, numerator, denominator); - if (remainder == 0) { - return false; // No rounding error. - } - - uint256 errPercentageTimes1000000 = safeDiv( - safeMul(remainder, 1000000), - safeMul(numerator, target) - ); - isError = errPercentageTimes1000000 > 1000; - return isError; - } - /// @dev Logs a Fill event with the given arguments. /// The sole purpose of this function is to get around the stack variable limit. function emitFillEvent( diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol index df86a9dfa..cab2ccfb6 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol @@ -22,19 +22,20 @@ pragma experimental ABIEncoderV2; import "./mixins/MSettlement.sol"; import "./mixins/MAssetProxyDispatcher.sol"; import "./LibOrder.sol"; -import "./LibPartialAmount.sol"; +import "./LibMath.sol"; import "../AssetProxy/IAssetProxy.sol"; /// @dev Provides MixinSettlement contract MixinSettlement is - LibPartialAmount, + LibMath, MSettlement, MAssetProxyDispatcher { - bytes ZRX_PROXY_DATA; + bytes internal ZRX_PROXY_DATA; function zrxProxyData() - external view + external + view returns (bytes memory) { return ZRX_PROXY_DATA; diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol index 46bf01e78..690a70820 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol @@ -24,6 +24,7 @@ import "./ISigner.sol"; /// @dev Provides MSignatureValidator contract MixinSignatureValidator is MSignatureValidator { + enum SignatureType { Illegal, // Default value Invalid, @@ -47,7 +48,8 @@ contract MixinSignatureValidator is MSignatureValidator { bytes32 hash, address signer, bytes memory signature) - public view + internal + view returns (bool isValid) { // TODO: Domain separation: make hash depend on role. (Taker sig should not be valid as maker sig, etc.) @@ -164,8 +166,8 @@ contract MixinSignatureValidator is MSignatureValidator { function preSign( bytes32 hash, address signer, - bytes memory signature) - public + bytes signature) + external { require(isValidSignature(hash, signer, signature)); preSigned[hash][signer] = true; diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol index fe58e3d18..105c0f0a0 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol @@ -19,19 +19,17 @@ pragma solidity ^0.4.21; pragma experimental ABIEncoderV2; -import "../../utils/SafeMath/SafeMath.sol"; import "../../utils/LibBytes/LibBytes.sol"; import "./mixins/MExchangeCore.sol"; -import "./LibPartialAmount.sol"; +import "./LibMath.sol"; import "./LibOrder.sol"; import "./LibFillResults.sol"; /// @dev Consumes MExchangeCore contract MixinWrapperFunctions is - SafeMath, LibOrder, LibFillResults, - LibPartialAmount, + LibMath, LibBytes, MExchangeCore { diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol index a46be9d0f..6d0a3cd38 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol @@ -55,6 +55,7 @@ contract MAssetProxyDispatcher { /// @param assetProxyId Id of the asset proxy. /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. function getAssetProxy(uint8 assetProxyId) - external view + external + view returns (address); } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol index 3f020e0a4..043a7da9c 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol @@ -30,6 +30,7 @@ contract MSignatureValidator { bytes32 hash, address signer, bytes memory signature) - public view + internal + view returns (bool isValid); } diff --git a/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol new file mode 100644 index 000000000..a5b327a90 --- /dev/null +++ b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol @@ -0,0 +1,80 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.21; +pragma experimental ABIEncoderV2; + +import "../../protocol/Exchange/LibMath.sol"; +import "../../protocol/Exchange/LibOrder.sol"; +import "../../protocol/Exchange/LibFillResults.sol"; + +contract TestLibs is + LibMath, + LibOrder, + LibFillResults +{ + function publicGetPartialAmount( + uint256 numerator, + uint256 denominator, + uint256 target) + public + pure + returns (uint256 partialAmount) + { + partialAmount = getPartialAmount( + numerator, + denominator, + target + ); + return partialAmount; + } + + function publicIsRoundingError( + uint256 numerator, + uint256 denominator, + uint256 target) + public + pure + returns (bool isError) + { + isError = isRoundingError( + numerator, + denominator, + target + ); + return isError; + } + + function publicGetOrderHash(Order memory order) + public + view + returns (bytes32 orderHash) + { + orderHash = getOrderHash(order); + return orderHash; + } + + function publicAddFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults) + public + pure + returns (FillResults memory) + { + addFillResults(totalFillResults, singleFillResults); + return totalFillResults; + } +}
\ No newline at end of file diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol b/packages/contracts/src/contracts/current/test/TestSignatureValidator/TestSignatureValidator.sol index 2ff244e98..4ba04b64e 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol +++ b/packages/contracts/src/contracts/current/test/TestSignatureValidator/TestSignatureValidator.sol @@ -19,20 +19,23 @@ pragma solidity ^0.4.21; pragma experimental ABIEncoderV2; -import "../../utils/SafeMath/SafeMath.sol"; - -contract LibPartialAmount is SafeMath { - - /// @dev Calculates partial value given a numerator and denominator. - /// @param numerator Numerator. - /// @param denominator Denominator. - /// @param target Value to calculate partial of. - /// @return Partial value of target. - function getPartialAmount(uint256 numerator, uint256 denominator, uint256 target) - public pure - returns (uint256 partialAmount) +import "../../protocol/Exchange/MixinSignatureValidator.sol"; + +contract TestSignatureValidator is MixinSignatureValidator { + + function publicIsValidSignature( + bytes32 hash, + address signer, + bytes memory signature) + public + view + returns (bool isValid) { - partialAmount = safeDiv(safeMul(numerator, target), denominator); - return partialAmount; + isValid = isValidSignature( + hash, + signer, + signature + ); + return isValid; } -} +}
\ No newline at end of file diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts index 000905854..27fdd698f 100644 --- a/packages/contracts/src/utils/exchange_wrapper.ts +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -221,37 +221,6 @@ export class ExchangeWrapper { const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); return tx; } - public async getOrderHashAsync(signedOrder: SignedOrder): Promise<string> { - const order = orderUtils.getOrderStruct(signedOrder); - const orderHash = await this._exchange.getOrderHash.callAsync(order); - return orderHash; - } - public async isValidSignatureAsync(signedOrder: SignedOrder): Promise<boolean> { - const isValidSignature = await this._exchange.isValidSignature.callAsync( - orderUtils.getOrderHashHex(signedOrder), - signedOrder.makerAddress, - signedOrder.signature, - ); - return isValidSignature; - } - public async isRoundingErrorAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<boolean> { - const isRoundingError = await this._exchange.isRoundingError.callAsync(numerator, denominator, target); - return isRoundingError; - } - public async getPartialAmountAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - const partialAmount = new BigNumber( - await this._exchange.getPartialAmount.callAsync(numerator, denominator, target), - ); - return partialAmount; - } public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> { const filledAmount = new BigNumber(await this._exchange.filled.callAsync(orderHashHex)); return filledAmount; diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 934dc52fa..6f9aeda94 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -94,6 +94,8 @@ export enum ContractName { EtherDelta = 'EtherDelta', Arbitrage = 'Arbitrage', TestAssetProxyDispatcher = 'TestAssetProxyDispatcher', + TestLibs = 'TestLibs', + TestSignatureValidator = 'TestSignatureValidator', ERC20Proxy = 'ERC20Proxy', ERC721Proxy = 'ERC721Proxy', DummyERC721Token = 'DummyERC721Token', diff --git a/packages/contracts/test/exchange/helpers.ts b/packages/contracts/test/exchange/helpers.ts deleted file mode 100644 index f986665ff..000000000 --- a/packages/contracts/test/exchange/helpers.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { ZeroEx } from '0x.js'; -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import ethUtil = require('ethereumjs-util'); - -import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; -import { addressUtils } from '../../src/utils/address_utils'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; -import { constants } from '../../src/utils/constants'; -import { ExchangeWrapper } from '../../src/utils/exchange_wrapper'; -import { OrderFactory } from '../../src/utils/order_factory'; -import { orderUtils } from '../../src/utils/order_utils'; -import { AssetProxyId, ContractName, SignedOrder } from '../../src/utils/types'; -import { chaiSetup } from '../utils/chai_setup'; -import { deployer } from '../utils/deployer'; -import { provider, web3Wrapper } from '../utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; - -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('Exchange helpers', () => { - let signedOrder: SignedOrder; - let exchangeWrapper: ExchangeWrapper; - let orderFactory: OrderFactory; - - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const makerAddress = accounts[0]; - const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [AssetProxyId.ERC20]); - const exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider); - const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID }); - exchangeWrapper = new ExchangeWrapper(exchange, zeroEx); - - const defaultOrderParams = { - ...constants.STATIC_ORDER_PARAMS, - exchangeAddress: exchange.address, - makerAddress, - feeRecipientAddress: addressUtils.generatePseudoRandomAddress(), - makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), - }; - const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; - orderFactory = new OrderFactory(privateKey, defaultOrderParams); - }); - - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - signedOrder = orderFactory.newSignedOrder(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - - describe('getOrderHash', () => { - it('should output the correct orderHash', async () => { - const orderHashHex = await exchangeWrapper.getOrderHashAsync(signedOrder); - expect(orderUtils.getOrderHashHex(signedOrder)).to.be.equal(orderHashHex); - }); - }); - - describe('isValidSignature', () => { - beforeEach(async () => { - signedOrder = orderFactory.newSignedOrder(); - }); - - it('should return true with a valid signature', async () => { - const success = await exchangeWrapper.isValidSignatureAsync(signedOrder); - expect(success).to.be.true(); - }); - - it('should return false with an invalid signature', async () => { - const invalidR = ethUtil.sha3('invalidR'); - const invalidS = ethUtil.sha3('invalidS'); - const invalidSigBuff = Buffer.concat([ - ethUtil.toBuffer(signedOrder.signature.slice(0, 6)), - invalidR, - invalidS, - ]); - const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; - signedOrder.signature = invalidSigHex; - const success = await exchangeWrapper.isValidSignatureAsync(signedOrder); - expect(success).to.be.false(); - }); - }); - - describe('isRoundingError', () => { - it('should return false if there is a rounding error of 0.1%', async () => { - const numerator = new BigNumber(20); - const denominator = new BigNumber(999); - const target = new BigNumber(50); - // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); - }); - - it('should return false if there is a rounding of 0.09%', async () => { - const numerator = new BigNumber(20); - const denominator = new BigNumber(9991); - const target = new BigNumber(500); - // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); - }); - - it('should return true if there is a rounding error of 0.11%', async () => { - const numerator = new BigNumber(20); - const denominator = new BigNumber(9989); - const target = new BigNumber(500); - // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.true(); - }); - - it('should return true if there is a rounding error > 0.1%', async () => { - const numerator = new BigNumber(3); - const denominator = new BigNumber(7); - const target = new BigNumber(10); - // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.true(); - }); - - it('should return false when there is no rounding error', async () => { - const numerator = new BigNumber(1); - const denominator = new BigNumber(2); - const target = new BigNumber(10); - - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); - }); - - it('should return false when there is rounding error <= 0.1%', async () => { - // randomly generated numbers - const numerator = new BigNumber(76564); - const denominator = new BigNumber(676373677); - const target = new BigNumber(105762562); - // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) / - // (76564*105762562/676373677) = 0.0007% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); - }); - }); - - describe('getPartialAmount', () => { - it('should return the numerator/denominator*target', async () => { - const numerator = new BigNumber(1); - const denominator = new BigNumber(2); - const target = new BigNumber(10); - - const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target); - const expectedPartialAmount = 5; - expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); - }); - - it('should round down', async () => { - const numerator = new BigNumber(2); - const denominator = new BigNumber(3); - const target = new BigNumber(10); - - const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target); - const expectedPartialAmount = 6; - expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); - }); - - it('should round .5 down', async () => { - const numerator = new BigNumber(1); - const denominator = new BigNumber(20); - const target = new BigNumber(10); - - const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target); - const expectedPartialAmount = 0; - expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); - }); - }); -}); diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts new file mode 100644 index 000000000..0074cdff7 --- /dev/null +++ b/packages/contracts/test/exchange/libs.ts @@ -0,0 +1,155 @@ +import { ZeroEx } from '0x.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import ethUtil = require('ethereumjs-util'); + +import { TestLibsContract } from '../../src/contract_wrappers/generated/test_libs'; +import { addressUtils } from '../../src/utils/address_utils'; +import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; +import { constants } from '../../src/utils/constants'; +import { OrderFactory } from '../../src/utils/order_factory'; +import { orderUtils } from '../../src/utils/order_utils'; +import { ContractName, SignedOrder } from '../../src/utils/types'; +import { chaiSetup } from '../utils/chai_setup'; +import { deployer } from '../utils/deployer'; +import { provider, web3Wrapper } from '../utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; + +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('Exchange libs', () => { + let signedOrder: SignedOrder; + let orderFactory: OrderFactory; + let libs: TestLibsContract; + + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const makerAddress = accounts[0]; + const libsInstance = await deployer.deployAsync(ContractName.TestLibs); + libs = new TestLibsContract(libsInstance.abi, libsInstance.address, provider); + const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID }); + + const defaultOrderParams = { + ...constants.STATIC_ORDER_PARAMS, + exchangeAddress: libs.address, + makerAddress, + feeRecipientAddress: addressUtils.generatePseudoRandomAddress(), + makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + }; + const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; + orderFactory = new OrderFactory(privateKey, defaultOrderParams); + }); + + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + signedOrder = orderFactory.newSignedOrder(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + + describe('LibOrder', () => { + describe('getOrderHash', () => { + it('should output the correct orderHash', async () => { + const orderHashHex = await libs.publicGetOrderHash.callAsync(signedOrder); + expect(orderUtils.getOrderHashHex(signedOrder)).to.be.equal(orderHashHex); + }); + }); + }); + + describe('LibMath', () => { + describe('isRoundingError', () => { + it('should return false if there is a rounding error of 0.1%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(999); + const target = new BigNumber(50); + // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + + it('should return false if there is a rounding of 0.09%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(9991); + const target = new BigNumber(500); + // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + + it('should return true if there is a rounding error of 0.11%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(9989); + const target = new BigNumber(500); + // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); + + it('should return true if there is a rounding error > 0.1%', async () => { + const numerator = new BigNumber(3); + const denominator = new BigNumber(7); + const target = new BigNumber(10); + // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); + + it('should return false when there is no rounding error', async () => { + const numerator = new BigNumber(1); + const denominator = new BigNumber(2); + const target = new BigNumber(10); + + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + + it('should return false when there is rounding error <= 0.1%', async () => { + // randomly generated numbers + const numerator = new BigNumber(76564); + const denominator = new BigNumber(676373677); + const target = new BigNumber(105762562); + // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) / + // (76564*105762562/676373677) = 0.0007% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + }); + + describe('getPartialAmount', () => { + it('should return the numerator/denominator*target', async () => { + const numerator = new BigNumber(1); + const denominator = new BigNumber(2); + const target = new BigNumber(10); + + const partialAmount = await libs.publicGetPartialAmount.callAsync(numerator, denominator, target); + const expectedPartialAmount = 5; + expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); + }); + + it('should round down', async () => { + const numerator = new BigNumber(2); + const denominator = new BigNumber(3); + const target = new BigNumber(10); + + const partialAmount = await libs.publicGetPartialAmount.callAsync(numerator, denominator, target); + const expectedPartialAmount = 6; + expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); + }); + + it('should round .5 down', async () => { + const numerator = new BigNumber(1); + const denominator = new BigNumber(20); + const target = new BigNumber(10); + + const partialAmount = await libs.publicGetPartialAmount.callAsync(numerator, denominator, target); + const expectedPartialAmount = 0; + expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); + }); + }); + }); +}); diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts new file mode 100644 index 000000000..f47999bfa --- /dev/null +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -0,0 +1,93 @@ +import { ZeroEx } from '0x.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import ethUtil = require('ethereumjs-util'); + +import { TestSignatureValidatorContract } from '../../src/contract_wrappers/generated/test_signature_validator'; +import { addressUtils } from '../../src/utils/address_utils'; +import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; +import { constants } from '../../src/utils/constants'; +import { OrderFactory } from '../../src/utils/order_factory'; +import { orderUtils } from '../../src/utils/order_utils'; +import { ContractName, SignedOrder } from '../../src/utils/types'; +import { chaiSetup } from '../utils/chai_setup'; +import { deployer } from '../utils/deployer'; +import { provider, web3Wrapper } from '../utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; + +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('MixinSignatureValidator', () => { + let signedOrder: SignedOrder; + let orderFactory: OrderFactory; + let signatureValidator: TestSignatureValidatorContract; + + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const makerAddress = accounts[0]; + const signatureValidatorInstance = await deployer.deployAsync(ContractName.TestSignatureValidator); + signatureValidator = new TestSignatureValidatorContract( + signatureValidatorInstance.abi, + signatureValidatorInstance.address, + provider, + ); + const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID }); + + const defaultOrderParams = { + ...constants.STATIC_ORDER_PARAMS, + exchangeAddress: signatureValidatorInstance.address, + makerAddress, + feeRecipientAddress: addressUtils.generatePseudoRandomAddress(), + makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + }; + const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; + orderFactory = new OrderFactory(privateKey, defaultOrderParams); + }); + + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + signedOrder = orderFactory.newSignedOrder(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + + describe('isValidSignature', () => { + beforeEach(async () => { + signedOrder = orderFactory.newSignedOrder(); + }); + + it('should return true with a valid signature', async () => { + const orderHashHex = orderUtils.getOrderHashHex(signedOrder); + const success = await signatureValidator.publicIsValidSignature.callAsync( + orderHashHex, + signedOrder.makerAddress, + signedOrder.signature, + ); + expect(success).to.be.true(); + }); + + it('should return false with an invalid signature', async () => { + const invalidR = ethUtil.sha3('invalidR'); + const invalidS = ethUtil.sha3('invalidS'); + const invalidSigBuff = Buffer.concat([ + ethUtil.toBuffer(signedOrder.signature.slice(0, 6)), + invalidR, + invalidS, + ]); + const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; + signedOrder.signature = invalidSigHex; + const orderHashHex = orderUtils.getOrderHashHex(signedOrder); + const success = await signatureValidator.publicIsValidSignature.callAsync( + orderHashHex, + signedOrder.makerAddress, + signedOrder.signature, + ); + expect(success).to.be.false(); + }); + }); +}); |