From 1267436ff61257fe7686761da8ff2e88f0602f84 Mon Sep 17 00:00:00 2001 From: Maxim Kucherov Date: Fri, 19 Aug 2022 18:16:17 +0300 Subject: [PATCH 1/5] Implement fees calculation for ethereum operations --- .../erc20EthereumWeb3AtomexProtocolV1.ts | 12 ++--- .../ethereumWeb3AtomexProtocolV1.ts | 12 ++--- .../atomexProtocol/web3AtomexProtocolV1.ts | 45 +++++++++++++++++-- src/evm/helpers/index.ts | 1 + src/evm/helpers/web3Helper.ts | 10 +++++ src/evm/utils/index.ts | 3 ++ 6 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 src/evm/helpers/index.ts create mode 100644 src/evm/helpers/web3Helper.ts create mode 100644 src/evm/utils/index.ts diff --git a/src/ethereum/atomexProtocol/erc20EthereumWeb3AtomexProtocolV1.ts b/src/ethereum/atomexProtocol/erc20EthereumWeb3AtomexProtocolV1.ts index b176f625..0f03b517 100644 --- a/src/ethereum/atomexProtocol/erc20EthereumWeb3AtomexProtocolV1.ts +++ b/src/ethereum/atomexProtocol/erc20EthereumWeb3AtomexProtocolV1.ts @@ -28,8 +28,8 @@ export class ERC20EthereumWeb3AtomexProtocolV1 extends Web3AtomexProtocolV1 { throw new Error('Method not implemented.'); } - getInitiateFees(_params: Partial): Promise { - throw new Error('Method not implemented.'); + getInitiateFees(params: Partial): Promise { + return super.getInitiateFees(params); } redeem(_params: AtomexProtocolV1RedeemParameters): Promise { @@ -40,15 +40,15 @@ export class ERC20EthereumWeb3AtomexProtocolV1 extends Web3AtomexProtocolV1 { throw new Error('Method not implemented.'); } - getRedeemFees(_params: Partial): Promise { - throw new Error('Method not implemented.'); + getRedeemFees(params: Partial): Promise { + return super.getRedeemFees(params); } refund(_params: AtomexProtocolV1RefundParameters): Promise { throw new Error('Method not implemented.'); } - getRefundFees(_params: Partial): Promise { - throw new Error('Method not implemented.'); + getRefundFees(params: Partial): Promise { + return super.getRefundFees(params); } } diff --git a/src/ethereum/atomexProtocol/ethereumWeb3AtomexProtocolV1.ts b/src/ethereum/atomexProtocol/ethereumWeb3AtomexProtocolV1.ts index d55632c9..3aac7ca1 100644 --- a/src/ethereum/atomexProtocol/ethereumWeb3AtomexProtocolV1.ts +++ b/src/ethereum/atomexProtocol/ethereumWeb3AtomexProtocolV1.ts @@ -24,8 +24,8 @@ export class EthereumWeb3AtomexProtocolV1 extends Web3AtomexProtocolV1 { throw new Error('Method not implemented.'); } - async getInitiateFees(_params: Partial): Promise { - throw new Error('Method not implemented.'); + getInitiateFees(params: Partial): Promise { + return super.getInitiateFees(params); } redeem(_params: AtomexProtocolV1RedeemParameters): Promise { @@ -36,15 +36,15 @@ export class EthereumWeb3AtomexProtocolV1 extends Web3AtomexProtocolV1 { throw new Error('Method not implemented.'); } - getRedeemFees(_params: Partial): Promise { - throw new Error('Method not implemented.'); + getRedeemFees(params: Partial): Promise { + return super.getRedeemFees(params); } refund(_params: AtomexProtocolV1RefundParameters): Promise { throw new Error('Method not implemented.'); } - getRefundFees(_params: Partial): Promise { - throw new Error('Method not implemented.'); + getRefundFees(params: Partial): Promise { + return super.getRefundFees(params); } } diff --git a/src/evm/atomexProtocol/web3AtomexProtocolV1.ts b/src/evm/atomexProtocol/web3AtomexProtocolV1.ts index 623cbfa2..7212365d 100644 --- a/src/evm/atomexProtocol/web3AtomexProtocolV1.ts +++ b/src/evm/atomexProtocol/web3AtomexProtocolV1.ts @@ -1,5 +1,6 @@ -import type BigNumber from 'bignumber.js'; +import BigNumber from 'bignumber.js'; import type Web3 from 'web3'; +import type { Unit } from 'web3-utils'; import type { AtomexBlockchainProvider, @@ -8,9 +9,12 @@ import type { } from '../../blockchain/index'; import type { AtomexNetwork } from '../../common/index'; import type { DeepReadonly } from '../../core/index'; +import { web3Helper } from '../helpers'; import type { Web3AtomexProtocolV1Options } from '../models/index'; +import { gweiInEth } from '../utils'; export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { + protected static maxNetworkFeeMultiplier = new BigNumber(1.2); readonly version = 1; constructor( @@ -28,17 +32,43 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { abstract initiate(_params: AtomexProtocolV1InitiateParameters): Promise; - abstract getInitiateFees(_params: Partial): Promise; + async getInitiateFees(params: Partial): Promise { + const gasPrice = await this.getGasPriceInGwei(); + const gasLimitOptions = this.atomexProtocolOptions.initiateOperation.gasLimit; + const hasRewardForRedeem = params.rewardForRedeem?.isGreaterThan(0); + const gasLimit = new BigNumber(hasRewardForRedeem ? gasLimitOptions.withReward : gasLimitOptions.withoutReward); + + const estimated = gasPrice.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier).div(gweiInEth); + const result: FeesInfo = { estimated, max: estimated }; + + return Promise.resolve(result); + } abstract redeem(_params: AtomexProtocolV1RedeemParameters): Promise; abstract getRedeemReward(_nativeTokenPriceInUsd: number, _nativeTokenPriceInCurrency: number): Promise; - abstract getRedeemFees(_params: Partial): Promise; + async getRedeemFees(_params: Partial): Promise { + const gasPrice = await this.getGasPriceInGwei(); + const gasLimit = this.atomexProtocolOptions.redeemOperation.gasLimit; + + const estimated = gasPrice.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier).div(gweiInEth); + const result: FeesInfo = { estimated, max: estimated }; + + return Promise.resolve(result); + } abstract refund(_params: AtomexProtocolV1RefundParameters): Promise; - abstract getRefundFees(_params: Partial): Promise; + async getRefundFees(_params: Partial): Promise { + const gasPrice = await this.getGasPriceInGwei(); + const gasLimit = this.atomexProtocolOptions.refundOperation.gasLimit; + + const estimated = gasPrice.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier).div(gweiInEth); + const result: FeesInfo = { estimated, max: estimated }; + + return Promise.resolve(result); + } protected async getReadonlyWeb3(): Promise { const toolkit = await this.atomexBlockchainProvider.getReadonlyToolkit('web3', this.blockchain); @@ -55,4 +85,11 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { return web3Wallet; } + + protected async getGasPriceInGwei(): Promise { + const toolkit = await this.getReadonlyWeb3(); + const gasPrice = await web3Helper.getGasPrice(toolkit, 'gwei'); + + return gasPrice; + } } diff --git a/src/evm/helpers/index.ts b/src/evm/helpers/index.ts new file mode 100644 index 00000000..865d4b06 --- /dev/null +++ b/src/evm/helpers/index.ts @@ -0,0 +1 @@ +export * as web3Helper from './web3Helper'; diff --git a/src/evm/helpers/web3Helper.ts b/src/evm/helpers/web3Helper.ts new file mode 100644 index 00000000..ed3faf16 --- /dev/null +++ b/src/evm/helpers/web3Helper.ts @@ -0,0 +1,10 @@ +import BigNumber from 'bignumber.js'; +import type Web3 from 'web3'; +import type { Unit } from 'web3-utils'; + +export const getGasPrice = async (toolkit: Web3, unit: Unit): Promise => { + const gasPriceInWei = await toolkit.eth.getGasPrice(); + const gasPriceInTargetUnit = toolkit.utils.fromWei(gasPriceInWei, unit); + + return new BigNumber(gasPriceInTargetUnit); +}; diff --git a/src/evm/utils/index.ts b/src/evm/utils/index.ts new file mode 100644 index 00000000..2cbb948e --- /dev/null +++ b/src/evm/utils/index.ts @@ -0,0 +1,3 @@ +import BigNumber from 'bignumber.js'; + +export const gweiInEth = new BigNumber(1_000_000_000); From afc5b7f06fba1703844f1cefa9000b75fa494edb Mon Sep 17 00:00:00 2001 From: Maxim Kucherov Date: Fri, 19 Aug 2022 18:53:30 +0300 Subject: [PATCH 2/5] Remove redundant import --- src/evm/atomexProtocol/web3AtomexProtocolV1.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/evm/atomexProtocol/web3AtomexProtocolV1.ts b/src/evm/atomexProtocol/web3AtomexProtocolV1.ts index 7212365d..58431b54 100644 --- a/src/evm/atomexProtocol/web3AtomexProtocolV1.ts +++ b/src/evm/atomexProtocol/web3AtomexProtocolV1.ts @@ -1,6 +1,5 @@ import BigNumber from 'bignumber.js'; import type Web3 from 'web3'; -import type { Unit } from 'web3-utils'; import type { AtomexBlockchainProvider, From 25574704f677b48f0d552ed846c45497697b0a89 Mon Sep 17 00:00:00 2001 From: Maxim Kucherov Date: Fri, 19 Aug 2022 19:24:22 +0300 Subject: [PATCH 3/5] Refactor calculations --- .../atomexProtocol/web3AtomexProtocolV1.ts | 34 +++++++++++-------- src/evm/helpers/index.ts | 2 +- src/evm/helpers/web3Helper.ts | 22 +++++++++--- src/evm/utils/index.ts | 3 -- 4 files changed, 37 insertions(+), 24 deletions(-) delete mode 100644 src/evm/utils/index.ts diff --git a/src/evm/atomexProtocol/web3AtomexProtocolV1.ts b/src/evm/atomexProtocol/web3AtomexProtocolV1.ts index 58431b54..d80c1980 100644 --- a/src/evm/atomexProtocol/web3AtomexProtocolV1.ts +++ b/src/evm/atomexProtocol/web3AtomexProtocolV1.ts @@ -8,9 +8,8 @@ import type { } from '../../blockchain/index'; import type { AtomexNetwork } from '../../common/index'; import type { DeepReadonly } from '../../core/index'; -import { web3Helper } from '../helpers'; +import { Web3Helper } from '../helpers'; import type { Web3AtomexProtocolV1Options } from '../models/index'; -import { gweiInEth } from '../utils'; export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { protected static maxNetworkFeeMultiplier = new BigNumber(1.2); @@ -32,12 +31,14 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { abstract initiate(_params: AtomexProtocolV1InitiateParameters): Promise; async getInitiateFees(params: Partial): Promise { - const gasPrice = await this.getGasPriceInGwei(); + const web3Helper = await this.createWeb3Helper(); + const gasPriceInWei = await web3Helper.getGasPriceInWei(); const gasLimitOptions = this.atomexProtocolOptions.initiateOperation.gasLimit; const hasRewardForRedeem = params.rewardForRedeem?.isGreaterThan(0); const gasLimit = new BigNumber(hasRewardForRedeem ? gasLimitOptions.withReward : gasLimitOptions.withoutReward); - const estimated = gasPrice.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier).div(gweiInEth); + const estimatedWei = gasPriceInWei.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier); + const estimated = web3Helper.convertFromWei(estimatedWei, 'ether'); const result: FeesInfo = { estimated, max: estimated }; return Promise.resolve(result); @@ -48,10 +49,12 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { abstract getRedeemReward(_nativeTokenPriceInUsd: number, _nativeTokenPriceInCurrency: number): Promise; async getRedeemFees(_params: Partial): Promise { - const gasPrice = await this.getGasPriceInGwei(); + const web3Helper = await this.createWeb3Helper(); + const gasPriceInWei = await web3Helper.getGasPriceInWei(); const gasLimit = this.atomexProtocolOptions.redeemOperation.gasLimit; - const estimated = gasPrice.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier).div(gweiInEth); + const estimatedWei = gasPriceInWei.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier); + const estimated = web3Helper.convertFromWei(estimatedWei, 'ether'); const result: FeesInfo = { estimated, max: estimated }; return Promise.resolve(result); @@ -60,15 +63,23 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { abstract refund(_params: AtomexProtocolV1RefundParameters): Promise; async getRefundFees(_params: Partial): Promise { - const gasPrice = await this.getGasPriceInGwei(); + const web3Helper = await this.createWeb3Helper(); + const gasPriceInWei = await web3Helper.getGasPriceInWei(); const gasLimit = this.atomexProtocolOptions.refundOperation.gasLimit; - const estimated = gasPrice.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier).div(gweiInEth); + const estimatedWei = gasPriceInWei.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier); + const estimated = web3Helper.convertFromWei(estimatedWei, 'ether'); const result: FeesInfo = { estimated, max: estimated }; return Promise.resolve(result); } + protected async createWeb3Helper(): Promise { + const toolkit = await this.getReadonlyWeb3(); + + return new Web3Helper(toolkit); + } + protected async getReadonlyWeb3(): Promise { const toolkit = await this.atomexBlockchainProvider.getReadonlyToolkit('web3', this.blockchain); if (!toolkit) @@ -84,11 +95,4 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { return web3Wallet; } - - protected async getGasPriceInGwei(): Promise { - const toolkit = await this.getReadonlyWeb3(); - const gasPrice = await web3Helper.getGasPrice(toolkit, 'gwei'); - - return gasPrice; - } } diff --git a/src/evm/helpers/index.ts b/src/evm/helpers/index.ts index 865d4b06..575cb40c 100644 --- a/src/evm/helpers/index.ts +++ b/src/evm/helpers/index.ts @@ -1 +1 @@ -export * as web3Helper from './web3Helper'; +export { Web3Helper } from './web3Helper'; diff --git a/src/evm/helpers/web3Helper.ts b/src/evm/helpers/web3Helper.ts index ed3faf16..0a67bdcc 100644 --- a/src/evm/helpers/web3Helper.ts +++ b/src/evm/helpers/web3Helper.ts @@ -2,9 +2,21 @@ import BigNumber from 'bignumber.js'; import type Web3 from 'web3'; import type { Unit } from 'web3-utils'; -export const getGasPrice = async (toolkit: Web3, unit: Unit): Promise => { - const gasPriceInWei = await toolkit.eth.getGasPrice(); - const gasPriceInTargetUnit = toolkit.utils.fromWei(gasPriceInWei, unit); +export class Web3Helper { + constructor( + private readonly toolkit: Web3 + ) { } - return new BigNumber(gasPriceInTargetUnit); -}; + async getGasPriceInWei(): Promise { + const gasPrice = await this.toolkit.eth.getGasPrice(); + + return new BigNumber(gasPrice); + } + + convertFromWei(value: BigNumber | string, unit: Unit): BigNumber { + const stringValue = typeof value === 'string' ? value : value.toString(); + const result = this.toolkit.utils.fromWei(stringValue, unit); + + return new BigNumber(result); + } +} diff --git a/src/evm/utils/index.ts b/src/evm/utils/index.ts deleted file mode 100644 index 2cbb948e..00000000 --- a/src/evm/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import BigNumber from 'bignumber.js'; - -export const gweiInEth = new BigNumber(1_000_000_000); From 65c6a2775165d406fb02ac4cdc64ff44d5b504ee Mon Sep 17 00:00:00 2001 From: Maxim Kucherov Date: Fri, 19 Aug 2022 19:31:31 +0300 Subject: [PATCH 4/5] Fix converting --- src/evm/helpers/web3Helper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evm/helpers/web3Helper.ts b/src/evm/helpers/web3Helper.ts index 0a67bdcc..32c12346 100644 --- a/src/evm/helpers/web3Helper.ts +++ b/src/evm/helpers/web3Helper.ts @@ -14,7 +14,7 @@ export class Web3Helper { } convertFromWei(value: BigNumber | string, unit: Unit): BigNumber { - const stringValue = typeof value === 'string' ? value : value.toString(); + const stringValue = typeof value === 'string' ? value : value.toString(10); const result = this.toolkit.utils.fromWei(stringValue, unit); return new BigNumber(result); From f68546d207735ab4a6b690fa3978c0d5b7924df6 Mon Sep 17 00:00:00 2001 From: Maxim Kucherov Date: Fri, 19 Aug 2022 19:53:22 +0300 Subject: [PATCH 5/5] Refactor --- .../atomexProtocol/web3AtomexProtocolV1.ts | 26 +++++++------------ src/evm/helpers/index.ts | 2 +- src/evm/helpers/web3Helper.ts | 24 +++++++---------- 3 files changed, 20 insertions(+), 32 deletions(-) diff --git a/src/evm/atomexProtocol/web3AtomexProtocolV1.ts b/src/evm/atomexProtocol/web3AtomexProtocolV1.ts index d80c1980..bff132ab 100644 --- a/src/evm/atomexProtocol/web3AtomexProtocolV1.ts +++ b/src/evm/atomexProtocol/web3AtomexProtocolV1.ts @@ -8,7 +8,7 @@ import type { } from '../../blockchain/index'; import type { AtomexNetwork } from '../../common/index'; import type { DeepReadonly } from '../../core/index'; -import { Web3Helper } from '../helpers'; +import { web3Helper } from '../helpers'; import type { Web3AtomexProtocolV1Options } from '../models/index'; export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { @@ -31,14 +31,14 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { abstract initiate(_params: AtomexProtocolV1InitiateParameters): Promise; async getInitiateFees(params: Partial): Promise { - const web3Helper = await this.createWeb3Helper(); - const gasPriceInWei = await web3Helper.getGasPriceInWei(); + const toolkit = await this.getReadonlyWeb3(); + const gasPriceInWei = await web3Helper.getGasPriceInWei(toolkit); const gasLimitOptions = this.atomexProtocolOptions.initiateOperation.gasLimit; const hasRewardForRedeem = params.rewardForRedeem?.isGreaterThan(0); const gasLimit = new BigNumber(hasRewardForRedeem ? gasLimitOptions.withReward : gasLimitOptions.withoutReward); const estimatedWei = gasPriceInWei.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier); - const estimated = web3Helper.convertFromWei(estimatedWei, 'ether'); + const estimated = web3Helper.convertFromWei(toolkit, estimatedWei, 'ether'); const result: FeesInfo = { estimated, max: estimated }; return Promise.resolve(result); @@ -49,12 +49,12 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { abstract getRedeemReward(_nativeTokenPriceInUsd: number, _nativeTokenPriceInCurrency: number): Promise; async getRedeemFees(_params: Partial): Promise { - const web3Helper = await this.createWeb3Helper(); - const gasPriceInWei = await web3Helper.getGasPriceInWei(); + const toolkit = await this.getReadonlyWeb3(); + const gasPriceInWei = await web3Helper.getGasPriceInWei(toolkit); const gasLimit = this.atomexProtocolOptions.redeemOperation.gasLimit; const estimatedWei = gasPriceInWei.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier); - const estimated = web3Helper.convertFromWei(estimatedWei, 'ether'); + const estimated = web3Helper.convertFromWei(toolkit, estimatedWei, 'ether'); const result: FeesInfo = { estimated, max: estimated }; return Promise.resolve(result); @@ -63,23 +63,17 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 { abstract refund(_params: AtomexProtocolV1RefundParameters): Promise; async getRefundFees(_params: Partial): Promise { - const web3Helper = await this.createWeb3Helper(); - const gasPriceInWei = await web3Helper.getGasPriceInWei(); + const toolkit = await this.getReadonlyWeb3(); + const gasPriceInWei = await web3Helper.getGasPriceInWei(toolkit); const gasLimit = this.atomexProtocolOptions.refundOperation.gasLimit; const estimatedWei = gasPriceInWei.multipliedBy(gasLimit).multipliedBy(Web3AtomexProtocolV1.maxNetworkFeeMultiplier); - const estimated = web3Helper.convertFromWei(estimatedWei, 'ether'); + const estimated = web3Helper.convertFromWei(toolkit, estimatedWei, 'ether'); const result: FeesInfo = { estimated, max: estimated }; return Promise.resolve(result); } - protected async createWeb3Helper(): Promise { - const toolkit = await this.getReadonlyWeb3(); - - return new Web3Helper(toolkit); - } - protected async getReadonlyWeb3(): Promise { const toolkit = await this.atomexBlockchainProvider.getReadonlyToolkit('web3', this.blockchain); if (!toolkit) diff --git a/src/evm/helpers/index.ts b/src/evm/helpers/index.ts index 575cb40c..865d4b06 100644 --- a/src/evm/helpers/index.ts +++ b/src/evm/helpers/index.ts @@ -1 +1 @@ -export { Web3Helper } from './web3Helper'; +export * as web3Helper from './web3Helper'; diff --git a/src/evm/helpers/web3Helper.ts b/src/evm/helpers/web3Helper.ts index 32c12346..600380c2 100644 --- a/src/evm/helpers/web3Helper.ts +++ b/src/evm/helpers/web3Helper.ts @@ -2,21 +2,15 @@ import BigNumber from 'bignumber.js'; import type Web3 from 'web3'; import type { Unit } from 'web3-utils'; -export class Web3Helper { - constructor( - private readonly toolkit: Web3 - ) { } +export const getGasPriceInWei = async (toolkit: Web3): Promise => { + const gasPrice = await toolkit.eth.getGasPrice(); - async getGasPriceInWei(): Promise { - const gasPrice = await this.toolkit.eth.getGasPrice(); + return new BigNumber(gasPrice); +}; - return new BigNumber(gasPrice); - } +export const convertFromWei = (toolkit: Web3, value: BigNumber | string, unit: Unit): BigNumber => { + const stringValue = typeof value === 'string' ? value : value.toString(10); + const result = toolkit.utils.fromWei(stringValue, unit); - convertFromWei(value: BigNumber | string, unit: Unit): BigNumber { - const stringValue = typeof value === 'string' ? value : value.toString(10); - const result = this.toolkit.utils.fromWei(stringValue, unit); - - return new BigNumber(result); - } -} + return new BigNumber(result); +};