@ethersproject/abstract-provider#TransactionRequest TypeScript Examples

The following examples show how to use @ethersproject/abstract-provider#TransactionRequest. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Provider.ts    From evm-provider.js with Apache License 2.0 6 votes vote down vote up
async _resolveTransaction(
    tx: Deferrable<TransactionRequest>
  ): Promise<Deferrable<TransactionRequest>> {
    for (const key of ['gasLimit', 'value']) {
      const typeKey = key as 'gasLimit' | 'value';

      if (tx[typeKey]) {
        if (BigNumber.isBigNumber(tx[typeKey])) {
          tx[typeKey] = (tx[typeKey] as BigNumber).toHexString();
        } else if (isNumber(tx[typeKey])) {
          tx[typeKey] = numberToHex(tx[typeKey] as number);
        }
      }
    }

    delete tx.nonce;
    delete tx.gasPrice;
    delete tx.chainId;

    return tx;
  }
Example #2
Source File: base-provider.ts    From bodhi.js with Apache License 2.0 6 votes vote down vote up
call = async (
    transaction: Deferrable<TransactionRequest>,
    _blockTag?: BlockTag | Promise<BlockTag>
  ): Promise<string> => {
    await this.getNetwork();
    const blockTag = await this._ensureSafeModeBlockTagFinalization(_blockTag);

    const resolved = await resolveProperties({
      transaction: this._getTransactionRequest(transaction),
      blockHash: this._getBlockHash(blockTag)
    });

    const callRequest: CallRequest = {
      from: resolved.transaction.from,
      to: resolved.transaction.to,
      gasLimit: resolved.transaction.gasLimit?.toBigInt(),
      storageLimit: undefined,
      value: resolved.transaction.value?.toBigInt(),
      data: resolved.transaction.data,
      accessList: resolved.transaction.accessList
    };

    const data = resolved.blockHash
      ? await (this.api.rpc as any).evm.call(callRequest, resolved.blockHash)
      : await (this.api.rpc as any).evm.call(callRequest);

    return data.toHex();
  };
Example #3
Source File: base-provider.ts    From bodhi.js with Apache License 2.0 6 votes vote down vote up
/**
   * Estimate gas for a transaction.
   * @param transaction The transaction to estimate the gas of
   * @returns The estimated gas used by this transaction
   */
  estimateGas = async (transaction: Deferrable<TransactionRequest>): Promise<BigNumber> => {
    await this.call(transaction);
    const { storageDepositPerByte, txFeePerGas } = this._getGasConsts();
    const gasPrice = (await transaction.gasPrice) || (await this.getGasPrice());
    const storageEntryLimit = BigNumber.from(gasPrice).and(0xffff);
    const storageEntryDeposit = BigNumber.from(storageDepositPerByte).mul(64);
    const storageGasLimit = storageEntryLimit.mul(storageEntryDeposit).div(txFeePerGas);

    const resources = await this.estimateResources(transaction);
    return resources.gas.add(storageGasLimit);
  };
Example #4
Source File: Provider.ts    From evm-provider.js with Apache License 2.0 6 votes vote down vote up
/**
   * Estimate gas for a transaction.
   * @param transaction The transaction to estimate the gas of
   * @returns The estimated gas used by this transaction
   */
  async estimateGas(
    transaction: Deferrable<TransactionRequest>
  ): Promise<BigNumber> {
    const resources = await this.estimateResources(transaction);
    return resources.gas.add(resources.storage);
  }
Example #5
Source File: base-provider.ts    From bodhi.js with Apache License 2.0 6 votes vote down vote up
_getTransactionRequest = async (transaction: Deferrable<TransactionRequest>): Promise<Partial<Transaction>> => {
    const values: any = await transaction;

    const tx: any = {};

    ['from', 'to'].forEach((key) => {
      if (values[key] === null || values[key] === undefined) {
        return;
      }
      tx[key] = Promise.resolve(values[key]).then((v) => (v ? this._getAddress(v) : null));
    });

    ['gasLimit', 'gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas', 'value'].forEach((key) => {
      if (values[key] === null || values[key] === undefined) {
        return;
      }
      tx[key] = Promise.resolve(values[key]).then((v) => (v ? BigNumber.from(v) : null));
    });

    ['type'].forEach((key) => {
      if (values[key] === null || values[key] === undefined) {
        return;
      }
      tx[key] = Promise.resolve(values[key]).then((v) => (v !== null || v !== undefined ? v : null));
    });

    if (values.accessList) {
      tx.accessList = accessListify(values.accessList);
    }

    ['data'].forEach((key) => {
      if (values[key] === null || values[key] === undefined) {
        return;
      }
      tx[key] = Promise.resolve(values[key]).then((v) => (v ? hexlify(v) : null));
    });

    return await resolveProperties(tx);
  };
Example #6
Source File: Signer.ts    From evm-provider.js with Apache License 2.0 6 votes vote down vote up
signTransaction(
    transaction: Deferrable<TransactionRequest>
  ): Promise<string> {
    return logger.throwError(
      'signing transactions is unsupported',
      Logger.errors.UNSUPPORTED_OPERATION,
      {
        operation: 'signTransaction'
      }
    );
  }
Example #7
Source File: usePromiseTransaction.ts    From useDApp with MIT License 6 votes vote down vote up
/**
 * @internal
 */
