From 173ba9b2b592408e57ea0bb8bec97014b5c68c41 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 2 Dec 2019 16:42:00 -0800 Subject: [PATCH] Add ChaiBridge unit tests --- .../contracts/test/TestChaiBridge.sol | 79 +++++++++++++++++++ contracts/asset-proxy/package.json | 2 +- contracts/asset-proxy/src/artifacts.ts | 2 + contracts/asset-proxy/src/wrappers.ts | 1 + contracts/asset-proxy/test/artifacts.ts | 2 + contracts/asset-proxy/test/chai_bridge.ts | 60 ++++++++++++++ contracts/asset-proxy/test/wrappers.ts | 1 + contracts/asset-proxy/tsconfig.json | 2 + packages/types/src/index.ts | 2 + 9 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 contracts/asset-proxy/contracts/test/TestChaiBridge.sol create mode 100644 contracts/asset-proxy/test/chai_bridge.ts diff --git a/contracts/asset-proxy/contracts/test/TestChaiBridge.sol b/contracts/asset-proxy/contracts/test/TestChaiBridge.sol new file mode 100644 index 0000000000..c3773c6a4d --- /dev/null +++ b/contracts/asset-proxy/contracts/test/TestChaiBridge.sol @@ -0,0 +1,79 @@ +/* + + Copyright 2019 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.5.9; +pragma experimental ABIEncoderV2; + +import "../src/bridges/ChaiBridge.sol"; +import "@0x/contracts-erc20/contracts/src/ERC20Token.sol"; + + +contract TestChaiDai is + ERC20Token +{ + address private ALWAYS_REVERT_ADDRESS = address(1); + + function draw( + address from, + uint256 amount + ) + external + { + if (from == ALWAYS_REVERT_ADDRESS) { + revert(); + } + balances[msg.sender] += amount; + } +} + +contract TestChaiBridge is + ChaiBridge +{ + address public testChaiDai; + address private ALWAYS_REVERT_ADDRESS = address(1); + + constructor() + public + { + testChaiDai = address(new TestChaiDai()); + } + + function _getDaiAddress() + internal + view + returns (address) + { + return testChaiDai; + } + + function _getChaiAddress() + internal + view + returns (address) + { + return testChaiDai; + } + + function _getERC20BridgeProxyAddress() + internal + view + returns (address) + { + return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender; + } +} diff --git a/contracts/asset-proxy/package.json b/contracts/asset-proxy/package.json index 68292102bb..18ddc9ab0c 100644 --- a/contracts/asset-proxy/package.json +++ b/contracts/asset-proxy/package.json @@ -39,7 +39,7 @@ }, "config": { "publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,KyberBridge,ChaiBridge,TestStaticCallTarget", - "abis": "./test/generated-artifacts/@(ChaiBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json", + "abis": "./test/generated-artifacts/@(ChaiBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/asset-proxy/src/artifacts.ts b/contracts/asset-proxy/src/artifacts.ts index 14ed1ee378..a9559e4bba 100644 --- a/contracts/asset-proxy/src/artifacts.ts +++ b/contracts/asset-proxy/src/artifacts.ts @@ -5,6 +5,7 @@ */ import { ContractArtifact } from 'ethereum-types'; +import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json'; import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json'; import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json'; import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json'; @@ -29,5 +30,6 @@ export const artifacts = { IAssetProxy: IAssetProxy as ContractArtifact, UniswapBridge: UniswapBridge as ContractArtifact, KyberBridge: KyberBridge as ContractArtifact, + ChaiBridge: ChaiBridge as ContractArtifact, TestStaticCallTarget: TestStaticCallTarget as ContractArtifact, }; diff --git a/contracts/asset-proxy/src/wrappers.ts b/contracts/asset-proxy/src/wrappers.ts index 7b01c84334..b335aa2f5b 100644 --- a/contracts/asset-proxy/src/wrappers.ts +++ b/contracts/asset-proxy/src/wrappers.ts @@ -3,6 +3,7 @@ * Warning: This file is auto-generated by contracts-gen. Don't edit manually. * ----------------------------------------------------------------------------- */ +export * from '../generated-wrappers/chai_bridge'; export * from '../generated-wrappers/erc1155_proxy'; export * from '../generated-wrappers/erc20_bridge_proxy'; export * from '../generated-wrappers/erc20_proxy'; diff --git a/contracts/asset-proxy/test/artifacts.ts b/contracts/asset-proxy/test/artifacts.ts index c9724ac0d7..1e9b719310 100644 --- a/contracts/asset-proxy/test/artifacts.ts +++ b/contracts/asset-proxy/test/artifacts.ts @@ -27,6 +27,7 @@ import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizabl import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json'; import * as Ownable from '../test/generated-artifacts/Ownable.json'; import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json'; +import * as TestChaiBridge from '../test/generated-artifacts/TestChaiBridge.json'; import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json'; import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json'; import * as TestKyberBridge from '../test/generated-artifacts/TestKyberBridge.json'; @@ -57,6 +58,7 @@ export const artifacts = { IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact, IUniswapExchange: IUniswapExchange as ContractArtifact, IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact, + TestChaiBridge: TestChaiBridge as ContractArtifact, TestERC20Bridge: TestERC20Bridge as ContractArtifact, TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact, TestKyberBridge: TestKyberBridge as ContractArtifact, diff --git a/contracts/asset-proxy/test/chai_bridge.ts b/contracts/asset-proxy/test/chai_bridge.ts new file mode 100644 index 0000000000..9bed1f9864 --- /dev/null +++ b/contracts/asset-proxy/test/chai_bridge.ts @@ -0,0 +1,60 @@ +import { ERC20TokenContract } from '@0x/contracts-erc20'; +import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils'; +import { AssetProxyId, RevertReason } from '@0x/types'; +import { BigNumber } from '@0x/utils'; + +import { artifacts } from './artifacts'; +import { TestChaiBridgeContract } from './wrappers'; + +blockchainTests.resets('ChaiBridge unit tests', env => { + let chaiBridgeContract: TestChaiBridgeContract; + let testDaiContract: ERC20TokenContract; + let fromAddress: string; + let toAddress: string; + + const alwaysRevertAddress = '0x0000000000000000000000000000000000000001'; + const amount = new BigNumber(1); + + before(async () => { + [fromAddress, toAddress] = await env.getAccountAddressesAsync(); + chaiBridgeContract = await TestChaiBridgeContract.deployFrom0xArtifactAsync( + artifacts.TestChaiBridge, + env.provider, + env.txDefaults, + artifacts, + ); + const testChaiDaiAddress = await chaiBridgeContract.testChaiDai().callAsync(); + testDaiContract = new ERC20TokenContract(testChaiDaiAddress, env.provider, env.txDefaults); + }); + + describe('bridgeTransferFrom()', () => { + it('fails if not called by ERC20BridgeProxy', async () => { + return expect( + chaiBridgeContract + .bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES) + .awaitTransactionSuccessAsync({ from: alwaysRevertAddress }), + ).to.revertWith(RevertReason.ChaiBridgeOnlyCallableByErc20BridgeProxy); + }); + it('returns magic bytes upon success', async () => { + const magicBytes = await chaiBridgeContract + .bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES) + .callAsync(); + expect(magicBytes).to.eq(AssetProxyId.ERC20Bridge); + }); + it('should increase the Dai balance of `toAddress` by `amount` if successful', async () => { + const initialBalance = await testDaiContract.balanceOf(toAddress).callAsync(); + await chaiBridgeContract + .bridgeTransferFrom(randomAddress(), fromAddress, toAddress, amount, constants.NULL_BYTES) + .awaitTransactionSuccessAsync(); + const endBalance = await testDaiContract.balanceOf(toAddress).callAsync(); + expect(endBalance).to.bignumber.eq(initialBalance.plus(amount)); + }); + it('fails if the `chai.draw` call fails', async () => { + return expect( + chaiBridgeContract + .bridgeTransferFrom(randomAddress(), alwaysRevertAddress, toAddress, amount, constants.NULL_BYTES) + .awaitTransactionSuccessAsync(), + ).to.revertWith(RevertReason.ChaiBridgeDrawDaiFailed); + }); + }); +}); diff --git a/contracts/asset-proxy/test/wrappers.ts b/contracts/asset-proxy/test/wrappers.ts index 20ec5ea23e..18085e0d6d 100644 --- a/contracts/asset-proxy/test/wrappers.ts +++ b/contracts/asset-proxy/test/wrappers.ts @@ -25,6 +25,7 @@ export * from '../test/generated-wrappers/mixin_authorizable'; export * from '../test/generated-wrappers/multi_asset_proxy'; export * from '../test/generated-wrappers/ownable'; export * from '../test/generated-wrappers/static_call_proxy'; +export * from '../test/generated-wrappers/test_chai_bridge'; export * from '../test/generated-wrappers/test_erc20_bridge'; export * from '../test/generated-wrappers/test_eth2_dai_bridge'; export * from '../test/generated-wrappers/test_kyber_bridge'; diff --git a/contracts/asset-proxy/tsconfig.json b/contracts/asset-proxy/tsconfig.json index 6c8b9358da..b7dfad1922 100644 --- a/contracts/asset-proxy/tsconfig.json +++ b/contracts/asset-proxy/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], "files": [ + "generated-artifacts/ChaiBridge.json", "generated-artifacts/ERC1155Proxy.json", "generated-artifacts/ERC20BridgeProxy.json", "generated-artifacts/ERC20Proxy.json", @@ -37,6 +38,7 @@ "test/generated-artifacts/MultiAssetProxy.json", "test/generated-artifacts/Ownable.json", "test/generated-artifacts/StaticCallProxy.json", + "test/generated-artifacts/TestChaiBridge.json", "test/generated-artifacts/TestERC20Bridge.json", "test/generated-artifacts/TestEth2DaiBridge.json", "test/generated-artifacts/TestKyberBridge.json", diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 3af01e21fb..66438e0026 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -338,6 +338,8 @@ export enum RevertReason { CustomTimeLockIncomplete = 'CUSTOM_TIME_LOCK_INCOMPLETE', EqualLengthsRequired = 'EQUAL_LENGTHS_REQUIRED', OnlyCallableByWallet = 'ONLY_CALLABLE_BY_WALLET', + ChaiBridgeOnlyCallableByErc20BridgeProxy = 'ChaiBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY', + ChaiBridgeDrawDaiFailed = 'ChaiBridge/DRAW_DAI_FAILED', } export enum StatusCodes {