From a2c495a9830170173e5024b2e8e44e99cc319d06 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 25 Jun 2017 14:48:48 -0700 Subject: Re-wrap BigNumber returned from web3 in `getBalanceInEthAsync` --- src/web3_wrapper.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/web3_wrapper.ts b/src/web3_wrapper.ts index 6bdca499f..afecbec7f 100644 --- a/src/web3_wrapper.ts +++ b/src/web3_wrapper.ts @@ -36,7 +36,8 @@ export class Web3Wrapper { } public async getBalanceInEthAsync(owner: string): Promise { const balanceInWei = await promisify(this.web3.eth.getBalance)(owner); - const balanceEth = this.web3.fromWei(balanceInWei, 'ether'); + let balanceEth = this.web3.fromWei(balanceInWei, 'ether'); + balanceEth = new BigNumber(balanceEth); return balanceEth; } public async doesContractExistAtAddressAsync(address: string): Promise { -- cgit v1.2.3 From 7d001240c1dae380294cf5664abaf5997e61c61c Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 25 Jun 2017 14:49:12 -0700 Subject: Add missing return comment --- src/contract_wrappers/token_wrapper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/contract_wrappers/token_wrapper.ts b/src/contract_wrappers/token_wrapper.ts index 29f9b2d1c..e34c624ab 100644 --- a/src/contract_wrappers/token_wrapper.ts +++ b/src/contract_wrappers/token_wrapper.ts @@ -28,6 +28,7 @@ export class TokenWrapper extends ContractWrapper { * Retrieves an owner's ERC20 token balance. * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed. * @param ownerAddress The hex encoded user Ethereum address whose balance you would like to check. + * @return The owner's ERC20 token balance in base units. */ public async getBalanceAsync(tokenAddress: string, ownerAddress: string): Promise { assert.isETHAddressHex('ownerAddress', ownerAddress); -- cgit v1.2.3 From 60b3f3e6dd39afe12884a17f9d978dd604a138b5 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 25 Jun 2017 14:50:11 -0700 Subject: Implement EtherTokenWrapper and tests, with deposit and withdraw methods --- src/0x.ts | 7 ++ src/contract_wrappers/ether_token_wrapper.ts | 76 +++++++++++++++++++++ src/types.ts | 8 +++ src/web3_wrapper.ts | 4 ++ test/ether_token_wrapper_test.ts | 99 ++++++++++++++++++++++++++++ 5 files changed, 194 insertions(+) create mode 100644 src/contract_wrappers/ether_token_wrapper.ts create mode 100644 test/ether_token_wrapper_test.ts diff --git a/src/0x.ts b/src/0x.ts index 3a06c7b5a..d7a01ba70 100644 --- a/src/0x.ts +++ b/src/0x.ts @@ -12,6 +12,7 @@ import {utils} from './utils/utils'; import {assert} from './utils/assert'; import {ExchangeWrapper} from './contract_wrappers/exchange_wrapper'; import {TokenRegistryWrapper} from './contract_wrappers/token_registry_wrapper'; +import {EtherTokenWrapper} from './contract_wrappers/ether_token_wrapper'; import {ecSignatureSchema} from './schemas/ec_signature_schema'; import {TokenWrapper} from './contract_wrappers/token_wrapper'; import {ECSignature, ZeroExError, Order, SignedOrder, Web3Provider} from './types'; @@ -46,6 +47,11 @@ export class ZeroEx { * An instance of the TokenWrapper class containing methods for interacting with any ERC20 token smart contract. */ public token: TokenWrapper; + /** + * An instance of the EtherTokenWrapper class containing methods for interacting with the + * wrapped ETH ERC20 token smart contract. + */ + public etherToken: EtherTokenWrapper; private _web3Wrapper: Web3Wrapper; /** * Verifies that the elliptic curve signature `signature` was generated @@ -145,6 +151,7 @@ export class ZeroEx { this.token = new TokenWrapper(this._web3Wrapper); this.exchange = new ExchangeWrapper(this._web3Wrapper, this.token); this.tokenRegistry = new TokenRegistryWrapper(this._web3Wrapper); + this.etherToken = new EtherTokenWrapper(this._web3Wrapper, this.token); } /** * Sets a new provider for the web3 instance used by 0x.js. Updating the provider will stop all diff --git a/src/contract_wrappers/ether_token_wrapper.ts b/src/contract_wrappers/ether_token_wrapper.ts new file mode 100644 index 000000000..e2ef0270d --- /dev/null +++ b/src/contract_wrappers/ether_token_wrapper.ts @@ -0,0 +1,76 @@ +import * as _ from 'lodash'; +import {Web3Wrapper} from '../web3_wrapper'; +import {ContractWrapper} from './contract_wrapper'; +import {TokenWrapper} from './token_wrapper'; +import {EtherTokenContract, ZeroExError} from '../types'; +import {assert} from '../utils/assert'; +import * as EtherTokenArtifacts from '../artifacts/EtherToken.json'; + +/** + * This class includes all the functionality related to interacting with a wrapped Ether ERC20 token contract. + * The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back. + */ +export class EtherTokenWrapper extends ContractWrapper { + private _etherTokenContractIfExists?: EtherTokenContract; + private _tokenWrapper: TokenWrapper; + constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper) { + super(web3Wrapper); + this._tokenWrapper = tokenWrapper; + } + /** + * Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens + * to the depositor address. These wrapped ETH tokens can be used in 0x trades and are redeemable for 1-to-1 + * for ETH. + * @param amountInWei Amount of ETH in Wei the caller wishes to deposit. + * @param depositor The hex encoded user Ethereum address that would like to make the deposit. + */ + public async depositAsync(amountInWei: BigNumber.BigNumber, depositor: string): Promise { + assert.isBigNumber('amountInWei', amountInWei); + await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper); + + const ethBalance = await this._web3Wrapper.getBalanceInEthAsync(depositor); + const ethBalanceInWei = this._web3Wrapper.toWei(ethBalance); + assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT); + + const wethContract = await this._getEtherTokenContractAsync(); + await wethContract.deposit({ + from: depositor, + value: amountInWei, + }); + } + /** + * Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the + * equivalent number of wrapped ETH tokens. + * @param amountInWei Amount of ETH in Wei the caller wishes to withdraw. + * @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawl. + */ + public async withdrawAsync(amountInWei: BigNumber.BigNumber, withdrawer: string): Promise { + assert.isBigNumber('amountInWei', amountInWei); + await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper); + + const wethContractAddress = await this.getContractAddressAsync(); + const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(wethContractAddress, withdrawer); + assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL); + + const wethContract = await this._getEtherTokenContractAsync(); + await wethContract.withdraw(amountInWei, { + from: withdrawer, + }); + } + /** + * Retrieves the Wrapped Ether token contract address + * @return The Wrapped Ether token contract address + */ + public async getContractAddressAsync(): Promise { + const wethContract = await this._getEtherTokenContractAsync(); + return wethContract.address; + } + private async _getEtherTokenContractAsync(): Promise { + if (!_.isUndefined(this._etherTokenContractIfExists)) { + return this._etherTokenContractIfExists; + } + const contractInstance = await this._instantiateContractIfExistsAsync((EtherTokenArtifacts as any)); + this._etherTokenContractIfExists = contractInstance as EtherTokenContract; + return this._etherTokenContractIfExists; + } +} diff --git a/src/types.ts b/src/types.ts index 2b7fba226..c21060e7f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,6 +19,8 @@ export const ZeroExError = strEnum([ 'ZRX_NOT_IN_TOKEN_REGISTRY', 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER', 'INSUFFICIENT_BALANCE_FOR_TRANSFER', + 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT', + 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL', 'INVALID_JUMP', 'OUT_OF_GAS', ]); @@ -140,6 +142,11 @@ export interface TokenRegistryContract extends ContractInstance { }; } +export interface EtherTokenContract extends ContractInstance { + deposit: (txOpts: TxOpts) => Promise; + withdraw: (amount: BigNumber.BigNumber, txOpts: TxOpts) => Promise; +} + export const SolidityTypes = strEnum([ 'address', 'uint256', @@ -255,6 +262,7 @@ export interface Token { export interface TxOpts { from: string; gas?: number; + value?: BigNumber.BigNumber; } export interface TokenAddressBySymbol { diff --git a/src/web3_wrapper.ts b/src/web3_wrapper.ts index afecbec7f..0a310aeee 100644 --- a/src/web3_wrapper.ts +++ b/src/web3_wrapper.ts @@ -34,6 +34,10 @@ export class Web3Wrapper { return undefined; } } + public toWei(ethAmount: BigNumber.BigNumber): BigNumber.BigNumber { + const balanceWei = this.web3.toWei(ethAmount, 'ether'); + return balanceWei; + } public async getBalanceInEthAsync(owner: string): Promise { const balanceInWei = await promisify(this.web3.eth.getBalance)(owner); let balanceEth = this.web3.fromWei(balanceInWei, 'ether'); diff --git a/test/ether_token_wrapper_test.ts b/test/ether_token_wrapper_test.ts new file mode 100644 index 000000000..730b85497 --- /dev/null +++ b/test/ether_token_wrapper_test.ts @@ -0,0 +1,99 @@ +import 'mocha'; +import * as chai from 'chai'; +import {chaiSetup} from './utils/chai_setup'; +import * as Web3 from 'web3'; +import * as BigNumber from 'bignumber.js'; +import promisify = require('es6-promisify'); +import {web3Factory} from './utils/web3_factory'; +import {ZeroEx, ZeroExError, Token} from '../src'; +import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(); + +describe.only('EtherTokenWrapper', () => { + let web3: Web3; + let zeroEx: ZeroEx; + let userAddresses: string[]; + let addressWithETH: string; + let wethContractAddress: string; + let depositETHAmount: BigNumber.BigNumber; + let depositWeiAmount: BigNumber.BigNumber; + let decimalPlaces: number; + before(async () => { + web3 = web3Factory.create(); + zeroEx = new ZeroEx(web3.currentProvider); + userAddresses = await promisify(web3.eth.getAccounts)(); + addressWithETH = userAddresses[0]; + wethContractAddress = await zeroEx.etherToken.getContractAddressAsync(); + depositETHAmount = new BigNumber(5); + depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(depositETHAmount); + decimalPlaces = 7; + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('#depositAsync', () => { + it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => { + const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); + expect(preETHBalance).to.be.bignumber.gt(0); + expect(preWETHBalance).to.be.bignumber.equal(0); + + await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH); + + const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); + + expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(depositWeiAmount); + const remainingETH = preETHBalance.minus(depositETHAmount); + return expect(postETHBalance.round(decimalPlaces)).to.be.bignumber.equal(remainingETH); + }); + it('should throw if user has insufficient ETH balance for deposit', async () => { + const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + + const overETHBalance = preETHBalance.add(5); + const overETHBalanceinWei = (zeroEx as any)._web3Wrapper.toWei(overETHBalance); + + return expect( + zeroEx.etherToken.depositAsync(overETHBalanceinWei, addressWithETH), + ).to.be.rejectedWith(ZeroExError.INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT); + }); + }); + describe('#withdrawAsync', () => { + it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => { + const ETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + + await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH); + + const expectedPreETHBalance = ETHBalance.minus(depositETHAmount); + const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); + expect(preETHBalance.round(decimalPlaces)).to.be.bignumber.equal(expectedPreETHBalance); + expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount); + + await zeroEx.etherToken.withdrawAsync(depositWeiAmount, addressWithETH); + + const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); + + expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0); + const expectedETHBalance = preETHBalance.add(depositETHAmount).round(decimalPlaces); + return expect(postETHBalance.round(decimalPlaces)).to.be.bignumber.equal(expectedETHBalance); + }); + it('should throw if user has insufficient WETH balance for withdrawl', async () => { + const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); + expect(preWETHBalance).to.be.bignumber.equal(0); + + const overWETHBalance = preWETHBalance.add(999999999); + + return expect( + zeroEx.etherToken.withdrawAsync(overWETHBalance, addressWithETH), + ).to.be.rejectedWith(ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL); + }); + }); +}); -- cgit v1.2.3 From 0f413febd3cf2798a9be1149b1ef7ddd3550f7d0 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 26 Jun 2017 10:30:09 -0700 Subject: Fix typo --- src/contract_wrappers/ether_token_wrapper.ts | 2 +- src/types.ts | 2 +- test/ether_token_wrapper_test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/contract_wrappers/ether_token_wrapper.ts b/src/contract_wrappers/ether_token_wrapper.ts index e2ef0270d..e8efbc9a6 100644 --- a/src/contract_wrappers/ether_token_wrapper.ts +++ b/src/contract_wrappers/ether_token_wrapper.ts @@ -50,7 +50,7 @@ export class EtherTokenWrapper extends ContractWrapper { const wethContractAddress = await this.getContractAddressAsync(); const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(wethContractAddress, withdrawer); - assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL); + assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL); const wethContract = await this._getEtherTokenContractAsync(); await wethContract.withdraw(amountInWei, { diff --git a/src/types.ts b/src/types.ts index c21060e7f..200e65d56 100644 --- a/src/types.ts +++ b/src/types.ts @@ -20,7 +20,7 @@ export const ZeroExError = strEnum([ 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER', 'INSUFFICIENT_BALANCE_FOR_TRANSFER', 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT', - 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL', + 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL', 'INVALID_JUMP', 'OUT_OF_GAS', ]); diff --git a/test/ether_token_wrapper_test.ts b/test/ether_token_wrapper_test.ts index 730b85497..60fe00925 100644 --- a/test/ether_token_wrapper_test.ts +++ b/test/ether_token_wrapper_test.ts @@ -93,7 +93,7 @@ describe.only('EtherTokenWrapper', () => { return expect( zeroEx.etherToken.withdrawAsync(overWETHBalance, addressWithETH), - ).to.be.rejectedWith(ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL); + ).to.be.rejectedWith(ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL); }); }); }); -- cgit v1.2.3 From efb5556e4ef22ea1e9200759cb0582b36ae8f2e8 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 26 Jun 2017 10:33:53 -0700 Subject: Rename INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL to INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL --- src/contract_wrappers/ether_token_wrapper.ts | 2 +- src/types.ts | 2 +- test/ether_token_wrapper_test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/contract_wrappers/ether_token_wrapper.ts b/src/contract_wrappers/ether_token_wrapper.ts index e2ef0270d..e8efbc9a6 100644 --- a/src/contract_wrappers/ether_token_wrapper.ts +++ b/src/contract_wrappers/ether_token_wrapper.ts @@ -50,7 +50,7 @@ export class EtherTokenWrapper extends ContractWrapper { const wethContractAddress = await this.getContractAddressAsync(); const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(wethContractAddress, withdrawer); - assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL); + assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL); const wethContract = await this._getEtherTokenContractAsync(); await wethContract.withdraw(amountInWei, { diff --git a/src/types.ts b/src/types.ts index c21060e7f..200e65d56 100644 --- a/src/types.ts +++ b/src/types.ts @@ -20,7 +20,7 @@ export const ZeroExError = strEnum([ 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER', 'INSUFFICIENT_BALANCE_FOR_TRANSFER', 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT', - 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL', + 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL', 'INVALID_JUMP', 'OUT_OF_GAS', ]); diff --git a/test/ether_token_wrapper_test.ts b/test/ether_token_wrapper_test.ts index 730b85497..60fe00925 100644 --- a/test/ether_token_wrapper_test.ts +++ b/test/ether_token_wrapper_test.ts @@ -93,7 +93,7 @@ describe.only('EtherTokenWrapper', () => { return expect( zeroEx.etherToken.withdrawAsync(overWETHBalance, addressWithETH), - ).to.be.rejectedWith(ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWL); + ).to.be.rejectedWith(ZeroExError.INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL); }); }); }); -- cgit v1.2.3 From e780664b40df55616e7b6ac6470fbd43e8421851 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 26 Jun 2017 10:34:45 -0700 Subject: Remove .only from describe --- test/ether_token_wrapper_test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ether_token_wrapper_test.ts b/test/ether_token_wrapper_test.ts index 60fe00925..37ec253bb 100644 --- a/test/ether_token_wrapper_test.ts +++ b/test/ether_token_wrapper_test.ts @@ -12,7 +12,7 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(); -describe.only('EtherTokenWrapper', () => { +describe('EtherTokenWrapper', () => { let web3: Web3; let zeroEx: ZeroEx; let userAddresses: string[]; -- cgit v1.2.3 From 41098c6a35cc483b30ddc0566ca18c98b1548364 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 26 Jun 2017 11:20:16 -0700 Subject: refactor getBalanceInEthAsync to getBalanceInWeiAsync and change the test assertions to check if the discrepancy between the existing ETH balance and expected balance is small enough to simply be the gas cost used by the transaction. --- src/contract_wrappers/ether_token_wrapper.ts | 3 +- src/web3_wrapper.ts | 9 +++--- test/ether_token_wrapper_test.ts | 43 ++++++++++++++++------------ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/contract_wrappers/ether_token_wrapper.ts b/src/contract_wrappers/ether_token_wrapper.ts index e8efbc9a6..76e7289b7 100644 --- a/src/contract_wrappers/ether_token_wrapper.ts +++ b/src/contract_wrappers/ether_token_wrapper.ts @@ -28,8 +28,7 @@ export class EtherTokenWrapper extends ContractWrapper { assert.isBigNumber('amountInWei', amountInWei); await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper); - const ethBalance = await this._web3Wrapper.getBalanceInEthAsync(depositor); - const ethBalanceInWei = this._web3Wrapper.toWei(ethBalance); + const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(depositor); assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT); const wethContract = await this._getEtherTokenContractAsync(); diff --git a/src/web3_wrapper.ts b/src/web3_wrapper.ts index 0a310aeee..630f0bef3 100644 --- a/src/web3_wrapper.ts +++ b/src/web3_wrapper.ts @@ -38,11 +38,10 @@ export class Web3Wrapper { const balanceWei = this.web3.toWei(ethAmount, 'ether'); return balanceWei; } - public async getBalanceInEthAsync(owner: string): Promise { - const balanceInWei = await promisify(this.web3.eth.getBalance)(owner); - let balanceEth = this.web3.fromWei(balanceInWei, 'ether'); - balanceEth = new BigNumber(balanceEth); - return balanceEth; + public async getBalanceInWeiAsync(owner: string): Promise { + let balanceInWei = await promisify(this.web3.eth.getBalance)(owner); + balanceInWei = new BigNumber(balanceInWei); + return balanceInWei; } public async doesContractExistAtAddressAsync(address: string): Promise { const code = await promisify(this.web3.eth.getCode)(address); diff --git a/test/ether_token_wrapper_test.ts b/test/ether_token_wrapper_test.ts index 60fe00925..ebce81e97 100644 --- a/test/ether_token_wrapper_test.ts +++ b/test/ether_token_wrapper_test.ts @@ -12,13 +12,18 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(); -describe.only('EtherTokenWrapper', () => { +// Since the address depositing/withdrawing ETH/WETH also needs to pay gas costs for the transaction, +// a small amount of ETH will be used to pay this gas cost. We therefore check that the difference between +// the expected balance and actual balance (given the amount of ETH deposited), only deviates by the amount +// required to pay gas costs. +const MAX_REASONABLE_GAS_COST_IN_WEI = 62237; + +describe('EtherTokenWrapper', () => { let web3: Web3; let zeroEx: ZeroEx; let userAddresses: string[]; let addressWithETH: string; let wethContractAddress: string; - let depositETHAmount: BigNumber.BigNumber; let depositWeiAmount: BigNumber.BigNumber; let decimalPlaces: number; before(async () => { @@ -27,8 +32,7 @@ describe.only('EtherTokenWrapper', () => { userAddresses = await promisify(web3.eth.getAccounts)(); addressWithETH = userAddresses[0]; wethContractAddress = await zeroEx.etherToken.getContractAddressAsync(); - depositETHAmount = new BigNumber(5); - depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(depositETHAmount); + depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(new BigNumber(5)); decimalPlaces = 7; }); beforeEach(async () => { @@ -39,25 +43,26 @@ describe.only('EtherTokenWrapper', () => { }); describe('#depositAsync', () => { it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => { - const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); expect(preETHBalance).to.be.bignumber.gt(0); expect(preWETHBalance).to.be.bignumber.equal(0); await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH); - const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const postETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(depositWeiAmount); - const remainingETH = preETHBalance.minus(depositETHAmount); - return expect(postETHBalance.round(decimalPlaces)).to.be.bignumber.equal(remainingETH); + const remainingETHInWei = preETHBalance.minus(depositWeiAmount); + const gasCost = remainingETHInWei.minus(postETHBalanceInWei); + expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI); }); it('should throw if user has insufficient ETH balance for deposit', async () => { - const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); - const overETHBalance = preETHBalance.add(5); - const overETHBalanceinWei = (zeroEx as any)._web3Wrapper.toWei(overETHBalance); + const extraETHBalance = (zeroEx as any)._web3Wrapper.toWei(5, 'ether'); + const overETHBalanceinWei = preETHBalance.add(extraETHBalance); return expect( zeroEx.etherToken.depositAsync(overETHBalanceinWei, addressWithETH), @@ -66,24 +71,26 @@ describe.only('EtherTokenWrapper', () => { }); describe('#withdrawAsync', () => { it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => { - const ETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const ETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH); - const expectedPreETHBalance = ETHBalance.minus(depositETHAmount); - const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount); + const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); - expect(preETHBalance.round(decimalPlaces)).to.be.bignumber.equal(expectedPreETHBalance); + let gasCost = expectedPreETHBalance.minus(preETHBalance); + expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI); expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount); await zeroEx.etherToken.withdrawAsync(depositWeiAmount, addressWithETH); - const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInEthAsync(addressWithETH); + const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); const postWETHBalanceInBaseUnits = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0); - const expectedETHBalance = preETHBalance.add(depositETHAmount).round(decimalPlaces); - return expect(postETHBalance.round(decimalPlaces)).to.be.bignumber.equal(expectedETHBalance); + const expectedETHBalance = preETHBalance.add(depositWeiAmount).round(decimalPlaces); + gasCost = expectedETHBalance.minus(postETHBalance); + expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI); }); it('should throw if user has insufficient WETH balance for withdrawl', async () => { const preWETHBalance = await zeroEx.token.getBalanceAsync(wethContractAddress, addressWithETH); -- cgit v1.2.3