export async function estimateGasLimit(
  transactionRequest: TransactionRequest | undefined,
  signer: Signer | undefined,
  bufferGasLimitPercentage: number
) {
  if (!signer || !transactionRequest) {
    return undefined
  }
  try {
    const estimatedGas = transactionRequest.gasLimit
      ? BigNumber.from(transactionRequest.gasLimit)
      : await signer.estimateGas(transactionRequest)
    return estimatedGas?.mul(bufferGasLimitPercentage + 100).div(100)
  } catch (err: any) {
    console.error(err)
    return undefined
  }
}
Example #8
Source File: Provider.ts    From evm-provider.js with Apache License 2.0 6 votes vote down vote up
async _resolveStorageLimit(
    tx: Deferrable<TransactionRequest>
  ): Promise<BigNumber> {
    if (tx.customData) {
      if ('storageLimit' in tx.customData) {
        const storageLimit = tx.customData.storageLimit;
        if (BigNumber.isBigNumber(storageLimit)) {
          return storageLimit;
        } else if (isNumber(storageLimit)) {
          return BigNumber.from(storageLimit);
        }
      }
    }

    // At least 60 REEF are needed to deploy
    return BigNumber.from(60_000);
  }
Example #9
Source File: Provider.ts    From evm-provider.js with Apache License 2.0 6 votes vote down vote up
/**
   * Submit a transaction to be executed on chain.
   * @param transaction The transaction to call
   * @param blockTag
   * @returns The call result as a hash
   */
  async call(
    transaction: Deferrable<TransactionRequest>,
    blockTag?: BlockTag | Promise<BlockTag>
  ): Promise<string> {
    const resolved = await this._resolveTransaction(transaction);
    if (blockTag) {
      const blockHash = await this._resolveBlockHash(blockTag);
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const result = await (this.api.rpc as any).evm.call(resolved, blockHash);
      return result.toHex();
    } else {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const result = await (this.api.rpc as any).evm.call({
        to: resolved.to,
        from: resolved.from,
        data: resolved.data,
        storageLimit: '0'
      });
      return result.toHex();
    }
  }
Example #10
Source File: transaction-manager.ts    From hoprnet with GNU General Public License v3.0 6 votes vote down vote up
/**
   * Return all the queuing transactions
   * @returns Array of transaction hashes
   */
  public getAllQueuingTxs(): TransactionRequest[] {
    // queuing tx hashes
    const queuingTxHash = Array.from(this.queuing.keys())
    return queuingTxHash.map((txHash) => {
      const { to, data, value } = this.payloads.get(txHash)
      const { nonce, maxPrority } = this.queuing.get(txHash)
      return {
        to,
        data,
        value,
        nonce,
        maxPrority
      }
    })
  }
Example #11
Source File: useSendTransaction.ts    From useDApp with MIT License 5 votes vote down vote up
/**
 * Hook returns an object with three variables: `state`, `resetState`, and `sendTransaction`.
 *
 * ``state` represents the status of transaction. See {@link TransactionStatus}.
 *
 * `resetState` can be used to reset the state to `None` after a transaction attempt has either succeeded or failed.
 *
 * To send a transaction use `sendTransaction` function returned by `useSendTransaction`.
 *
 * Function accepts a [Transaction Request](https://docs.ethers.io/v5/api/providers/types/#providers-TransactionRequest) object as a parameter.
 * @public
 * @param options additional options of type {@link TransactionOptions}
 * @returns {} object with two variables: `sendTransaction` and `state`: `{ sendTransaction: (...args: any[]) => void, state: TransactionStatus }`.
 *
 * @example
 * const { sendTransaction, state } = useSendTransaction({ transactionName: 'Send Ethereum' })
 *
 * const handleClick = () => {
 *   ...
 *   sendTransaction({ to: address, value: utils.parseEther(amount) })
 * }
 */
export function useSendTransaction(options?: TransactionOptions) {
  const { library, chainId } = useEthers()
  const { promiseTransaction, state, resetState } = usePromiseTransaction(chainId, options)
  const { bufferGasLimitPercentage = 0 } = useConfig()

  const sendTransaction = async (transactionRequest: TransactionRequest) => {
    const signer = options?.signer || library?.getSigner()
    if (signer) {
      const gasLimit = await estimateGasLimit(transactionRequest, signer, bufferGasLimitPercentage)

      await promiseTransaction(
        signer.sendTransaction({
          ...transactionRequest,
          gasLimit,
        })
      )
    }
  }

  return { sendTransaction, state, resetState }
}
Example #12
Source File: Provider.ts    From evm-provider.js with Apache License 2.0 5 votes vote down vote up
/**
   * Estimate resources for a transaction.
   * @param transaction The transaction to estimate the resources of
   * @returns The estimated resources used by this transaction
   */
  async estimateResources(
    transaction: Deferrable<TransactionRequest>
  ): Promise<{
    gas: BigNumber;
    storage: BigNumber;
    weightFee: BigNumber;
  }> {
    const resolved = await this._resolveTransaction(transaction);

    const from = await resolved.from;
    const value = await resolved.value;
    const to = await resolved.to;
    const data = await resolved.data;
    const storageLimit = await this._resolveStorageLimit(resolved);

    if (!from) {
      return logger.throwError('From cannot be undefined');
    }

    // construct extrinsic to estimate
    const extrinsic = !to
      ? this.api.tx.evm.create(
          data,
          toBN(value),
          toBN(await resolved.gasLimit),
          toBN(storageLimit)
        )
      : this.api.tx.evm.call(
          to,
          data,
          toBN(value),
          toBN(await resolved.gasLimit),
          toBN(storageLimit)
        );

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const result = await (this.api.rpc as any).evm.estimateResources(
      resolved.from,
      extrinsic.toHex() // returns transaction bytecode?
    );

    return {
      gas: BigNumber.from((result.gas as BN).toString()),
      storage: BigNumber.from((result.storage as BN).toString()),
      weightFee: BigNumber.from((result.weightFee as BN).toString())
    };
  }
