sendStore.js 3.18 KiB
Newer Older
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
Amaury Martiny's avatar
Amaury Martiny committed
// SPDX-License-Identifier: BSD-3-Clause
Amaury Martiny's avatar
Amaury Martiny committed
import abi from '@parity/shared/lib/contracts/abi/eip20';
import { action, computed, observable } from 'mobx';
import { BigNumber } from 'bignumber.js';
Amaury Martiny's avatar
Amaury Martiny committed
import { blockNumber$, makeContract$, post$ } from '@parity/light.js';
Amaury Martiny's avatar
Amaury Martiny committed
import Debug from '../utils/debug';
import parityStore from './parityStore';
import { txForErc20, txForEth } from '../utils/estimateGas';
Amaury Martiny's avatar
Amaury Martiny committed
const debug = Debug('sendStore');
const GAS_MULT_FACTOR = 1.25; // Since estimateGas is not always accurate, we add a 33% factor for buffer.
const DEFAULT_GAS = new BigNumber(21000 * GAS_MULT_FACTOR); // Default gas amount
Amaury Martiny's avatar
Amaury Martiny committed

Amaury Martiny's avatar
Amaury Martiny committed
export class SendStore {
Amaury Martiny's avatar
Amaury Martiny committed
  @observable blockNumber; // Current block number, used to calculate tx confirmations.
Amaury Martiny's avatar
Amaury Martiny committed
  @observable estimated = DEFAULT_GAS; // Estimated gas amount for this transaction.
  tx = {}; // The actual tx we are sending. No need to be observable.
Amaury Martiny's avatar
Amaury Martiny committed
  @observable txStatus; // Status of the tx, see wiki for details.
Amaury Martiny's avatar
Amaury Martiny committed
  acceptRequest = (requestId, password) => {
    // Since we accepted this request, we also start to listen to blockNumber,
    // to calculate the number of confirmations
Amaury Martiny's avatar
Amaury Martiny committed
    this.subscription = blockNumber$().subscribe(this.setBlockNumber);
Amaury Martiny's avatar
Amaury Martiny committed
    return parityStore.api.signer.confirmRequest(requestId, null, password);
Amaury Martiny's avatar
Amaury Martiny committed
  /**
   * Back to defaults.
   */
  clear = () => {
Amaury Martiny's avatar
Amaury Martiny committed
    this.tx = {};
Amaury Martiny's avatar
Amaury Martiny committed
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  };

Amaury Martiny's avatar
Amaury Martiny committed
  /**
   * Get the number of confirmations our transaction has.
   */
  @computed
  get confirmations () {
Amaury Martiny's avatar
Amaury Martiny committed
    if (!this.txStatus || !this.txStatus.confirmed) {
      return -1;
    }
    return this.blockNumber - +this.txStatus.confirmed.blockNumber;
  }

Amaury Martiny's avatar
Amaury Martiny committed
  /**
Amaury Martiny's avatar
Amaury Martiny committed
   * If it's a token, then return the makeContract$ object.
   */
  @computed
  get contract () {
    if (this.tokenAddress === 'ETH') {
      return null;
    }
    return makeContract$(this.tokenAddress, abi);
  }

Amaury Martiny's avatar
Amaury Martiny committed
  /**
   * Create a transaction.
   */
Amaury Martiny's avatar
Amaury Martiny committed
  send = (token, password) => {
Amaury Martiny's avatar
Amaury Martiny committed
    const send$ =
Amaury Martiny's avatar
Amaury Martiny committed
      token.address === 'ETH'
        ? post$(txForEth(this.tx))
Amaury Martiny's avatar
Amaury Martiny committed
        : this.contract.transfer$(
Amaury Martiny's avatar
Amaury Martiny committed
          ...txForErc20(this.tx, token).args,
          txForErc20(this.tx, token).options
Amaury Martiny's avatar
Amaury Martiny committed
    debug(
      'Sending tx.',
Amaury Martiny's avatar
Amaury Martiny committed
      token.address === 'ETH' ? this.txForEth : this.txForErc20
Amaury Martiny's avatar
Amaury Martiny committed
    );

Amaury Martiny's avatar
Amaury Martiny committed
    return new Promise((resolve, reject) => {
      send$.subscribe(txStatus => {
        // When we arrive to the `requested` stage, we accept the request
        if (txStatus.requested) {
          this.acceptRequest(txStatus.requested, password)
            .then(resolve)
            .catch(reject);
        }
        this.setTxStatus(txStatus);
Amaury Martiny's avatar
Amaury Martiny committed
        debug('Tx status updated.', txStatus);
Amaury Martiny's avatar
Amaury Martiny committed
      });
Amaury Martiny's avatar
Amaury Martiny committed
  @action
  setBlockNumber = blockNumber => {
    this.blockNumber = blockNumber;
  setEstimated = estimated => {
    this.estimated = estimated.mul(GAS_MULT_FACTOR);
    debug('Estimated gas,', +estimated, ', with buffer,', +this.estimated);
Amaury Martiny's avatar
Amaury Martiny committed
  @action
  setTx = tx => {
    this.tx = tx;
Amaury Martiny's avatar
Amaury Martiny committed
  };

  @action
  setTxStatus = txStatus => {
    this.txStatus = txStatus;
  };
}

export default new SendStore();