From 337041688e39418fbf19b338f2509972a7399097 Mon Sep 17 00:00:00 2001 From: Alexandre ABRIOUX Date: Fri, 4 Feb 2022 14:33:12 +0100 Subject: [PATCH 1/4] chore(request-node): eip-1559 for storage --- packages/request-node/package.json | 1 + .../src/thegraph/TheGraphStorage.ts | 16 +++- yarn.lock | 91 +++++++++++++++++++ 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/packages/request-node/package.json b/packages/request-node/package.json index 930c168d0..27fddcf54 100644 --- a/packages/request-node/package.json +++ b/packages/request-node/package.json @@ -51,6 +51,7 @@ "chalk": "4.1.0", "cors": "2.8.5", "dotenv": "8.2.0", + "eip1559-fee-suggestions-ethers": "1.3.3", "ethers": "5.5.2", "express": "4.17.1", "graphql": "15.5.0", diff --git a/packages/request-node/src/thegraph/TheGraphStorage.ts b/packages/request-node/src/thegraph/TheGraphStorage.ts index f3c83401e..78b838bf3 100644 --- a/packages/request-node/src/thegraph/TheGraphStorage.ts +++ b/packages/request-node/src/thegraph/TheGraphStorage.ts @@ -1,10 +1,11 @@ import { EventEmitter } from 'events'; -import { utils, Signer, ContractReceipt } from 'ethers'; +import { utils, Signer, ContractReceipt, BigNumber, providers } from 'ethers'; import TypedEmitter from 'typed-emitter'; import Utils from '@requestnetwork/utils'; import { LogTypes, StorageTypes } from '@requestnetwork/types'; import { requestHashSubmitterArtifact } from '@requestnetwork/smart-contracts'; import { RequestOpenHashSubmitter } from '@requestnetwork/smart-contracts/types'; +import { suggestFees } from 'eip1559-fee-suggestions-ethers'; type TheGraphStorageProps = { network: string; @@ -43,10 +44,17 @@ export class TheGraphStorage { const { ipfsHash, ipfsSize } = await this.ipfsStorage.ipfsAdd(content); const fee = await this.hashSubmitter.getFeesAmount(ipfsSize); + const suggestedFee = await suggestFees( + this.hashSubmitter.provider as providers.JsonRpcProvider, + ); const tx = await this.hashSubmitter.submitHash( ipfsHash, utils.hexZeroPad(utils.hexlify(ipfsSize), 32), - { value: fee }, + { + value: fee, + maxFeePerGas: BigNumber.from(suggestedFee.baseFeeSuggestion), + maxPriorityFeePerGas: BigNumber.from(suggestedFee.maxPriorityFeeSuggestions.fast), + }, ); const eventEmitter = new EventEmitter() as TheGraphStorageEventEmitter; @@ -66,13 +74,13 @@ export class TheGraphStorage { void tx .wait() - .then((receipt) => { + .then((receipt: providers.TransactionReceipt) => { this.logger.debug( `TX ${receipt.transactionHash} confirmed at block ${receipt.blockNumber}`, ); eventEmitter.emit('confirmed', receipt); }) - .catch((e) => eventEmitter.emit('error', e)); + .catch((e: Error) => eventEmitter.emit('error', e)); return Object.assign(eventEmitter, result); } diff --git a/yarn.lock b/yarn.lock index 379f50146..3f9b18fe3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2686,6 +2686,13 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/networks@5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.2.tgz#784c8b1283cd2a931114ab428dae1bd00c07630b" + integrity sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ== + dependencies: + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.4.0.tgz#71eecd3ef3755118b42c1a5d2a44a7e07202e10a" @@ -2805,6 +2812,31 @@ bech32 "1.1.4" ws "7.4.6" +"@ethersproject/providers@5.5.3", "@ethersproject/providers@^5.5.0": + version "5.5.3" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.3.tgz#56c2b070542ac44eb5de2ed3cf6784acd60a3130" + integrity sha512-ZHXxXXXWHuwCQKrgdpIkbzMNJMvs+9YWemanwp1fA7XZEv7QlilseysPvQe0D7Q7DlkJX/w/bGA1MdgK2TbGvA== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/basex" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + bech32 "1.1.4" + ws "7.4.6" + "@ethersproject/random@5.4.0", "@ethersproject/random@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.4.0.tgz#9cdde60e160d024be39cc16f8de3b9ce39191e16" @@ -2821,6 +2853,14 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/random@5.5.1": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.5.1.tgz#7cdf38ea93dc0b1ed1d8e480ccdaf3535c555415" + integrity sha512-YaU2dQ7DuhL5Au7KbcQLHxcRHfgyNgvFV4sQOo0HrtW3Zkrc9ctWNz8wXQ4uCSfSDsqX2vcjhroxU5RQRV0nqA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/rlp@5.4.0", "@ethersproject/rlp@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.4.0.tgz#de61afda5ff979454e76d3b3310a6c32ad060931" @@ -10839,6 +10879,16 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +eip1559-fee-suggestions-ethers@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/eip1559-fee-suggestions-ethers/-/eip1559-fee-suggestions-ethers-1.3.3.tgz#cf21b6ae4734b697c8f79e556d91aeda3e464d60" + integrity sha512-W/5SQICRpDL0XvOdcX5oVWfaE2sow2u9il8I6Qyd9fkL9XY1knypywTcoBTdU9ULrPoP3phn/RnEKGq7Y8qkLw== + dependencies: + "@ethersproject/providers" "^5.5.0" + bignumber.js "^9.0.1" + ethers "^5.4.2" + moving-averages "^4.0.6" + electron-to-chromium@^1.3.47: version "1.3.778" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.778.tgz#bf01048736c95b78f2988e88005e0ebb385942a4" @@ -12126,6 +12176,42 @@ ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.2: "@ethersproject/web" "5.4.0" "@ethersproject/wordlists" "5.4.0" +ethers@^5.4.2: + version "5.5.4" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.5.4.tgz#e1155b73376a2f5da448e4a33351b57a885f4352" + integrity sha512-N9IAXsF8iKhgHIC6pquzRgPBJEzc9auw3JoRkaKe+y4Wl/LFBtDDunNe7YmdomontECAcC5APaAgWZBiu1kirw== + dependencies: + "@ethersproject/abi" "5.5.0" + "@ethersproject/abstract-provider" "5.5.1" + "@ethersproject/abstract-signer" "5.5.0" + "@ethersproject/address" "5.5.0" + "@ethersproject/base64" "5.5.0" + "@ethersproject/basex" "5.5.0" + "@ethersproject/bignumber" "5.5.0" + "@ethersproject/bytes" "5.5.0" + "@ethersproject/constants" "5.5.0" + "@ethersproject/contracts" "5.5.0" + "@ethersproject/hash" "5.5.0" + "@ethersproject/hdnode" "5.5.0" + "@ethersproject/json-wallets" "5.5.0" + "@ethersproject/keccak256" "5.5.0" + "@ethersproject/logger" "5.5.0" + "@ethersproject/networks" "5.5.2" + "@ethersproject/pbkdf2" "5.5.0" + "@ethersproject/properties" "5.5.0" + "@ethersproject/providers" "5.5.3" + "@ethersproject/random" "5.5.1" + "@ethersproject/rlp" "5.5.0" + "@ethersproject/sha2" "5.5.0" + "@ethersproject/signing-key" "5.5.0" + "@ethersproject/solidity" "5.5.0" + "@ethersproject/strings" "5.5.0" + "@ethersproject/transactions" "5.5.0" + "@ethersproject/units" "5.5.0" + "@ethersproject/wallet" "5.5.0" + "@ethersproject/web" "5.5.1" + "@ethersproject/wordlists" "5.5.0" + ethjs-abi@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/ethjs-abi/-/ethjs-abi-0.2.1.tgz#e0a7a93a7e81163a94477bad56ede524ab6de533" @@ -18066,6 +18152,11 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" +moving-averages@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/moving-averages/-/moving-averages-4.0.6.tgz#4978d4d9f68aef8f2b5fa028b1ec316da0cc8c95" + integrity sha512-Jv+mH0emTFP40Q5ONsBqTfIO9NuUyK9zuW4pWbOzWJm8jEqpLBtAH2CnE2MFIuH/G9f9nDugmnDVUJaHx9jckw== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" From c162f4a7da6dcd28990a88383f50eae324324090 Mon Sep 17 00:00:00 2001 From: Alexandre ABRIOUX Date: Mon, 7 Feb 2022 15:22:04 +0100 Subject: [PATCH 2/4] fix: support pre-eip-1559 networks --- .../src/thegraph/TheGraphStorage.ts | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/request-node/src/thegraph/TheGraphStorage.ts b/packages/request-node/src/thegraph/TheGraphStorage.ts index 78b838bf3..d33f44387 100644 --- a/packages/request-node/src/thegraph/TheGraphStorage.ts +++ b/packages/request-node/src/thegraph/TheGraphStorage.ts @@ -1,11 +1,12 @@ import { EventEmitter } from 'events'; -import { utils, Signer, ContractReceipt, BigNumber, providers } from 'ethers'; +import { BigNumber, ContractReceipt, PayableOverrides, providers, Signer, utils } from 'ethers'; import TypedEmitter from 'typed-emitter'; import Utils from '@requestnetwork/utils'; import { LogTypes, StorageTypes } from '@requestnetwork/types'; import { requestHashSubmitterArtifact } from '@requestnetwork/smart-contracts'; import { RequestOpenHashSubmitter } from '@requestnetwork/smart-contracts/types'; import { suggestFees } from 'eip1559-fee-suggestions-ethers'; +import GasPriceDefiner from '@requestnetwork/ethereum-storage/dist/src/gas-price-definer'; type TheGraphStorageProps = { network: string; @@ -44,17 +45,27 @@ export class TheGraphStorage { const { ipfsHash, ipfsSize } = await this.ipfsStorage.ipfsAdd(content); const fee = await this.hashSubmitter.getFeesAmount(ipfsSize); - const suggestedFee = await suggestFees( - this.hashSubmitter.provider as providers.JsonRpcProvider, - ); + const overrides: PayableOverrides = { value: fee }; + try { + const suggestedFee = await suggestFees( + this.hashSubmitter.provider as providers.JsonRpcProvider, + ); + overrides.maxFeePerGas = BigNumber.from(suggestedFee.baseFeeSuggestion); + overrides.maxPriorityFeePerGas = BigNumber.from( + suggestedFee.maxPriorityFeeSuggestions.urgent, + ); + } catch (e) { + // for networks where the eth_feeHistory RPC method is not available (pre EIP-1559) + const gasPriceDefiner = new GasPriceDefiner(); + overrides.gasPrice = await gasPriceDefiner.getGasPrice( + StorageTypes.GasPriceType.FAST, + this.network, + ); + } const tx = await this.hashSubmitter.submitHash( ipfsHash, utils.hexZeroPad(utils.hexlify(ipfsSize), 32), - { - value: fee, - maxFeePerGas: BigNumber.from(suggestedFee.baseFeeSuggestion), - maxPriorityFeePerGas: BigNumber.from(suggestedFee.maxPriorityFeeSuggestions.fast), - }, + overrides, ); const eventEmitter = new EventEmitter() as TheGraphStorageEventEmitter; From feb49392cba8f447f5a9d1e97223320874aaf228 Mon Sep 17 00:00:00 2001 From: Alexandre ABRIOUX Date: Mon, 7 Feb 2022 16:11:22 +0100 Subject: [PATCH 3/4] fixes https://github.com/RequestNetwork/requestNetwork/pull/760#discussion_r800713622 --- packages/ethereum-storage/src/gas-price-definer.ts | 2 +- packages/ethereum-storage/src/index.ts | 1 + packages/ethereum-storage/src/smart-contract-manager.ts | 2 +- packages/ethereum-storage/test/gas-price-definer.test.ts | 2 +- packages/request-node/src/thegraph/TheGraphStorage.ts | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/ethereum-storage/src/gas-price-definer.ts b/packages/ethereum-storage/src/gas-price-definer.ts index 7d0a2913e..d7bfa0130 100644 --- a/packages/ethereum-storage/src/gas-price-definer.ts +++ b/packages/ethereum-storage/src/gas-price-definer.ts @@ -14,7 +14,7 @@ import XDaiFixedProvider from './gas-price-providers/xdai-fixed-provider'; * Determines the gas price to use depending on the used network * Polls gas price API providers if necessary */ -export default class GasPriceDefiner { +export class GasPriceDefiner { private defaultProviders = [ new EtherchainProvider(), new EthGasStationProvider(), diff --git a/packages/ethereum-storage/src/index.ts b/packages/ethereum-storage/src/index.ts index 06f814830..3cdfaf2e7 100644 --- a/packages/ethereum-storage/src/index.ts +++ b/packages/ethereum-storage/src/index.ts @@ -1,2 +1,3 @@ export { EthereumStorage } from './ethereum-storage'; +export { GasPriceDefiner } from './gas-price-definer'; export { IpfsStorage } from './ipfs-storage'; diff --git a/packages/ethereum-storage/src/smart-contract-manager.ts b/packages/ethereum-storage/src/smart-contract-manager.ts index 569a2230a..26ccb6f02 100644 --- a/packages/ethereum-storage/src/smart-contract-manager.ts +++ b/packages/ethereum-storage/src/smart-contract-manager.ts @@ -5,7 +5,7 @@ import * as Bluebird from 'bluebird'; import * as config from './config'; import EthereumBlocks from './ethereum-blocks'; import EthereumUtils from './ethereum-utils'; -import GasPriceDefiner from './gas-price-definer'; +import { GasPriceDefiner } from './gas-price-definer'; // eslint-disable-next-line @typescript-eslint/no-var-requires const web3Eth = require('web3-eth'); diff --git a/packages/ethereum-storage/test/gas-price-definer.test.ts b/packages/ethereum-storage/test/gas-price-definer.test.ts index 54505ec58..0dea696cd 100644 --- a/packages/ethereum-storage/test/gas-price-definer.test.ts +++ b/packages/ethereum-storage/test/gas-price-definer.test.ts @@ -4,7 +4,7 @@ import { StorageTypes } from '@requestnetwork/types'; import EthereumUtils from '../src/ethereum-utils'; import * as config from '../src/config'; -import GasPriceDefiner from '../src/gas-price-definer'; +import { GasPriceDefiner } from '../src/gas-price-definer'; import { BigNumber } from 'ethers'; diff --git a/packages/request-node/src/thegraph/TheGraphStorage.ts b/packages/request-node/src/thegraph/TheGraphStorage.ts index d33f44387..d643b2a88 100644 --- a/packages/request-node/src/thegraph/TheGraphStorage.ts +++ b/packages/request-node/src/thegraph/TheGraphStorage.ts @@ -6,7 +6,7 @@ import { LogTypes, StorageTypes } from '@requestnetwork/types'; import { requestHashSubmitterArtifact } from '@requestnetwork/smart-contracts'; import { RequestOpenHashSubmitter } from '@requestnetwork/smart-contracts/types'; import { suggestFees } from 'eip1559-fee-suggestions-ethers'; -import GasPriceDefiner from '@requestnetwork/ethereum-storage/dist/src/gas-price-definer'; +import { GasPriceDefiner } from '@requestnetwork/ethereum-storage'; type TheGraphStorageProps = { network: string; From ed5892aee60e09f08952b4a3906abba8d233498b Mon Sep 17 00:00:00 2001 From: Alexandre ABRIOUX Date: Mon, 7 Feb 2022 16:47:16 +0100 Subject: [PATCH 4/4] fixes https://github.com/RequestNetwork/requestNetwork/pull/760#discussion_r800712345 --- .../src/thegraph/TheGraphStorage.ts | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/request-node/src/thegraph/TheGraphStorage.ts b/packages/request-node/src/thegraph/TheGraphStorage.ts index d643b2a88..f39a1e0b8 100644 --- a/packages/request-node/src/thegraph/TheGraphStorage.ts +++ b/packages/request-node/src/thegraph/TheGraphStorage.ts @@ -7,6 +7,7 @@ import { requestHashSubmitterArtifact } from '@requestnetwork/smart-contracts'; import { RequestOpenHashSubmitter } from '@requestnetwork/smart-contracts/types'; import { suggestFees } from 'eip1559-fee-suggestions-ethers'; import { GasPriceDefiner } from '@requestnetwork/ethereum-storage'; +import assert from 'assert'; type TheGraphStorageProps = { network: string; @@ -21,15 +22,22 @@ export type TheGraphStorageEventEmitter = TypedEmitter<{ }>; export class TheGraphStorage { - private logger: LogTypes.ILogger; - private ipfsStorage: StorageTypes.IIpfsStorage; - private hashSubmitter: RequestOpenHashSubmitter; - private network: string; + private readonly logger: LogTypes.ILogger; + private readonly ipfsStorage: StorageTypes.IIpfsStorage; + private readonly hashSubmitter: RequestOpenHashSubmitter; + private readonly network: string; + private readonly provider: providers.JsonRpcProvider; + private enableEip1559 = true; constructor({ network, signer, ipfsStorage, logger }: TheGraphStorageProps) { this.logger = logger || new Utils.SimpleLogger(); this.ipfsStorage = ipfsStorage; this.network = network; + assert( + signer.provider instanceof providers.JsonRpcProvider, + 'TheGraphStorage provider must be a JsonRpcProvider', + ); + this.provider = signer.provider; this.hashSubmitter = requestHashSubmitterArtifact.connect( network, signer, @@ -38,6 +46,14 @@ export class TheGraphStorage { async initialize(): Promise { await this.ipfsStorage.initialize(); + try { + await this.provider.send('eth_feeHistory', []); + } catch (e) { + this.logger.warn( + 'This RPC provider does not support the "eth_feeHistory" method: switching to legacy gas price', + ); + this.enableEip1559 = false; + } this.logger.debug('TheGraph storage initialized'); } @@ -46,7 +62,7 @@ export class TheGraphStorage { const fee = await this.hashSubmitter.getFeesAmount(ipfsSize); const overrides: PayableOverrides = { value: fee }; - try { + if (this.enableEip1559) { const suggestedFee = await suggestFees( this.hashSubmitter.provider as providers.JsonRpcProvider, ); @@ -54,8 +70,8 @@ export class TheGraphStorage { overrides.maxPriorityFeePerGas = BigNumber.from( suggestedFee.maxPriorityFeeSuggestions.urgent, ); - } catch (e) { - // for networks where the eth_feeHistory RPC method is not available (pre EIP-1559) + } else { + // retro-compatibility for networks where the eth_feeHistory RPC method is not available (pre EIP-1559) const gasPriceDefiner = new GasPriceDefiner(); overrides.gasPrice = await gasPriceDefiner.getGasPrice( StorageTypes.GasPriceType.FAST,