diff options
-rw-r--r-- | src/0x.js.ts | 13 | ||||
-rw-r--r-- | src/contract_wrappers/erc20_wrapper.ts | 28 | ||||
-rw-r--r-- | src/types.ts | 6 | ||||
-rw-r--r-- | test/erc20_wrapper_test.ts | 55 |
4 files changed, 93 insertions, 9 deletions
diff --git a/src/0x.js.ts b/src/0x.js.ts index 69c0cc567..7932559fb 100644 --- a/src/0x.js.ts +++ b/src/0x.js.ts @@ -13,14 +13,16 @@ import compareVersions = require('compare-versions'); import {ExchangeWrapper} from './contract_wrappers/exchange_wrapper'; import {TokenRegistryWrapper} from './contract_wrappers/token_registry_wrapper'; import {ecSignatureSchema} from './schemas/ec_signature_schema'; +import {ERC20Wrapper} from './contract_wrappers/erc20_wrapper'; import {SolidityTypes, ECSignature, ZeroExError} from './types'; const MAX_DIGITS_IN_UNSIGNED_256_INT = 78; export class ZeroEx { + public web3Wrapper: Web3Wrapper; public exchange: ExchangeWrapper; public tokenRegistry: TokenRegistryWrapper; - private web3Wrapper: Web3Wrapper; + public erc20: ERC20Wrapper; /** * Computes the orderHash given the order parameters and returns it as a hex encoded string. */ @@ -135,14 +137,7 @@ export class ZeroEx { this.web3Wrapper = new Web3Wrapper(web3); this.exchange = new ExchangeWrapper(this.web3Wrapper); this.tokenRegistry = new TokenRegistryWrapper(this.web3Wrapper); - } - /** - * Sets a new provider for the web3 instance used by 0x.js - */ - public setProvider(provider: Web3.Provider) { - this.web3Wrapper.setProvider(provider); - this.exchange.invalidateContractInstance(); - this.tokenRegistry.invalidateContractInstance(); + this.erc20 = new ERC20Wrapper(this.web3Wrapper); } /** * Signs an orderHash and returns it's elliptic curve signature diff --git a/src/contract_wrappers/erc20_wrapper.ts b/src/contract_wrappers/erc20_wrapper.ts new file mode 100644 index 000000000..60b1887db --- /dev/null +++ b/src/contract_wrappers/erc20_wrapper.ts @@ -0,0 +1,28 @@ +import * as _ from 'lodash'; +import * as BigNumber from 'bignumber.js'; +import {Web3Wrapper} from '../web3_wrapper'; +import {assert} from '../utils/assert'; +import {ContractWrapper} from './contract_wrapper'; +import * as TokenArtifacts from '../artifacts/Token.json'; +import {ERC20Contract} from '../types'; + +export class ERC20Wrapper extends ContractWrapper { + constructor(web3Wrapper: Web3Wrapper) { + super(web3Wrapper); + } + /** + * Returns an owner's ERC20 token balance + */ + public async getBalanceAsync(tokenAddress: string, ownerAddress: string): Promise<BigNumber.BigNumber> { + assert.isETHAddressHex('ownerAddress', ownerAddress); + assert.isETHAddressHex('tokenAddress', tokenAddress); + + const contractInstance = await this.instantiateContractIfExistsAsync((TokenArtifacts as any), tokenAddress); + const tokenContract = contractInstance as ERC20Contract; + let balance = await tokenContract.balanceOf.call(ownerAddress); + // The BigNumber instance returned by Web3 is of a much older version then our own, we therefore + // should always re-instantiate the returned BigNumber after retrieval. + balance = _.isUndefined(balance) ? new BigNumber(0) : new BigNumber(balance); + return balance; + } +} diff --git a/src/types.ts b/src/types.ts index 6fce95706..9c82c1fa6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -30,6 +30,12 @@ export interface ExchangeContract { isValidSignature: any; } +export interface ERC20Contract { + balanceOf: { + call: (address: string) => Promise<BigNumber.BigNumber>; + }; +} + export interface TokenRegistryContract { getTokenMetaData: { call: (address: string) => Promise<TokenMetadata>; diff --git a/test/erc20_wrapper_test.ts b/test/erc20_wrapper_test.ts new file mode 100644 index 000000000..c28c057a7 --- /dev/null +++ b/test/erc20_wrapper_test.ts @@ -0,0 +1,55 @@ +import 'mocha'; +import * as chai from 'chai'; +import chaiAsPromised = require('chai-as-promised'); +import * as Web3 from 'web3'; +import * as BigNumber from 'bignumber.js'; +import promisify = require('es6-promisify'); +import {web3Factory} from './utils/web3_factory'; +import {ZeroEx} from '../src/0x.js'; +import {ZeroExError, Token} from '../src/types'; +import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; + +const expect = chai.expect; +chai.use(chaiAsPromised); +const blockchainLifecycle = new BlockchainLifecycle(); + +describe('ERC20Wrapper', () => { + let web3: Web3; + let zeroEx: ZeroEx; + let userAddresses: string[]; + let tokens: Token[]; + before(async () => { + web3 = web3Factory.create(); + zeroEx = new ZeroEx(web3); + userAddresses = await promisify(web3.eth.getAccounts)(); + tokens = await zeroEx.tokenRegistry.getTokensAsync(); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('#getBalanceAsync', () => { + it('should return the balance for an existing ERC20 token', async () => { + const aToken = tokens[0]; + const aOwnerAddress = userAddresses[0]; + const balance = await zeroEx.erc20.getBalanceAsync(aToken.address, aOwnerAddress); + const expectedBalance = new BigNumber('100000000000000000000000000'); + expect(balance).to.be.bignumber.equal(expectedBalance); + }); + it ('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { + const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; + const aOwnerAddress = userAddresses[0]; + expect(zeroEx.erc20.getBalanceAsync(nonExistentTokenAddress, aOwnerAddress)) + .to.be.rejectedWith(ZeroExError.CONTRACT_DOES_NOT_EXIST); + }); + it ('should return a balance of 0 for a non-existent owner address', async () => { + const aToken = tokens[0]; + const aNonExistentOwner = '0x198C6Ad858F213Fb31b6FE809E25040E6B964593'; + const balance = await zeroEx.erc20.getBalanceAsync(aToken.address, aNonExistentOwner); + const expectedBalance = new BigNumber('0'); + expect(balance).to.be.bignumber.equal(expectedBalance); + }); + }); +}); |