diff --git a/src/feeOracle.ts b/src/feeOracle.ts index 8c08fdc..190a939 100644 --- a/src/feeOracle.ts +++ b/src/feeOracle.ts @@ -2,14 +2,14 @@ import { GasPriceOracle } from '@tornado/gas-price-oracle'; import { BigNumber, BigNumberish, ethers } from 'ethers'; import { parseUnits } from 'ethers/lib/utils'; import { TransactionData, TxType, ITornadoFeeOracle, GasPrice, LegacyGasPriceKey } from './types'; -import { Provider } from '@ethersproject/abstract-provider'; +import { JsonRpcProvider } from '@ethersproject/providers'; import { ChainId, defaultGasPrices } from './config'; -import { bump, calculateGasPriceInWei, fromGweiToWeiHex, serializeTx } from './utils'; +import { bump, calculateGasPriceInWei, convertETHToToken, fromGweiToWeiHex, serializeTx } from './utils'; import { getOptimismL1FeeOracle } from './contracts/factories'; import { AvailableTokenSymbols } from '@tornado/tornado-config'; export abstract class TornadoFeeOracle implements ITornadoFeeOracle { - protected provider: Provider; + protected provider: JsonRpcProvider; public constructor( protected chainId: ChainId, @@ -94,7 +94,7 @@ export abstract class TornadoFeeOracle implements ITornadoFeeOracle { /** * Estimates gas limit for transaction (or basic gas limit, if no tx data provided) * @param {TransactionData} tx Transaction data (object in web3 / ethers format) - * @param {TxType} type Tornado transaction type: withdrawal by user, withdrawal by relayer or 'other' + * @param {TxType} type Tornado transaction type: withdrawal by user, withdrawal by relayer, relayer fee check or 'other' * @returns {Promise} Gas limit */ abstract getGasLimit(tx?: TransactionData, type?: TxType): Promise; @@ -105,10 +105,30 @@ export abstract class TornadoFeeOracle implements ITornadoFeeOracle { * and if the relayer pays a commission and the transfer of tokens fails, this commission will remain to the relayer. * * Refund needed that recipient can use tokens after withdrawal (covers gas fee for send/swap) - * @returns {Promise} Refund amount + * @param {TransactionData} [tx] Transaction data (object in web3 / ethers format) + * @param {TxType} [type] Tornado transaction type: withdrawal by user, withdrawal by relayer, relayer fee check or 'other' + * @returns {Promise} Refund amount in WEI */ - async calculateRefundInETH(): Promise { - return (await this.getGas()).mul(2); + async calculateRefundInETH(tx?: TransactionData, type?: TxType): Promise { + return (await this.getGas(tx, type)).mul(2); + } + + /** + * Get refund amount on ETH or Goerli in non-native token + * @param {BigNumberish} tokenPriceInEth Token price in WEI in native currency + * @param {string | number} tokenDecimals Token (currency) decimals + * @param {TransactionData} [tx] Transaction data (object in web3 / ethers format) + * @param {TxType} [type] Tornado transaction type: withdrawal by user, withdrawal by relayer, relayer fee check or 'other' + * @returns {Promise} Refund amount in WEI in selected token + */ + async calculateRefundInToken( + tokenPriceInEth: BigNumberish, + tokenDecimals: string | number, + tx?: TransactionData, + type?: TxType, + ): Promise { + const refundInEth = await this.calculateRefundInETH(tx, type); + return convertETHToToken(refundInEth, tokenDecimals, tokenPriceInEth); } /** @@ -118,28 +138,26 @@ export abstract class TornadoFeeOracle implements ITornadoFeeOracle { * @param {TxType} type Tornado transaction type: withdrawal costs calculation from user side or from relayer side * @param {TransactionData} tx Transaction data (object in web3 / ethers format) * @param {number} relayerFeePercent Relayer fee percent from the transaction amount (for example, 0.15 for BNB or 0.4 for ETH Mainnet) - * @param {string} currency Currency symbol - * @param {number} amount Withdrawal amount in selected currency - * @param {number} decimals Token (currency) decimals - * @param {BigNumberish} refund Refund in ETH, if withdrawed other tokens on Mainnet (not ETH) - * @param {BigNumberish} tokenPriceInEth If withdrawing other token on Mainnet or Goerli, need to provide token price in ETH (in WEI) + * @param {AvailableTokenSymbols | Uppercase} currency Currency symbol + * @param {number | string } amount Withdrawal amount in selected currency + * @param {number | string } decimals Token (currency) decimals + * @param {BigNumberish} [refund=0] Refund in ETH, if withdrawed other tokens on Mainnet (not ETH) + * @param {BigNumberish} [tokenPriceInEth] If withdrawing other token on Mainnet or Goerli, need to provide token price in ETH (in WEI) * @returns {Promise} Fee in WEI */ async calculateWithdrawalFeeViaRelayer( type: TxType, tx: TransactionData, relayerFeePercent: number, - currency: AvailableTokenSymbols, - amount: string, - decimals: number, + currency: AvailableTokenSymbols | Uppercase, + amount: string | number, + decimals: string | number, refund: BigNumberish = 0, tokenPriceInEth?: BigNumberish, ): Promise { - let withdrawalFee = BigNumber.from(0); const gasCosts = await this.getGas(tx, type); - withdrawalFee = gasCosts; - const relayerFee = parseUnits(amount, decimals) + const relayerFee = parseUnits(amount.toString(), decimals) .mul(`${relayerFeePercent * 1e10}`) .div(`${100 * 1e10}`); @@ -148,10 +166,11 @@ export abstract class TornadoFeeOracle implements ITornadoFeeOracle { console.error('Token price is required argument, if not native chain token is withdrawed'); return BigNumber.from(0); } - const tokenDecimals = BigNumber.from(10).pow(decimals); - return withdrawalFee.add(refund).mul(tokenDecimals).div(tokenPriceInEth).add(relayerFee); + + const feeInEth = gasCosts.add(refund); + return convertETHToToken(feeInEth, decimals, tokenPriceInEth).add(relayerFee); } - return withdrawalFee.add(relayerFee); + return gasCosts.add(relayerFee); } } diff --git a/src/types.ts b/src/types.ts index abdbe9a..86e57d5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -36,7 +36,13 @@ export interface ITornadoFeeOracle { getGasPrice: (type?: TxType, speed?: LegacyGasPriceKey, bumpPercent?: number) => Promise; getGasPriceInHex: (type?: TxType, speed?: LegacyGasPriceKey, bumpPercent?: number) => Promise; getGasLimit: (tx?: TransactionData, type?: TxType, bumpPercent?: number) => Promise; - calculateRefundInETH: () => Promise; + calculateRefundInETH: (tx?: TransactionData, type?: TxType) => Promise; + calculateRefundInToken: ( + tokenPriceInEth: BigNumberish, + tokenDecimals: string | number, + tx?: TransactionData, + type?: TxType, + ) => Promise; calculateWithdrawalFeeViaRelayer: ( type: TxType, tx: TransactionData, diff --git a/src/utils.ts b/src/utils.ts index 0ed4102..b7768c7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -32,3 +32,12 @@ export function bump(value: BigNumberish, percent: number): BigNumber { export function fromGweiToWeiHex(value: number | string): BigNumberish { return BigNumber.from(BigNumberFloat(value).times(GWEI).toString()).toHexString(); } + +export function convertETHToToken( + amountInWEI: BigNumberish, + tokenDecimals: number | string, + tokenPriceInWei: BigNumberish, +): BigNumber { + const tokenDecimalsMultiplier = BigNumber.from(10).pow(tokenDecimals); + return BigNumber.from(amountInWEI).mul(tokenDecimalsMultiplier).div(tokenPriceInWei); +}