aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts
blob: d4a08da8661d0d93cd121262269cd8cf71851742 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
import { WETH9Contract, WETH9EventArgs, WETH9Events } from '@0x/abi-gen-wrappers';
import { WETH9 } from '@0x/contract-artifacts';
import { schemas } from '@0x/json-schemas';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';

import { BlockRange, ContractWrappersError, EventCallback, IndexedFilterValues, TransactionOpts } from '../types';
import { assert } from '../utils/assert';

import { ContractWrapper } from './contract_wrapper';
import { ERC20TokenWrapper } from './erc20_token_wrapper';

const removeUndefinedProperties = _.pickBy;

/**
 * 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 {
    public abi: ContractAbi = WETH9.compilerOutput.abi;
    private _etherTokenContractsByAddress: {
        [address: string]: WETH9Contract;
    } = {};
    private _erc20TokenWrapper: ERC20TokenWrapper;
    /**
     * Instantiate EtherTokenWrapper.
     * @param web3Wrapper Web3Wrapper instance to use
     * @param networkId Desired networkId
     * @param erc20TokenWrapper The ERC20TokenWrapper instance to use
     * @param blockPollingIntervalMs The block polling interval to use for active subscriptions
     */
    constructor(
        web3Wrapper: Web3Wrapper,
        networkId: number,
        erc20TokenWrapper: ERC20TokenWrapper,
        blockPollingIntervalMs?: number,
    ) {
        super(web3Wrapper, networkId, blockPollingIntervalMs);
        this._erc20TokenWrapper = erc20TokenWrapper;
    }
    /**
     * 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   etherTokenAddress   EtherToken address you wish to deposit into.
     * @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.
     * @param   txOpts              Transaction parameters.
     * @return Transaction hash.
     */
    public async depositAsync(
        etherTokenAddress: string,
        amountInWei: BigNumber,
        depositor: string,
        txOpts: TransactionOpts = {},
    ): Promise<string> {
        assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
        assert.isValidBaseUnitAmount('amountInWei', amountInWei);
        await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);
        const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
        const normalizedDepositorAddress = depositor.toLowerCase();

        const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(normalizedDepositorAddress);
        assert.assert(ethBalanceInWei.gte(amountInWei), ContractWrappersError.InsufficientEthBalanceForDeposit);

        const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
        const txHash = await wethContract.deposit.sendTransactionAsync(
            removeUndefinedProperties({
                from: normalizedDepositorAddress,
                value: amountInWei,
                gas: txOpts.gasLimit,
                gasPrice: txOpts.gasPrice,
            }),
        );
        return txHash;
    }
    /**
     * Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the
     * equivalent number of wrapped ETH tokens.
     * @param   etherTokenAddress   EtherToken address you wish to withdraw from.
     * @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 withdrawal.
     * @param   txOpts       Transaction parameters.
     * @return Transaction hash.
     */
    public async withdrawAsync(
        etherTokenAddress: string,
        amountInWei: BigNumber,
        withdrawer: string,
        txOpts: TransactionOpts = {},
    ): Promise<string> {
        assert.isValidBaseUnitAmount('amountInWei', amountInWei);
        assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
        await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);
        const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
        const normalizedWithdrawerAddress = withdrawer.toLowerCase();

        const WETHBalanceInBaseUnits = await this._erc20TokenWrapper.getBalanceAsync(
            normalizedEtherTokenAddress,
            normalizedWithdrawerAddress,
        );
        assert.assert(
            WETHBalanceInBaseUnits.gte(amountInWei),
            ContractWrappersError.InsufficientWEthBalanceForWithdrawal,
        );

        const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
        const txHash = await wethContract.withdraw.sendTransactionAsync(
            amountInWei,
            removeUndefinedProperties({
                from: normalizedWithdrawerAddress,
                gas: txOpts.gasLimit,
                gasPrice: txOpts.gasPrice,
            }),
        );
        return txHash;
    }
    /**
     * Gets historical logs without creating a subscription
     * @param   etherTokenAddress   An address of the ether token that emitted the logs.
     * @param   eventName           The ether token contract event you would like to subscribe to.
     * @param   blockRange          Block range to get logs from.
     * @param   indexFilterValues   An object where the keys are indexed args returned by the event and
     *                              the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
     * @return  Array of logs that match the parameters
     */
    public async getLogsAsync<ArgsType extends WETH9EventArgs>(
        etherTokenAddress: string,
        eventName: WETH9Events,
        blockRange: BlockRange,
        indexFilterValues: IndexedFilterValues,
    ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
        assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
        const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
        assert.doesBelongToStringEnum('eventName', eventName, WETH9Events);
        assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
        assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
        const logs = await this._getLogsAsync<ArgsType>(
            normalizedEtherTokenAddress,
            eventName,
            blockRange,
            indexFilterValues,
            WETH9.compilerOutput.abi,
        );
        return logs;
    }
    /**
     * Subscribe to an event type emitted by the Token contract.
     * @param   etherTokenAddress   The hex encoded address where the ether token is deployed.
     * @param   eventName           The ether token contract event you would like to subscribe to.
     * @param   indexFilterValues   An object where the keys are indexed args returned by the event and
     *                              the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
     * @param   callback            Callback that gets called when a log is added/removed
     * @param   isVerbose           Enable verbose subscription warnings (e.g recoverable network issues encountered)
     * @return Subscription token used later to unsubscribe
     */
    public subscribe<ArgsType extends WETH9EventArgs>(
        etherTokenAddress: string,
        eventName: WETH9Events,
        indexFilterValues: IndexedFilterValues,
        callback: EventCallback<ArgsType>,
        isVerbose: boolean = false,
    ): string {
        assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
        const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
        assert.doesBelongToStringEnum('eventName', eventName, WETH9Events);
        assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
        assert.isFunction('callback', callback);
        const subscriptionToken = this._subscribe<ArgsType>(
            normalizedEtherTokenAddress,
            eventName,
            indexFilterValues,
            WETH9.compilerOutput.abi,
            callback,
            isVerbose,
        );
        return subscriptionToken;
    }
    /**
     * Cancel a subscription
     * @param   subscriptionToken Subscription token returned by `subscribe()`
     */
    public unsubscribe(subscriptionToken: string): void {
        assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken);
        this._unsubscribe(subscriptionToken);
    }
    /**
     * Cancels all existing subscriptions
     */
    public unsubscribeAll(): void {
        super._unsubscribeAll();
    }
    private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<WETH9Contract> {
        let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress];
        if (!_.isUndefined(etherTokenContract)) {
            return etherTokenContract;
        }
        const contractInstance = new WETH9Contract(
            this.abi,
            etherTokenAddress,
            this._web3Wrapper.getProvider(),
            this._web3Wrapper.getContractDefaults(),
        );
        etherTokenContract = contractInstance;
        this._etherTokenContractsByAddress[etherTokenAddress] = etherTokenContract;
        return etherTokenContract;
    }
}