Example #13
Source File: ethers-adapter.ts    From cloud-cryptographic-wallet with MIT License 5 votes vote down vote up
async signTransaction(
    deferrableTransaction: ethers.utils.Deferrable<TransactionRequest>
  ): Promise<string> {
    const transaction = await ethers.utils.resolveProperties(
      deferrableTransaction
    );

    const address = await this.getAddress();

    if (transaction.from != null) {
      if (ethers.utils.getAddress(transaction.from) !== address) {
        this.logger.throwArgumentError(
          "transaction from address mismatch",
          "transaction.from",
          transaction.from
        );
      }
    }

    const nonce = transaction.nonce
      ? ethers.BigNumber.from(transaction.nonce).toNumber()
      : undefined;

    const unsignedTransaction: ethers.utils.UnsignedTransaction = {
      to: transaction.to,
      nonce,
      gasLimit: transaction.gasLimit,
      gasPrice: transaction.gasPrice,
      data: transaction.data,
      value: transaction.value,
      chainId: transaction.chainId,
      type: transaction.type,
      accessList: transaction.accessList,
      maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
      maxFeePerGas: transaction.maxFeePerGas,
    };

    (
      Object.keys(unsignedTransaction) as Array<
        keyof ethers.utils.UnsignedTransaction
      >
    ).forEach((key) => {
      if (key in unsignedTransaction && unsignedTransaction[key] == undefined) {
        delete unsignedTransaction[key];
      }
    });
    const hash = ethers.utils.keccak256(
      ethers.utils.serializeTransaction(unsignedTransaction)
    );

    const signature = await this.config.signer.sign(Bytes.fromString(hash));

    const ethersSignature = ethers.utils.splitSignature({
      v: signature.v,
      r: signature.r.toString(),
      s: signature.s.toString(),
    });

    return ethers.utils.serializeTransaction(
      unsignedTransaction,
      ethersSignature
    );
  }
Example #14
Source File: kms-ethers-signer.ts    From cloud-cryptographic-wallet with MIT License 5 votes vote down vote up
async signTransaction(
    deferrableTransaction: Deferrable<TransactionRequest>
  ): Promise<string> {
    return this.adapter.signTransaction(deferrableTransaction);
  }
Example #15
Source File: adapter.ts    From cloud-cryptographic-wallet with MIT License 5 votes vote down vote up
async signTransaction(
    deferrableTransaction: Deferrable<TransactionRequest>
  ): Promise<string> {
    const transaction = await resolveProperties(deferrableTransaction);

    const address = await this.getAddress();
    if (transaction.from != null) {
      if (getAddress(transaction.from) !== address) {
        this.logger.throwArgumentError(
          "transaction from address mismatch",
          "transaction.from",
          transaction.from
        );
      }
    }

    const nonce = transaction.nonce
      ? BigNumber.from(transaction.nonce).toNumber()
      : undefined;

    const unsignedTransaction: UnsignedTransaction = {
      to: transaction.to,
      nonce,
      gasLimit: transaction.gasLimit,
      gasPrice: transaction.gasPrice,
      data: transaction.data,
      value: transaction.value,
      chainId: transaction.chainId,
      type: transaction.type,
      accessList: transaction.accessList,
      maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
      maxFeePerGas: transaction.maxFeePerGas,
    };
    (
      Object.keys(unsignedTransaction) as Array<keyof UnsignedTransaction>
    ).forEach((key) => {
      if (key in unsignedTransaction && unsignedTransaction[key] == undefined) {
        delete unsignedTransaction[key];
      }
    });
    const digest = keccak256(serialize(unsignedTransaction));

    const signature = await this.signer.sign(
      Buffer.from(digest.slice(2), "hex")
    );

    const ethersSignature = splitSignature({
      v: signature.v,
      r: `0x${signature.r.toString("hex")}`,
      s: `0x${signature.s.toString("hex")}`,
    });

    return serialize(unsignedTransaction, ethersSignature);
  }
