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(-) (limited to 'src') 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 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(+) (limited to 'src') 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 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 ++ 4 files changed, 95 insertions(+) create mode 100644 src/contract_wrappers/ether_token_wrapper.ts (limited to 'src') 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'); -- cgit 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 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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', ]); -- cgit 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 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'src') 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); -- cgit