Implement EtherTokenWrapper and tests, with deposit and withdraw methods
This commit is contained in:
@@ -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
|
||||
|
||||
76
src/contract_wrappers/ether_token_wrapper.ts
Normal file
76
src/contract_wrappers/ether_token_wrapper.ts
Normal file
@@ -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<void> {
|
||||
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<void> {
|
||||
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<string> {
|
||||
const wethContract = await this._getEtherTokenContractAsync();
|
||||
return wethContract.address;
|
||||
}
|
||||
private async _getEtherTokenContractAsync(): Promise<EtherTokenContract> {
|
||||
if (!_.isUndefined(this._etherTokenContractIfExists)) {
|
||||
return this._etherTokenContractIfExists;
|
||||
}
|
||||
const contractInstance = await this._instantiateContractIfExistsAsync((EtherTokenArtifacts as any));
|
||||
this._etherTokenContractIfExists = contractInstance as EtherTokenContract;
|
||||
return this._etherTokenContractIfExists;
|
||||
}
|
||||
}
|
||||
@@ -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<void>;
|
||||
withdraw: (amount: BigNumber.BigNumber, txOpts: TxOpts) => Promise<void>;
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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<BigNumber.BigNumber> {
|
||||
const balanceInWei = await promisify(this.web3.eth.getBalance)(owner);
|
||||
let balanceEth = this.web3.fromWei(balanceInWei, 'ether');
|
||||
|
||||
99
test/ether_token_wrapper_test.ts
Normal file
99
test/ether_token_wrapper_test.ts
Normal file
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user