Example #16
Source File: recoverFunds.ts    From aavegotchi-contracts with MIT License 5 votes vote down vote up
async function main() {
  //@ts-ignore
  const accounts = await ethers.getSigners();

  let signer: Signer = accounts[0];

  await network.provider.request({
    method: "hardhat_impersonateAccount",
    params: [aavegotchi],
  });
  signer = await ethers.provider.getSigner(aavegotchi);

  for (let index = 0; index < 30; index++) {
    let nonce = await signer.getTransactionCount();
    console.log("nonce:", nonce.toString());

    const tx: TransactionRequest = {
      data: deployData,
    };

    const result = await signer.sendTransaction(tx);

    //@ts-ignore
    if (result.creates === contractAddress) {
      //@ts-ignore
      console.log("result:", result.creates);
      nonce = await signer.getTransactionCount();
      console.log("nonce:", nonce.toString());

      let bpt = await ethers.getContractAt(
        "ERC20Token",
        "0x0ec9f76202a7061eb9b3a7d6b59d36215a7e37da"
      );

      bpt = await impersonate(contractAddress, bpt, ethers, network);

      let bal = await bpt.balanceOf(contractAddress);
      console.log("bal:", bal.toString());

      await bpt.transfer(aavegotchi, bal);

      bal = await bpt.balanceOf(contractAddress);
      console.log("bal:", bal.toString());

      throw new Error(`Yesss it worked, nonce is: ${nonce}!`);
    }
  }
}
Example #17
Source File: base-provider.ts    From bodhi.js with Apache License 2.0 5 votes vote down vote up
/**
   * Estimate resources for a transaction.
   * @param transaction The transaction to estimate the resources of
   * @returns The estimated resources used by this transaction
   */
  estimateResources = async (
    transaction: Deferrable<TransactionRequest>
  ): Promise<{
    gas: BigNumber;
    storage: BigNumber;
    weightFee: BigNumber;
  }> => {
    const ethTx = await this._getTransactionRequest(transaction);

    const { from, to, data, value } = ethTx;

    const accessList = ethTx.accessList?.map(({ address, storageKeys }) => [address, storageKeys]) || [];

    const extrinsic = !to
      ? this.api.tx.evm.create(
          data!,
          value?.toBigInt()!,
          U64MAX.toBigInt(), // gas_limit u64::max
          U32MAX.toBigInt(), // storage_limit u32::max
          // @ts-ignore @TODO fix type
          accessList
        )
      : this.api.tx.evm.call(
          to,
          data!,
          value?.toBigInt()!,
          U64MAX.toBigInt(), // gas_limit u64::max
          U32MAX.toBigInt(), // storage_limit u32::max
          // @ts-ignore @TODO fix type
          accessList
        );

    const result = await (this.api.rpc as any).evm.estimateResources(from, extrinsic.toHex());

    return {
      gas: BigNumber.from((result.gas as BN).toString()),
      storage: BigNumber.from((result.storage as BN).toString()),
      weightFee: BigNumber.from((result.weightFee as BN).toString())
    };
  };
Example #18
Source File: base-provider.ts    From bodhi.js with Apache License 2.0 5 votes vote down vote up
/**
   * Get the gas for eth transactions
   * @returns The gas used by eth transaction
   */
  getEthResources = async (
    transaction: Deferrable<TransactionRequest>,
    {
      gasLimit,
      storageLimit,
      validUntil
    }: {
      gasLimit?: BigNumberish;
      storageLimit?: BigNumberish;
      validUntil?: BigNumberish;
    } = {}
  ): Promise<{
    gasPrice: BigNumber;
    gasLimit: BigNumber;
  }> => {
    if (!gasLimit || !storageLimit) {
      const { gas, storage } = await this.estimateResources(transaction);
      gasLimit = gasLimit ?? gas;
      storageLimit = storageLimit ?? storage;
    }

    if (!validUntil) {
      const blockNumber = await this.getBlockNumber();
      // Expires after 100 blocks by default
      validUntil = blockNumber + 100;
    }

    const storageByteDeposit = (this.api.consts.evm.storageDepositPerByte as UInt).toBigInt();
    const txFeePerGas = (this.api.consts.evm.txFeePerGas as UInt).toBigInt();

    const { txGasLimit, txGasPrice } = calcEthereumTransactionParams({
      gasLimit,
      storageLimit,
      validUntil,
      storageByteDeposit,
      txFeePerGas
    });

    return {
      gasLimit: txGasLimit,
      gasPrice: txGasPrice
    };
  };
Example #19
Source File: Signer.ts    From bodhi.js with Apache License 2.0 5 votes vote down vote up
signTransaction(transaction: Deferrable<TransactionRequest>): Promise<string> {
    return logger.throwError('signing transactions is unsupported', Logger.errors.UNSUPPORTED_OPERATION, {
      operation: 'signTransaction'
    });
  }
Example #20
Source File: Signer.ts    From evm-provider.js with Apache License 2.0 4 votes vote down vote up
/**
   *
   * @param transaction
   * @returns A promise that resolves to the transaction's response
   */
  async sendTransaction(
    _transaction: Deferrable<TransactionRequest>
  ): Promise<TransactionResponse> {
    this._checkProvider('sendTransaction');

    const signerAddress = await this.getSubstrateAddress();
    const evmAddress = await this.getAddress();

    // estimateResources requires the from parameter.
    // However, when creating the contract, there is no from parameter in the tx
    const transaction = {
      from: evmAddress,
      ..._transaction
    };

    const resources = await this.provider.estimateResources(transaction);

    // Multiply by 3.1
    const gasLimit: BigNumber = resources.gas.mul(31).div(10);
    let storageLimit: BigNumber;

    // If the storage limit is supplied, override it from the estimateResources
    if (transaction.customData) {
      if ('storageLimit' in transaction.customData) {
        storageLimit = transaction.customData.storageLimit;
        if (isNumber(storageLimit)) {
          storageLimit = BigNumber.from(storageLimit);
        }
      }
    } else {
      storageLimit = resources.storage.mul(31).div(10);
    }

    let totalLimit = await transaction.gasLimit;

    if (totalLimit === null || totalLimit === undefined) {
      totalLimit = gasLimit.add(storageLimit);
    }

    transaction.gasLimit = totalLimit;

    const tx = await this.populateTransaction(transaction);

    const data = tx.data;
    const from = tx.from;

    if (!data) {
      return logger.throwError('Request data not found');
    }

    if (!from) {
      return logger.throwError('Request from not found');
    }

    let extrinsic: SubmittableExtrinsic<'promise'>;

    // @TODO create contract
    if (!tx.to) {
      extrinsic = this.provider.api.tx.evm.create(
        tx.data,
        toBN(tx.value),
        toBN(gasLimit),
        toBN(storageLimit.isNegative() ? 0 : storageLimit)
      );
    } else {
      extrinsic = this.provider.api.tx.evm.call(
        tx.to,
        tx.data,
        toBN(tx.value),
        toBN(gasLimit),
        toBN(storageLimit.isNegative() ? 0 : storageLimit)
      );
    }

    await extrinsic.signAsync(signerAddress);

    return new Promise((resolve, reject) => {
      extrinsic
        .send((result: SubmittableResult) => {
          handleTxResponse(result, this.provider.api)
            .then(() => {
              resolve({
                hash: extrinsic.hash.toHex(),
                from: from || '',
                confirmations: 0,
                nonce: toBN(tx.nonce).toNumber(),
                gasLimit: BigNumber.from(tx.gasLimit || '0'),
                gasPrice: BigNumber.from(0),
                data: dataToString(data),
                value: BigNumber.from(tx.value || '0'),
                chainId: 13939,
                wait: (confirmations?: number): Promise<TransactionReceipt> => {
                  return this.provider._resolveTransactionReceipt(
                    extrinsic.hash.toHex(),
                    result.status.asInBlock.toHex(),
                    from
                  );
                }
              });
            })
            .catch(({ message, result }) => {
              reject(message);
            });
        })
        .catch((error) => {
          reject(error && error.message);
        });
    });
  }
