diff --git a/packages/ethereum-storage/src/config.ts b/packages/ethereum-storage/src/config.ts index 77e0d80daf..f7e0fc3b76 100644 --- a/packages/ethereum-storage/src/config.ts +++ b/packages/ethereum-storage/src/config.ts @@ -17,6 +17,7 @@ const config = { retryDelay: 0, safeGasPriceLimit: '500000000000', transactionPollingTimeout: 300, + blockConfirmations: 2, }, ipfs: { defaultNode: { @@ -85,6 +86,14 @@ export function getDefaultEthereumGasPrice(): BigNumber { return BigNumber.from(process?.env?.GAS_PRICE_DEFAULT || config.ethereum.gasPriceDefault); } +/** + * Retrieve from config the default number of block confirmations to wait before considering a transaction successful + * @returns the number of block confirmations + */ +export function getDefaultEthereumBlockConfirmations(): number { + return config.ethereum.blockConfirmations; +} + /** * Retrieve from config the time to wait between query retries * @returns the query retry delay diff --git a/packages/ethereum-storage/src/ethereum-storage-ethers.ts b/packages/ethereum-storage/src/ethereum-storage-ethers.ts index 34817a143e..f3dd381976 100644 --- a/packages/ethereum-storage/src/ethereum-storage-ethers.ts +++ b/packages/ethereum-storage/src/ethereum-storage-ethers.ts @@ -5,6 +5,7 @@ import { CurrencyTypes, LogTypes, StorageTypes } from '@requestnetwork/types'; import { requestHashSubmitterArtifact } from '@requestnetwork/smart-contracts'; import { EthereumTransactionSubmitter } from './ethereum-tx-submitter'; import { getCurrentTimestampInSecond, SimpleLogger } from '@requestnetwork/utils'; +import { getDefaultEthereumBlockConfirmations } from './config'; export type GasDefinerProps = { gasPriceMin?: BigNumber; @@ -18,6 +19,7 @@ export type SubmitterProps = GasDefinerProps & { type StorageProps = SubmitterProps & { ipfsStorage: StorageTypes.IIpfsStorage; + blockConfirmations?: number; }; export type StorageEventEmitter = TypedEmitter<{ @@ -31,12 +33,21 @@ export class EthereumStorageEthers implements StorageTypes.IStorageWrite { private readonly network: CurrencyTypes.EvmChainName; private readonly txSubmitter: EthereumTransactionSubmitter; - - constructor({ network, signer, ipfsStorage, logger, gasPriceMin }: StorageProps) { + private readonly blockConfirmations: number | undefined; + + constructor({ + network, + signer, + ipfsStorage, + logger, + gasPriceMin, + blockConfirmations, + }: StorageProps) { this.logger = logger || new SimpleLogger(); this.ipfsStorage = ipfsStorage; this.network = network; this.txSubmitter = new EthereumTransactionSubmitter({ network, signer, logger, gasPriceMin }); + this.blockConfirmations = blockConfirmations; } async initialize(): Promise { @@ -75,7 +86,7 @@ export class EthereumStorageEthers implements StorageTypes.IStorageWrite { this.logger.debug(`TX ${tx.hash} submitted, waiting for confirmation...`); void tx - .wait() + .wait(this.blockConfirmations || getDefaultEthereumBlockConfirmations()) .then((receipt: providers.TransactionReceipt) => { this.logger.debug( `TX ${receipt.transactionHash} confirmed at block ${receipt.blockNumber}`, diff --git a/packages/payment-processor/test/payment/encoder-approval.test.ts b/packages/payment-processor/test/payment/encoder-approval.test.ts index 4e351bd4f8..e18aa80bbe 100644 --- a/packages/payment-processor/test/payment/encoder-approval.test.ts +++ b/packages/payment-processor/test/payment/encoder-approval.test.ts @@ -18,6 +18,8 @@ import { IConversionSettings } from '../../src/payment/settings'; /* eslint-disable @typescript-eslint/no-unused-expressions */ /* eslint-disable @typescript-eslint/await-thenable */ +// eslint-disable-next-line no-magic-numbers +jest.setTimeout(10000); const erc20ContractAddress = '0x9FBDa871d559710256a2502A2517b794B482Db40'; const feeAddress = '0xC5fdf4076b8F3A5357c5E395ab970B5B54098Fef'; diff --git a/packages/request-node/README.md b/packages/request-node/README.md index 2aad895d21..549382f6e7 100644 --- a/packages/request-node/README.md +++ b/packages/request-node/README.md @@ -243,6 +243,9 @@ Default values correspond to the basic configuration used to run a server in a t - `--headers` Custom headers to send with the API responses (as a stringified JSON object) - Default value: `'{}'` - Environment variable name: `$HEADERS` + `--blockConfirmations` The number of block confirmations to consider a transaction successful + - Default value: `2` + - Environment variable name: `$BLOCK_CONFIRMATIONS` - `--lastBlockNumberDelay` The minimum delay between getLastBlockNumber calls to ethereum network - Default value: `'10000'` - Environment variable name: `$LAST_BLOCK_NUMBER_DELAY` @@ -307,6 +310,9 @@ cd requestNetwork #### 2. Install and build all the dependencies. +Install IPFS Kubo (go-ipfs) +https://docs.ipfs.tech/install/command-line/#install-ipfs-kubo + ```bash yarn install yarn build diff --git a/packages/request-node/src/config.ts b/packages/request-node/src/config.ts index 75769c1e09..fcd6f9c3e1 100644 --- a/packages/request-node/src/config.ts +++ b/packages/request-node/src/config.ts @@ -19,6 +19,7 @@ const defaultValues: any = { networkId: 0, web3ProviderUrl: 'http://localhost:8545', gasPriceMin: '1000000000', // one gwei + blockConfirmations: 2, }, ipfs: { host: 'localhost', @@ -119,6 +120,18 @@ export function getGasPriceMin(): BigNumber | undefined { return gasPriceMin && BigNumber.from(gasPriceMin); } +/** + * Get the number of block confirmations to wait before considering a transaction successful + * @returns the number of block confirmations + */ +export function getBlockConfirmations(): number { + return ( + (argv.blockConfirmations && Number(argv.blockConfirmations)) || + (process.env.BLOCK_CONFIRMATIONS && Number(process.env.BLOCK_CONFIRMATIONS)) || + defaultValues.ethereumStorage.ethereum.blockConfirmations + ); +} + /** * Get host from command line argument, environment variables or default values to connect to IPFS gateway * @returns the host of the IPFS gateway diff --git a/packages/request-node/src/request/getStatus.ts b/packages/request-node/src/request/getStatus.ts index 61c47c359c..3fe5074bea 100644 --- a/packages/request-node/src/request/getStatus.ts +++ b/packages/request-node/src/request/getStatus.ts @@ -59,6 +59,7 @@ export default class GetStatusHandler { networkId: config.getStorageNetworkId(), providerUrl, retryDelay: config.getEthereumRetryDelay(), + blockConfirmations: config.getBlockConfirmations(), }, ipfs: { host: config.getIpfsHost(), diff --git a/packages/request-node/src/server.ts b/packages/request-node/src/server.ts index 31741158e2..88b3cd0157 100755 --- a/packages/request-node/src/server.ts +++ b/packages/request-node/src/server.ts @@ -26,6 +26,7 @@ const startNode = async (): Promise => { IPFS timeout: ${config.getIpfsTimeout()} Storage concurrency: ${config.getStorageConcurrency()} Initialization storage path: ${config.getInitializationStorageFilePath()} + Storage block confirmations: ${config.getBlockConfirmations()} `; logger.info(serverMessage); diff --git a/packages/request-node/src/thegraph-node.ts b/packages/request-node/src/thegraph-node.ts index 6c73885178..55be709ce4 100644 --- a/packages/request-node/src/thegraph-node.ts +++ b/packages/request-node/src/thegraph-node.ts @@ -35,12 +35,14 @@ export class TheGraphRequestNode extends RequestNodeBase { const signer = new NonceManager(wallet); const ipfsStorage = getIpfsStorage(logger); const gasPriceMin = config.getGasPriceMin(); + const blockConfirmations = config.getBlockConfirmations(); const storage = new EthereumStorageEthers({ ipfsStorage, signer, network, logger, gasPriceMin, + blockConfirmations, }); const dataAccess = new TheGraphDataAccess({ graphql: { url },