aboutsummaryrefslogblamecommitdiffstats
path: root/packages/contracts/src/utils/erc20_wrapper.ts
blob: ca2ca492b057d602dd6031a9849857b6019281d5 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                         
                                             
                                                      
                                          

                            

                                                                                             
 
                                        
                                        

                                               



                                           
                                      
                                
                                                            
                                                
                                      
                                                                                                  
                                       
                                                      



                                                          




                                                                                                                 

                                                                        




                                                 
                                  

                                                       

              
                                         
     
                                                                  

                                                                                 
                           
                       
          
                                                                                 
                                   
     



                                                   
                                                                 

                                                        



                                                                             



                                                             
                                                         
                  

                                                                          
                                                                            


                                                          
                                                         
                  

             
     


                                                                                              

                       









                                                                                                            
                         
     






                                                                                                              
                                                                    
                                                        
                                                         
                                         
                                                                                           


                                                                                               

                                      
                                                             
                   

             














                                                                                  
                                                                                                                  

                              








                                                                                                         
                                                              



                                                                                                         
                                                         



                                                                                                     
 
import { assetProxyUtils } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';

import { DummyERC20TokenContract } from '../generated_contract_wrappers/dummy_e_r_c20_token';
import { ERC20ProxyContract } from '../generated_contract_wrappers/e_r_c20_proxy';

import { artifacts } from './artifacts';
import { constants } from './constants';
import { ERC20BalancesByOwner } from './types';
import { txDefaults } from './web3_wrapper';

export class ERC20Wrapper {
    private _tokenOwnerAddresses: string[];
    private _contractOwnerAddress: string;
    private _web3Wrapper: Web3Wrapper;
    private _provider: Provider;
    private _dummyTokenContracts: DummyERC20TokenContract[];
    private _proxyContract?: ERC20ProxyContract;
    private _proxyIdIfExists?: number;
    constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
        this._dummyTokenContracts = [];
        this._web3Wrapper = new Web3Wrapper(provider);
        this._provider = provider;
        this._tokenOwnerAddresses = tokenOwnerAddresses;
        this._contractOwnerAddress = contractOwnerAddress;
    }
    public async deployDummyTokensAsync(num?: number, decimals?: BigNumber): Promise<DummyERC20TokenContract[]> {
        // TODO(fabio): Remove and refactor all tests
        const finalNum = _.isUndefined(num) ? constants.NUM_DUMMY_ERC20_TO_DEPLOY : num;
        const finalDecimals = _.isUndefined(decimals) ? constants.DUMMY_TOKEN_DECIMALS : decimals;
        for (let i = 0; i < finalNum; i++) {
            this._dummyTokenContracts.push(
                await DummyERC20TokenContract.deployFrom0xArtifactAsync(
                    artifacts.DummyERC20Token,
                    this._provider,
                    txDefaults,
                    constants.DUMMY_TOKEN_NAME,
                    constants.DUMMY_TOKEN_SYMBOL,
                    finalDecimals,
                    constants.DUMMY_TOKEN_TOTAL_SUPPLY,
                ),
            );
        }
        return this._dummyTokenContracts;
    }
    public async deployProxyAsync(): Promise<ERC20ProxyContract> {
        this._proxyContract = await ERC20ProxyContract.deployFrom0xArtifactAsync(
            artifacts.ERC20Proxy,
            this._provider,
            txDefaults,
        );
        this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
        return this._proxyContract;
    }
    public getProxyId(): number {
        this._validateProxyContractExistsOrThrow();
        return this._proxyIdIfExists as number;
    }
    public async setBalancesAndAllowancesAsync(): Promise<void> {
        this._validateDummyTokenContractsExistOrThrow();
        this._validateProxyContractExistsOrThrow();
        for (const dummyTokenContract of this._dummyTokenContracts) {
            for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
                await this._web3Wrapper.awaitTransactionSuccessAsync(
                    await dummyTokenContract.setBalance.sendTransactionAsync(
                        tokenOwnerAddress,
                        constants.INITIAL_ERC20_BALANCE,
                        { from: this._contractOwnerAddress },
                    ),
                    constants.AWAIT_TRANSACTION_MINED_MS,
                );
                await this._web3Wrapper.awaitTransactionSuccessAsync(
                    await dummyTokenContract.approve.sendTransactionAsync(
                        (this._proxyContract as ERC20ProxyContract).address,
                        constants.INITIAL_ERC20_ALLOWANCE,
                        { from: tokenOwnerAddress },
                    ),
                    constants.AWAIT_TRANSACTION_MINED_MS,
                );
            }
        }
    }
    public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
        const tokenContract = this._getTokenContractFromAssetData(assetData);
        const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress));
        return balance;
    }
    public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
        const tokenContract = this._getTokenContractFromAssetData(assetData);
        await tokenContract.setBalance.sendTransactionAsync(userAddress, amount, {
            from: this._contractOwnerAddress,
        });
    }
    public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
        const tokenContract = this._getTokenContractFromAssetData(assetData);
        const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
        const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress));
        return allowance;
    }
    public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise<void> {
        const tokenContract = this._getTokenContractFromAssetData(assetData);
        const proxyAddress = (this._proxyContract as ERC20ProxyContract).address;
        await tokenContract.approve.sendTransactionAsync(proxyAddress, amount, {
            from: userAddress,
        });
    }
    public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
        this._validateDummyTokenContractsExistOrThrow();
        const balancesByOwner: ERC20BalancesByOwner = {};
        const balances: BigNumber[] = [];
        const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = [];
        for (const dummyTokenContract of this._dummyTokenContracts) {
            for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
                balances.push(await dummyTokenContract.balanceOf.callAsync(tokenOwnerAddress));
                balanceInfo.push({
                    tokenOwnerAddress,
                    tokenAddress: dummyTokenContract.address,
                });
            }
        }
        _.forEach(balances, (balance, balanceIndex) => {
            const tokenAddress = balanceInfo[balanceIndex].tokenAddress;
            const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress;
            if (_.isUndefined(balancesByOwner[tokenOwnerAddress])) {
                balancesByOwner[tokenOwnerAddress] = {};
            }
            const wrappedBalance = new BigNumber(balance);
            balancesByOwner[tokenOwnerAddress][tokenAddress] = wrappedBalance;
        });
        return balancesByOwner;
    }
    public getTokenOwnerAddresses(): string[] {
        return this._tokenOwnerAddresses;
    }
    public getTokenAddresses(): string[] {
        const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address);
        return tokenAddresses;
    }
    private _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract {
        const erc20ProxyData = assetProxyUtils.decodeERC20AssetData(assetData);
        const tokenAddress = erc20ProxyData.tokenAddress;
        const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress);
        if (_.isUndefined(tokenContractIfExists)) {
            throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`);
        }
        return tokenContractIfExists;
    }
    private _validateDummyTokenContractsExistOrThrow(): void {
        if (_.isUndefined(this._dummyTokenContracts)) {
            throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"');
        }
    }
    private _validateProxyContractExistsOrThrow(): void {
        if (_.isUndefined(this._proxyContract)) {
            throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"');
        }
    }
}