Example #21
Source File: useRawLogs.test.ts    From useDApp with MIT License 4 votes vote down vote up
describe('useRawLogs', () => {
  const mockProvider = new MockProvider()
  const secondMockProvider = new MockProvider({ ganacheOptions: { _chainIdRpc: SECOND_TEST_CHAIN_ID } as any })
  const [deployer, receiver] = mockProvider.getWallets()
  const [secondDeployer] = secondMockProvider.getWallets()
  const eventTopic = ethers.utils.id('Transfer(address,address,uint256)')
  let token: Contract
  let secondToken: Contract

  beforeEach(async () => {
    token = await deployMockToken(deployer)
    secondToken = await deployMockToken(secondDeployer, SECOND_MOCK_TOKEN_INITIAL_BALANCE)
  })

  async function sendToken(signer: ethers.Wallet, to: string, amount: BigNumber) {
    const { result, waitForCurrent, waitForNextUpdate } = await renderWeb3Hook(
      () =>
        useSendTransaction({
          signer,
        }),
      { mockProvider }
    )

    await waitForNextUpdate()

    const txData = ERC20MockInterface.encodeFunctionData('transfer(address,uint)', [to, amount])

    const tx: TransactionRequest = {
      to: token.address,
      value: BigNumber.from(0),
      data: txData,
      gasPrice: 0,
    }

    await result.current.sendTransaction(tx)

    await waitForCurrent((val) => val.state !== undefined)
    expect(result.current.state.status).to.eq('Success')
  }

  function extractAddress(address: string) {
    let result: string
    result = hexStripZeros(address)
    while (result.length != 42) result = '0x0' + result.substring(2)

    return result
  }

  it('Can get only the recent token transfer log', async () => {
    const blockNumber = await mockProvider.getBlockNumber()

    const from = deployer
    const to = receiver

    const fromAddress = from.address
    const toAddress = to.address
    const amount = BigNumber.from(1)

    await sendToken(from, toAddress, amount)

    const filter: Filter = {
      address: token.address,
      fromBlock: blockNumber + 1,
      toBlock: blockNumber + 2,
      topics: [eventTopic],
    }

    const { result, waitForCurrent } = await renderWeb3Hook(() => useRawLogs(filter), { mockProvider })

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.length).to.equal(1, 'Number of logs')

    const log = result.current![0]

    expect(log.topics[0]).to.equal(eventTopic, 'Event topic')
    expect(getAddress(extractAddress(log.topics[1]))).to.equal(getAddress(fromAddress), 'From')
    expect(getAddress(extractAddress(log.topics[2]))).to.equal(getAddress(toAddress), 'To')

    const decodedData = defaultAbiCoder.decode(['uint'], log.data)

    expect(decodedData[0]).to.equal(amount, 'Amount')
  })

  it('Can get all token transfer logs', async () => {
    const from = deployer
    const to = receiver

    const fromAddress = from.address
    const toAddress = to.address
    const amount = BigNumber.from(1)

    await sendToken(from, toAddress, amount)

    const filter: Filter = {
      address: token.address,
      fromBlock: 0,
      toBlock: 'latest',
      topics: [eventTopic],
    }

    const { result, waitForCurrent } = await renderWeb3Hook(() => useRawLogs(filter), { mockProvider })

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.length).to.equal(2, 'Number of logs')

    // Mint transfer event
    const log1 = result.current![0]

    expect(log1.topics[0]).to.equal(eventTopic, 'Event topic')
    expect(getAddress(extractAddress(log1.topics[1]))).to.equal(getAddress(AddressZero), 'From')
    expect(getAddress(extractAddress(log1.topics[2]))).to.equal(getAddress(deployer.address), 'To')

    const decodedData1 = defaultAbiCoder.decode(['uint'], log1.data)

    expect(decodedData1[0]).to.equal(MOCK_TOKEN_INITIAL_BALANCE, 'Amount')

    // Recent transfer transaction log
    const log = result.current![1]

    expect(log.topics[0]).to.equal(eventTopic, 'Event topic')
    expect(getAddress(extractAddress(log.topics[1]))).to.equal(getAddress(fromAddress), 'From')
    expect(getAddress(extractAddress(log.topics[2]))).to.equal(getAddress(toAddress), 'To')

    const decodedData = defaultAbiCoder.decode(['uint'], log.data)

    expect(decodedData[0]).to.equal(amount, 'Amount')
  })

  it('Can get the mint transfer log', async () => {
    const filter: Filter = {
      address: token.address,
      fromBlock: 0,
      toBlock: 'latest',
      topics: [eventTopic],
    }

    const { result, waitForCurrent } = await renderWeb3Hook(() => useRawLogs(filter), { mockProvider })

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.length).to.equal(1, 'Number of logs')

    const log = result.current![0]

    expect(log.topics[0]).to.equal(eventTopic, 'Event topic')
    expect(getAddress(extractAddress(log.topics[1]))).to.equal(getAddress(AddressZero), 'From')
    expect(getAddress(extractAddress(log.topics[2]))).to.equal(getAddress(deployer.address), 'To')

    const decodedData = defaultAbiCoder.decode(['uint'], log.data)

    expect(decodedData[0]).to.equal(MOCK_TOKEN_INITIAL_BALANCE, 'Amount')
  })

  it('Can get the mint transfer log on the alternative chain', async () => {
    const filter: Filter = {
      address: secondToken.address,
      fromBlock: 0,
      toBlock: 'latest',
      topics: [eventTopic],
    }

    const { result, waitForCurrent } = await renderWeb3Hook(() => useRawLogs(filter), {
      mockProvider: secondMockProvider,
    })

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.length).to.equal(1, 'Number of logs')

    const log = result.current![0]

    expect(log.topics[0]).to.equal(eventTopic, 'Event topic')
    expect(getAddress(extractAddress(log.topics[1]))).to.equal(getAddress(AddressZero), 'From')
    expect(getAddress(extractAddress(log.topics[2]))).to.equal(getAddress(secondDeployer.address), 'To')

    const decodedData = defaultAbiCoder.decode(['uint'], log.data)

    expect(decodedData[0]).to.equal(SECOND_MOCK_TOKEN_INITIAL_BALANCE, 'Amount')
  })

  it('Works if there are no logs', async () => {
    const filter: Filter = {
      address: secondToken.address, // Token on the other chain... doesn't exist so there should be no logs
      fromBlock: 0,
      toBlock: 'latest',
      topics: [eventTopic],
    }

    const { result, waitForCurrent } = await renderWeb3Hook(() => useRawLogs(filter), { mockProvider })

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined
    expect(result.current?.length).to.equal(0, 'Number of logs')
  })
})
Example #22
Source File: useLogs.test.tsx    From useDApp with MIT License 4 votes vote down vote up
describe('useLogs', () => {
  const mockProvider = new MockProvider()
  const secondMockProvider = new MockProvider({ ganacheOptions: { _chainIdRpc: SECOND_TEST_CHAIN_ID } as any })
  const [deployer, receiver] = mockProvider.getWallets()
  const [secondDeployer] = secondMockProvider.getWallets()
  let token: Contract
  let secondToken: Contract

  beforeEach(async () => {
    token = await deployMockToken(deployer)
    secondToken = await deployMockToken(secondDeployer, SECOND_MOCK_TOKEN_INITIAL_BALANCE)
  })

  async function sendToken(signer: ethers.Wallet, to: string, amount: BigNumber) {
    const { result, waitForCurrent, waitForNextUpdate } = await renderWeb3Hook(
      () =>
        useSendTransaction({
          signer,
        }),
      { mockProvider }
    )

    await waitForNextUpdate()

    const txData = ERC20MockInterface.encodeFunctionData('transfer(address,uint)', [to, amount])

    const tx: TransactionRequest = {
      to: token.address,
      value: BigNumber.from(0),
      data: txData,
      gasPrice: 0,
    }

    await result.current.sendTransaction(tx)

    await waitForCurrent((val) => val.state !== undefined)
    expect(result.current.state.status).to.eq('Success')

    return result.current.state
  }

  it('Can get only the recent token transfer log', async () => {
    const blockNumber = await mockProvider.getBlockNumber()

    const from = deployer
    const to = receiver

    const fromAddress = from.address
    const toAddress = to.address
    const amount = BigNumber.from(1)

    await sendToken(from, toAddress, amount)

    const { result, waitForCurrent } = await renderWeb3Hook(
      () =>
        useLogs(
          {
            contract: token,
            event: 'Transfer',
            args: [],
          },
          {
            fromBlock: blockNumber + 1,
            toBlock: blockNumber + 2,
          }
        ),
      { mockProvider }
    )

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.value).to.not.be.undefined
    expect(result.current?.error).to.be.undefined
    expect(result.current?.value?.length).to.equal(1, 'Number of logs')

    const log = result.current!.value![0]

    expect(getAddress(log.data['from'])).to.equal(getAddress(fromAddress), 'From')
    expect(getAddress(log.data['to'])).to.equal(getAddress(toAddress), 'To')
    expect(log.data['value']).to.equal(amount, 'Amount')
  })

  it('Can get all token transfer logs using the default log query parameters', async () => {
    const from = deployer
    const to = receiver

    const fromAddress = from.address
    const toAddress = to.address
    const amount = BigNumber.from(1)

    await sendToken(from, toAddress, amount)

    const { result, waitForCurrent } = await renderWeb3Hook(
      () =>
        useLogs({
          contract: token,
          event: 'Transfer',
          args: [],
        }),
      { mockProvider }
    )

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.value).to.not.be.undefined
    expect(result.current?.error).to.be.undefined
    expect(result.current?.value?.length).to.equal(2, 'Number of logs')

    // Mint transfer event
    const log1 = result.current!.value![0]

    expect(getAddress(log1.data['from'])).to.equal(getAddress(AddressZero), 'From')
    expect(getAddress(log1.data['to'])).to.equal(getAddress(deployer.address), 'To')
    expect(log1.data['value']).to.equal(MOCK_TOKEN_INITIAL_BALANCE, 'Amount')

    // Recent transfer transaction log
    const log = result.current!.value![1]

    expect(getAddress(log.data['from'])).to.equal(getAddress(fromAddress), 'From')
    expect(getAddress(log.data['to'])).to.equal(getAddress(toAddress), 'To')
    expect(log.data['value']).to.equal(amount, 'Amount')
  })

  it('Can get the mint transfer log', async () => {
    const { result, waitForCurrent } = await renderWeb3Hook(
      () =>
        useLogs(
          {
            contract: token,
            event: 'Transfer',
            args: [],
          },
          {
            fromBlock: 0,
            toBlock: 'latest',
          }
        ),
      { mockProvider }
    )

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.value).to.not.be.undefined
    expect(result.current?.error).to.be.undefined
    expect(result.current?.value?.length).to.equal(1, 'Number of logs')

    const log = result.current!.value![0]

    expect(getAddress(log.data['from'])).to.equal(getAddress(AddressZero), 'From')
    expect(getAddress(log.data['to'])).to.equal(getAddress(deployer.address), 'To')
    expect(log.data['value']).to.equal(MOCK_TOKEN_INITIAL_BALANCE, 'Amount')
  })

  it('Can get the mint transfer log on the alternative chain', async () => {
    const { result, waitForCurrent } = await renderWeb3Hook(
      () =>
        useLogs(
          {
            contract: secondToken,
            event: 'Transfer',
            args: [],
          },
          {
            fromBlock: 0,
            toBlock: 'latest',
          }
        ),
      {
        mockProvider: secondMockProvider,
      }
    )

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.value).to.not.be.undefined
    expect(result.current?.error).to.be.undefined
    expect(result.current?.value?.length).to.equal(1, 'Number of logs')

    const log = result.current!.value![0]

    expect(getAddress(log.data['from'])).to.equal(getAddress(AddressZero), 'From')
    expect(getAddress(log.data['to'])).to.equal(getAddress(secondDeployer.address), 'To')
    expect(log.data['value']).to.equal(SECOND_MOCK_TOKEN_INITIAL_BALANCE, 'Amount')
  })

  it('Works if there are no logs', async () => {
    const { result, waitForCurrent } = await renderWeb3Hook(
      () =>
        useLogs(
          {
            contract: secondToken, // Token on the other chain... doesn't exist so there should be no logs
            event: 'Transfer',
            args: [],
          },
          {
            fromBlock: 0,
            toBlock: 'latest',
          }
        ),
      { mockProvider }
    )

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined
    expect(result.current?.value).to.not.be.undefined
    expect(result.current?.error).to.be.undefined
    expect(result.current?.value?.length).to.equal(0, 'Number of logs')
  })

  it('Can query mint transfer logs by sender', async () => {
    // Send to emit another Transfer token that our filter should filter out
    await sendToken(deployer, receiver.address, BigNumber.from(1))

    const { result, waitForCurrent } = await renderWeb3Hook(
      () =>
        useLogs(
          {
            contract: token,
            event: 'Transfer',
            args: [AddressZero],
          },
          {
            fromBlock: 0,
            toBlock: 'latest',
          }
        ),
      { mockProvider }
    )

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.value).to.not.be.undefined
    expect(result.current?.error).to.be.undefined
    expect(result.current?.value?.length).to.equal(1, 'Number of logs')

    const log = result.current!.value![0]

    expect(getAddress(log.data['from'])).to.equal(getAddress(AddressZero), 'From')
    expect(getAddress(log.data['to'])).to.equal(getAddress(deployer.address), 'To')
    expect(log.data['value']).to.equal(MOCK_TOKEN_INITIAL_BALANCE, 'Amount')
  })

  it('Can query mint transfer logs by receiver', async () => {
    // Send to emit another Transfer token that our filter should filter out
    await sendToken(deployer, receiver.address, BigNumber.from(1))

    const { result, waitForCurrent } = await renderWeb3Hook(
      () =>
        useLogs(
          {
            contract: token,
            event: 'Transfer',
            args: [null, deployer.address],
          },
          {
            fromBlock: 0,
            toBlock: 'latest',
          }
        ),
      { mockProvider }
    )

    await waitForCurrent((val) => val !== undefined)
    expect(result.error).to.be.undefined

    expect(result.current?.value).to.not.be.undefined
    expect(result.current?.error).to.be.undefined
    expect(result.current?.value?.length).to.equal(1, 'Number of logs')

    const log = result.current!.value![0]

    expect(getAddress(log.data['from'])).to.equal(getAddress(AddressZero), 'From')
    expect(getAddress(log.data['to'])).to.equal(getAddress(deployer.address), 'To')
    expect(log.data['value']).to.equal(MOCK_TOKEN_INITIAL_BALANCE, 'Amount')
  })

  it('We get an error when we query by un-indexed values', async () => {
    // Send to emit another Transfer token that our filter should filter out
    await sendToken(deployer, receiver.address, BigNumber.from(1))

    const { result, waitForCurrent } = await renderWeb3Hook(
      () =>
        useLogs(
          {
            contract: token,
            event: 'Transfer',
            args: [null, null, MOCK_TOKEN_INITIAL_BALANCE],
          },
          {
            fromBlock: 0,
            toBlock: 'latest',
          }
        ),
      { mockProvider }
    )

    await waitForCurrent((val) => val !== undefined)

    expect(result.error).to.be.undefined
    expect(result.current?.value).to.be.undefined
    expect(result.current?.error).to.not.be.undefined
  })

  it('Can query by block hash', async () => {
    // Send to emit another Transfer token that our filter should filter out
    const { receipt } = await sendToken(deployer, receiver.address, BigNumber.from(1))

    const { result, waitForCurrent } = await renderWeb3Hook(
      () =>
        useLogs(
          {
            contract: token,
            event: 'Transfer',
            args: [],
          },
          {
            blockHash: receipt?.blockHash,
          }
        ),
      { mockProvider }
    )

    await waitForCurrent((val) => val !== undefined)

    expect(result.error).to.be.undefined

    expect(result.current?.value).to.not.be.undefined
    expect(result.current?.error).to.be.undefined
    expect(result.current?.value?.length).to.equal(1, 'Number of logs')

    const log = result.current!.value![0]

    expect(getAddress(log.data['from'])).to.equal(getAddress(deployer.address), 'From')
    expect(getAddress(log.data['to'])).to.equal(getAddress(receiver.address), 'To')
    expect(log.data['value']).to.equal(BigNumber.from(1), 'Amount')
    expect(log.blockHash).to.equal(receipt?.blockHash, 'Block hash')
    expect(log.blockNumber).to.equal(receipt?.blockNumber, 'Block number')
    expect(log.transactionHash).to.equal(receipt?.transactionHash, 'Transaction hash')
    expect(log.transactionIndex).to.equal(receipt?.transactionIndex, 'Transaction index')
  })
})
Example #23
Source File: Signer.ts    From bodhi.js with Apache License 2.0 4 votes vote down vote up
/**
   *
   * @param transaction
   * @returns A promise that resolves to the transaction's response
   */
  async sendTransaction(_transaction: Deferrable<TransactionRequest>): Promise<TransactionResponse> {
    this._checkProvider('sendTransaction');

    const signerAddress = await this.getSubstrateAddress();
    const evmAddress = await this.getAddress();

    // estimateResources requires the from parameter.
    // However, when creating the contract, there is no from parameter in the tx
    const transaction = {
      from: evmAddress,
      ..._transaction
    };

    const resources = await this.provider.estimateResources(transaction);

    let gasLimit: BigNumber;
    let storageLimit: BigNumber;

    let totalLimit = await transaction.gasLimit;

    if (totalLimit === null || totalLimit === undefined) {
      gasLimit = resources.gas;
      storageLimit = resources.storage;
      totalLimit = resources.gas.add(resources.storage);
    } else {
      const estimateTotalLimit = resources.gas.add(resources.storage);
      gasLimit = BigNumber.from(totalLimit).mul(resources.gas).div(estimateTotalLimit).add(1);
      storageLimit = BigNumber.from(totalLimit).mul(resources.storage).div(estimateTotalLimit).add(1);
    }

    transaction.gasLimit = totalLimit;

    const tx = await this.populateTransaction(transaction);

    const data = tx.data;
    const from = tx.from;

    if (!data) {
      return logger.throwError('Request data not found');
    }

    if (!from) {
      return logger.throwError('Request from not found');
    }

    let extrinsic: SubmittableExtrinsic<'promise'>;

    // @TODO create contract
    if (!tx.to) {
      extrinsic = this.provider.api.tx.evm.create(
        tx.data,
        toBN(tx.value),
        toBN(gasLimit),
        toBN(storageLimit.isNegative() ? 0 : storageLimit),
        tx.accessList || []
      );
    } else {
      extrinsic = this.provider.api.tx.evm.call(
        tx.to,
        tx.data,
        toBN(tx.value),
        toBN(gasLimit),
        toBN(storageLimit.isNegative() ? 0 : storageLimit),
        tx.accessList || []
      );
    }

    await extrinsic.signAsync(signerAddress);

    return new Promise((resolve, reject) => {
      extrinsic
        .send((result: SubmittableResult) => {
          handleTxResponse(result, this.provider.api)
            .then(() => {
              resolve({
                hash: extrinsic.hash.toHex(),
                from: from || '',
                confirmations: 0,
                nonce: toBN(tx.nonce).toNumber(),
                gasLimit: BigNumber.from(tx.gasLimit || '0'),
                gasPrice: BigNumber.from(1),
                data: dataToString(data),
                value: BigNumber.from(tx.value || '0'),
                chainId: +this.provider.api.consts.evmAccounts.chainId.toString(),
                wait: (confirmations?: number): Promise<TransactionReceipt> => {
                  const hex = result.status.isInBlock
                    ? result.status.asInBlock.toHex()
                    : result.status.asFinalized.toHex();
                  return this.provider.getTransactionReceiptAtBlock(extrinsic.hash.toHex(), hex);
                }
              });
            })
            .catch(reject);
        })
        .catch(reject);
    });
  }