dkg.js 8.2.0 → 8.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/constants.js +3 -0
- package/index.cjs +184 -44
- package/package.json +2 -1
- package/services/blockchain-service/blockchain-service-base.js +168 -45
- package/services/input-service.js +14 -0
package/constants/constants.js
CHANGED
|
@@ -219,6 +219,7 @@ export const DEFAULT_PARAMETERS = {
|
|
|
219
219
|
SIMULATE_TXS: false,
|
|
220
220
|
FORCE_REPLACE_TXS: false,
|
|
221
221
|
GAS_LIMIT_MULTIPLIER: 1,
|
|
222
|
+
RETRY_TX_GAS_PRICE_MULTIPLIER: 3,
|
|
222
223
|
};
|
|
223
224
|
|
|
224
225
|
export const DEFAULT_GAS_PRICE = {
|
|
@@ -236,3 +237,5 @@ export const PARANET_KNOWLEDGE_ASSET_ACCESS_POLICY = {
|
|
|
236
237
|
};
|
|
237
238
|
|
|
238
239
|
export const CHUNK_BYTE_SIZE = 32;
|
|
240
|
+
|
|
241
|
+
export const FEE_HISTORY_BLOCK_COUNT = 5;
|
package/index.cjs
CHANGED
|
@@ -273,6 +273,7 @@ const DEFAULT_PARAMETERS = {
|
|
|
273
273
|
SIMULATE_TXS: false,
|
|
274
274
|
FORCE_REPLACE_TXS: false,
|
|
275
275
|
GAS_LIMIT_MULTIPLIER: 1,
|
|
276
|
+
RETRY_TX_GAS_PRICE_MULTIPLIER: 3,
|
|
276
277
|
};
|
|
277
278
|
|
|
278
279
|
const DEFAULT_GAS_PRICE = {
|
|
@@ -283,6 +284,8 @@ const DEFAULT_GAS_PRICE = {
|
|
|
283
284
|
|
|
284
285
|
const CHUNK_BYTE_SIZE$1 = 32;
|
|
285
286
|
|
|
287
|
+
const FEE_HISTORY_BLOCK_COUNT = 5;
|
|
288
|
+
|
|
286
289
|
function nodeSupported() {
|
|
287
290
|
return typeof window === 'undefined';
|
|
288
291
|
}
|
|
@@ -3507,10 +3510,10 @@ class BlockchainServiceBase {
|
|
|
3507
3510
|
);
|
|
3508
3511
|
gasLimit = Math.round(gasLimit * blockchain.gasLimitMultiplier);
|
|
3509
3512
|
|
|
3510
|
-
let gasPrice;
|
|
3511
|
-
if (blockchain.previousTxGasPrice && blockchain.retryTx) {
|
|
3512
|
-
// Increase previous tx gas price by
|
|
3513
|
-
gasPrice = Math.round(blockchain.previousTxGasPrice *
|
|
3513
|
+
// let gasPrice;
|
|
3514
|
+
/*if (blockchain.previousTxGasPrice && blockchain.retryTx) {
|
|
3515
|
+
// Increase previous tx gas price by retryTxGasPriceMultiplier
|
|
3516
|
+
gasPrice = Math.round(blockchain.previousTxGasPrice * blockchain.retryTxGasPriceMultiplier);
|
|
3514
3517
|
} else if (blockchain.forceReplaceTxs) {
|
|
3515
3518
|
// Get the current transaction count (nonce) of the wallet, including pending transactions
|
|
3516
3519
|
const currentNonce = await web3Instance.eth.getTransactionCount(publicKey, 'pending');
|
|
@@ -3530,21 +3533,23 @@ class BlockchainServiceBase {
|
|
|
3530
3533
|
);
|
|
3531
3534
|
|
|
3532
3535
|
if (pendingTx) {
|
|
3533
|
-
// If found, increase gas price of pending tx by
|
|
3534
|
-
gasPrice = Math.round(Number(pendingTx.gasPrice) *
|
|
3536
|
+
// If found, increase gas price of pending tx by retryTxGasPriceMultiplier
|
|
3537
|
+
gasPrice = Math.round(Number(pendingTx.gasPrice) * blockchain.retryTxGasPriceMultiplier);
|
|
3535
3538
|
} else {
|
|
3536
|
-
// If not found, use default/network gas price increased by
|
|
3539
|
+
// If not found, use default/network gas price increased by retryTxGasPriceMultiplier
|
|
3537
3540
|
// Theoretically this should never happen
|
|
3538
3541
|
gasPrice = Math.round(
|
|
3539
|
-
(blockchain.gasPrice || (await this.
|
|
3542
|
+
(blockchain.gasPrice || (await this.getSmartGasPrice(blockchain))) * blockchain.retryTxGasPriceMultiplier,
|
|
3540
3543
|
);
|
|
3541
3544
|
}
|
|
3542
3545
|
} else {
|
|
3543
|
-
gasPrice = blockchain.gasPrice || (await this.
|
|
3546
|
+
gasPrice = blockchain.gasPrice || (await this.getSmartGasPrice(blockchain));
|
|
3544
3547
|
}
|
|
3545
3548
|
} else {
|
|
3546
|
-
gasPrice = blockchain.gasPrice || (await this.
|
|
3547
|
-
}
|
|
3549
|
+
gasPrice = blockchain.gasPrice || (await this.getSmartGasPrice(blockchain));
|
|
3550
|
+
}*/
|
|
3551
|
+
|
|
3552
|
+
const gasPrice = blockchain.gasPrice ?? (await this.getSmartGasPrice(blockchain));
|
|
3548
3553
|
|
|
3549
3554
|
if (blockchain.simulateTxs) {
|
|
3550
3555
|
await web3Instance.eth.call({
|
|
@@ -3639,7 +3644,13 @@ class BlockchainServiceBase {
|
|
|
3639
3644
|
}
|
|
3640
3645
|
}
|
|
3641
3646
|
|
|
3642
|
-
async waitForEventFinality(
|
|
3647
|
+
async waitForEventFinality(
|
|
3648
|
+
initialReceipt,
|
|
3649
|
+
eventName,
|
|
3650
|
+
expectedEventId,
|
|
3651
|
+
blockchain,
|
|
3652
|
+
confirmations = 1,
|
|
3653
|
+
) {
|
|
3643
3654
|
await this.ensureBlockchainInfo(blockchain);
|
|
3644
3655
|
const web3Instance = await this.getWeb3Instance(blockchain);
|
|
3645
3656
|
|
|
@@ -3652,7 +3663,10 @@ class BlockchainServiceBase {
|
|
|
3652
3663
|
// eslint-disable-next-line no-constant-condition
|
|
3653
3664
|
while (true) {
|
|
3654
3665
|
// 1. Wait until the block containing the tx is at the required depth
|
|
3655
|
-
while (
|
|
3666
|
+
while (
|
|
3667
|
+
(await web3Instance.eth.getBlockNumber()) <
|
|
3668
|
+
receipt.blockNumber + confirmations
|
|
3669
|
+
) {
|
|
3656
3670
|
await sleepForMilliseconds(polling);
|
|
3657
3671
|
}
|
|
3658
3672
|
|
|
@@ -3674,7 +3688,9 @@ class BlockchainServiceBase {
|
|
|
3674
3688
|
|
|
3675
3689
|
const idMatches =
|
|
3676
3690
|
expectedEventId == null ||
|
|
3677
|
-
(eventData &&
|
|
3691
|
+
(eventData &&
|
|
3692
|
+
eventData.id != null &&
|
|
3693
|
+
eventData.id.toString() === expectedEventId.toString());
|
|
3678
3694
|
|
|
3679
3695
|
if (eventData && idMatches) {
|
|
3680
3696
|
return { receipt: currentReceipt, eventData };
|
|
@@ -3754,39 +3770,49 @@ class BlockchainServiceBase {
|
|
|
3754
3770
|
);
|
|
3755
3771
|
}
|
|
3756
3772
|
|
|
3773
|
+
async needsMoreAllowance(sender, tokenAmount, blockchain, knowledgeCollectionAddress) {
|
|
3774
|
+
const allowance = await this.callContractFunction(
|
|
3775
|
+
'Token',
|
|
3776
|
+
'allowance',
|
|
3777
|
+
[sender, knowledgeCollectionAddress],
|
|
3778
|
+
blockchain,
|
|
3779
|
+
);
|
|
3780
|
+
|
|
3781
|
+
if (BigInt(allowance) < BigInt(tokenAmount)) return true;
|
|
3782
|
+
|
|
3783
|
+
return false;
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3786
|
+
async maxAllowancePerTransaction(sender, blockchain) {
|
|
3787
|
+
if (blockchain.maxAllowance) {
|
|
3788
|
+
return blockchain.maxAllowance;
|
|
3789
|
+
} else {
|
|
3790
|
+
return await this.callContractFunction('Token', 'balanceOf', [sender], blockchain);
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
|
|
3757
3794
|
async increaseKnowledgeCollectionAllowance(sender, tokenAmount, blockchain) {
|
|
3758
3795
|
const knowledgeCollectionAddress = await this.getContractAddress(
|
|
3759
3796
|
'KnowledgeCollection',
|
|
3760
3797
|
blockchain,
|
|
3761
3798
|
);
|
|
3762
3799
|
|
|
3763
|
-
const
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
[sender, knowledgeCollectionAddress],
|
|
3800
|
+
const needsMoreAllowance = await this.needsMoreAllowance(
|
|
3801
|
+
sender,
|
|
3802
|
+
tokenAmount,
|
|
3767
3803
|
blockchain,
|
|
3804
|
+
knowledgeCollectionAddress,
|
|
3768
3805
|
);
|
|
3769
3806
|
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
if (allowanceGap > 0) {
|
|
3807
|
+
if (needsMoreAllowance) {
|
|
3808
|
+
const allowanceThreshold = await this.maxAllowancePerTransaction(sender, blockchain);
|
|
3773
3809
|
await this.executeContractFunction(
|
|
3774
3810
|
'Token',
|
|
3775
|
-
'
|
|
3776
|
-
[knowledgeCollectionAddress,
|
|
3811
|
+
'approve',
|
|
3812
|
+
[knowledgeCollectionAddress, allowanceThreshold],
|
|
3777
3813
|
blockchain,
|
|
3778
3814
|
);
|
|
3779
|
-
|
|
3780
|
-
return {
|
|
3781
|
-
allowanceIncreased: true,
|
|
3782
|
-
allowanceGap,
|
|
3783
|
-
};
|
|
3784
3815
|
}
|
|
3785
|
-
|
|
3786
|
-
return {
|
|
3787
|
-
allowanceIncreased: false,
|
|
3788
|
-
allowanceGap,
|
|
3789
|
-
};
|
|
3790
3816
|
}
|
|
3791
3817
|
|
|
3792
3818
|
// Knowledge assets operations
|
|
@@ -3799,19 +3825,16 @@ class BlockchainServiceBase {
|
|
|
3799
3825
|
stepHooks = emptyHooks$1,
|
|
3800
3826
|
) {
|
|
3801
3827
|
const sender = await this.getPublicKey(blockchain);
|
|
3802
|
-
let allowanceIncreased = false;
|
|
3803
|
-
let allowanceGap = 0;
|
|
3804
3828
|
|
|
3805
3829
|
try {
|
|
3806
3830
|
if (requestData?.paymaster && requestData?.paymaster !== ZERO_ADDRESS) {
|
|
3807
3831
|
// Handle the case when payer is passed
|
|
3808
3832
|
} else {
|
|
3809
|
-
(
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
));
|
|
3833
|
+
await this.increaseKnowledgeCollectionAllowance(
|
|
3834
|
+
sender,
|
|
3835
|
+
requestData.tokenAmount,
|
|
3836
|
+
blockchain,
|
|
3837
|
+
);
|
|
3815
3838
|
}
|
|
3816
3839
|
|
|
3817
3840
|
stepHooks.afterHook({
|
|
@@ -3850,9 +3873,7 @@ class BlockchainServiceBase {
|
|
|
3850
3873
|
|
|
3851
3874
|
return { knowledgeCollectionId: id, receipt };
|
|
3852
3875
|
} catch (error) {
|
|
3853
|
-
|
|
3854
|
-
await this.decreaseKnowledgeCollectionAllowance(allowanceGap, blockchain);
|
|
3855
|
-
}
|
|
3876
|
+
console.error('createKnowledgeCollection failed:', error);
|
|
3856
3877
|
throw error;
|
|
3857
3878
|
}
|
|
3858
3879
|
}
|
|
@@ -4693,6 +4714,111 @@ class BlockchainServiceBase {
|
|
|
4693
4714
|
}
|
|
4694
4715
|
}
|
|
4695
4716
|
|
|
4717
|
+
/**
|
|
4718
|
+
* Get fee history from the last N blocks using eth_feeHistory RPC call
|
|
4719
|
+
* @param {Object} blockchain - Blockchain configuration
|
|
4720
|
+
* @param {number} blockCount - Number of blocks to fetch (default: 5)
|
|
4721
|
+
* @returns {Promise<Object>} Fee history data with baseFeePerGas and priorityFees arrays
|
|
4722
|
+
*/
|
|
4723
|
+
async getFeeHistory(blockchain, blockCount = 5) {
|
|
4724
|
+
await this.ensureBlockchainInfo(blockchain);
|
|
4725
|
+
const web3Instance = await this.getWeb3Instance(blockchain);
|
|
4726
|
+
|
|
4727
|
+
try {
|
|
4728
|
+
// eth_feeHistory params: blockCount, newestBlock, rewardPercentiles
|
|
4729
|
+
// [50] = median priority fee per block
|
|
4730
|
+
const feeHistory = await web3Instance.eth.getFeeHistory(blockCount, 'latest', [50]);
|
|
4731
|
+
|
|
4732
|
+
// Extract median priority fees from each block (reward[blockIndex][percentileIndex])
|
|
4733
|
+
const priorityFees = feeHistory.reward
|
|
4734
|
+
? feeHistory.reward.map((blockRewards) => BigInt(blockRewards[0] || 0))
|
|
4735
|
+
: [];
|
|
4736
|
+
|
|
4737
|
+
return {
|
|
4738
|
+
supported: true,
|
|
4739
|
+
oldestBlock: parseInt(feeHistory.oldestBlock, 16),
|
|
4740
|
+
baseFeePerGas: feeHistory.baseFeePerGas.map((bf) => BigInt(bf)),
|
|
4741
|
+
priorityFees,
|
|
4742
|
+
};
|
|
4743
|
+
} catch (error) {
|
|
4744
|
+
// eth_feeHistory not supported on this network
|
|
4745
|
+
return {
|
|
4746
|
+
supported: false,
|
|
4747
|
+
error: error.message,
|
|
4748
|
+
};
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4752
|
+
/**
|
|
4753
|
+
* Apply buffer percentage to a gas price
|
|
4754
|
+
* @param {BigInt} gasPrice - Gas price in wei
|
|
4755
|
+
* @param {number} gasPriceBufferPercent - Buffer percentage to add
|
|
4756
|
+
* @returns {BigInt} Gas price with buffer applied
|
|
4757
|
+
*/
|
|
4758
|
+
applyGasPriceBuffer(gasPrice, gasPriceBufferPercent) {
|
|
4759
|
+
if (!gasPriceBufferPercent) return gasPrice;
|
|
4760
|
+
return (gasPrice * BigInt(100 + Number(gasPriceBufferPercent))) / 100n;
|
|
4761
|
+
}
|
|
4762
|
+
|
|
4763
|
+
/**
|
|
4764
|
+
* Estimate safe gas price using eth_feeHistory (EIP-1559 style)
|
|
4765
|
+
* Takes max base fee from last N blocks, adds a buffer for volatility,
|
|
4766
|
+
* and includes the priority fee (tip) for validator incentive
|
|
4767
|
+
* @param {Object} blockchain - Blockchain configuration
|
|
4768
|
+
* @returns {Promise<BigInt>} Estimated gas price in wei
|
|
4769
|
+
*/
|
|
4770
|
+
async estimateGasPriceFromFeeHistory(blockchain) {
|
|
4771
|
+
const { gasPriceBufferPercent } = blockchain;
|
|
4772
|
+
const feeHistory = await this.getFeeHistory(blockchain, FEE_HISTORY_BLOCK_COUNT);
|
|
4773
|
+
|
|
4774
|
+
// Fallback to network gas price if feeHistory not supported or empty
|
|
4775
|
+
if (!feeHistory.supported) {
|
|
4776
|
+
return this.applyGasPriceBuffer(
|
|
4777
|
+
BigInt(await this.getNetworkGasPrice(blockchain)),
|
|
4778
|
+
gasPriceBufferPercent,
|
|
4779
|
+
);
|
|
4780
|
+
}
|
|
4781
|
+
|
|
4782
|
+
const baseFees = Array.from(feeHistory.baseFeePerGas);
|
|
4783
|
+
const priorityFees = Array.from(feeHistory.priorityFees);
|
|
4784
|
+
|
|
4785
|
+
if (baseFees.length === 0 || priorityFees.length === 0) {
|
|
4786
|
+
return this.applyGasPriceBuffer(
|
|
4787
|
+
BigInt(await this.getNetworkGasPrice(blockchain)),
|
|
4788
|
+
gasPriceBufferPercent,
|
|
4789
|
+
);
|
|
4790
|
+
}
|
|
4791
|
+
|
|
4792
|
+
// Find max base fee and priority fee from recent blocks
|
|
4793
|
+
const maxBaseFee = baseFees.reduce((max, bf) => (bf > max ? bf : max), 0n);
|
|
4794
|
+
const maxPriorityFee = priorityFees.reduce((max, pf) => (pf > max ? pf : max), 0n);
|
|
4795
|
+
|
|
4796
|
+
return this.applyGasPriceBuffer(maxBaseFee + maxPriorityFee, gasPriceBufferPercent);
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4799
|
+
/**
|
|
4800
|
+
* Get gas price with EIP-1559 estimation (with fallback)
|
|
4801
|
+
* Tries eth_feeHistory first, falls back to legacy methods
|
|
4802
|
+
* @param {Object} blockchain - Blockchain configuration
|
|
4803
|
+
* @returns {Promise<string>} Gas price in wei (as string for web3 compatibility)
|
|
4804
|
+
*/
|
|
4805
|
+
async getSmartGasPrice(blockchain) {
|
|
4806
|
+
try {
|
|
4807
|
+
const estimatedPrice = await this.estimateGasPriceFromFeeHistory(blockchain);
|
|
4808
|
+
return estimatedPrice.toString();
|
|
4809
|
+
} catch (eip1559Error) {
|
|
4810
|
+
try {
|
|
4811
|
+
return await this.getNetworkGasPrice(blockchain);
|
|
4812
|
+
} catch (fallbackError) {
|
|
4813
|
+
throw new Error(
|
|
4814
|
+
`All gas price estimation methods failed. ` +
|
|
4815
|
+
`EIP-1559: ${eip1559Error?.message || 'N/A'}. ` +
|
|
4816
|
+
`Fallback: ${fallbackError.message}`,
|
|
4817
|
+
);
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4696
4822
|
async getWalletBalances(blockchain) {
|
|
4697
4823
|
await this.ensureBlockchainInfo(blockchain);
|
|
4698
4824
|
const web3Instance = await this.getWeb3Instance(blockchain);
|
|
@@ -6105,6 +6231,17 @@ class InputService {
|
|
|
6105
6231
|
BLOCKCHAINS[environment][name]?.gasPriceOracleLink ??
|
|
6106
6232
|
undefined;
|
|
6107
6233
|
|
|
6234
|
+
const maxAllowance =
|
|
6235
|
+
options.blockchain?.maxAllowance ?? this.config.blockchain?.maxAllowance ?? undefined;
|
|
6236
|
+
const gasPriceBufferPercent =
|
|
6237
|
+
options.blockchain?.gasPriceBufferPercent ??
|
|
6238
|
+
this.config.blockchain?.gasPriceBufferPercent ??
|
|
6239
|
+
undefined;
|
|
6240
|
+
const retryTxGasPriceMultiplier =
|
|
6241
|
+
options.blockchain?.retryTxGasPriceMultiplier ??
|
|
6242
|
+
this.config.blockchain?.retryTxGasPriceMultiplier ??
|
|
6243
|
+
DEFAULT_PARAMETERS.RETRY_TX_GAS_PRICE_MULTIPLIER; // e.g., 1.2
|
|
6244
|
+
|
|
6108
6245
|
const blockchainConfig = {
|
|
6109
6246
|
name,
|
|
6110
6247
|
rpc,
|
|
@@ -6118,6 +6255,9 @@ class InputService {
|
|
|
6118
6255
|
simulateTxs,
|
|
6119
6256
|
forceReplaceTxs,
|
|
6120
6257
|
gasPriceOracleLink,
|
|
6258
|
+
maxAllowance,
|
|
6259
|
+
gasPriceBufferPercent,
|
|
6260
|
+
retryTxGasPriceMultiplier,
|
|
6121
6261
|
};
|
|
6122
6262
|
|
|
6123
6263
|
if (name && name.startsWith('otp')) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dkg.js",
|
|
3
|
-
"version": "8.2.
|
|
3
|
+
"version": "8.2.1",
|
|
4
4
|
"description": "Javascript library for interaction with the OriginTrail Decentralized Knowledge Graph",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"os-browserify": "^0.3.0",
|
|
64
64
|
"prettier": "^2.7.1",
|
|
65
65
|
"rollup": "^4.28.1",
|
|
66
|
+
"@rollup/rollup-linux-x64-gnu": "^4.53.1",
|
|
66
67
|
"stream-browserify": "^3.0.0",
|
|
67
68
|
"stream-http": "^3.2.0",
|
|
68
69
|
"terser-webpack-plugin": "^5.3.6",
|
|
@@ -7,9 +7,9 @@ import { createRequire } from 'module';
|
|
|
7
7
|
import {
|
|
8
8
|
OPERATIONS_STEP_STATUS,
|
|
9
9
|
DEFAULT_GAS_PRICE,
|
|
10
|
-
DEFAULT_GAS_PRICE_WEI,
|
|
11
10
|
ZERO_ADDRESS,
|
|
12
11
|
NEUROWEB_INCENTIVE_TYPE_CHAINS,
|
|
12
|
+
FEE_HISTORY_BLOCK_COUNT,
|
|
13
13
|
} from '../../constants/constants.js';
|
|
14
14
|
import emptyHooks from '../../util/empty-hooks.js';
|
|
15
15
|
import { sleepForMilliseconds } from '../utilities.js';
|
|
@@ -185,10 +185,10 @@ export default class BlockchainServiceBase {
|
|
|
185
185
|
);
|
|
186
186
|
gasLimit = Math.round(gasLimit * blockchain.gasLimitMultiplier);
|
|
187
187
|
|
|
188
|
-
let gasPrice;
|
|
189
|
-
if (blockchain.previousTxGasPrice && blockchain.retryTx) {
|
|
190
|
-
// Increase previous tx gas price by
|
|
191
|
-
gasPrice = Math.round(blockchain.previousTxGasPrice *
|
|
188
|
+
// let gasPrice;
|
|
189
|
+
/*if (blockchain.previousTxGasPrice && blockchain.retryTx) {
|
|
190
|
+
// Increase previous tx gas price by retryTxGasPriceMultiplier
|
|
191
|
+
gasPrice = Math.round(blockchain.previousTxGasPrice * blockchain.retryTxGasPriceMultiplier);
|
|
192
192
|
} else if (blockchain.forceReplaceTxs) {
|
|
193
193
|
// Get the current transaction count (nonce) of the wallet, including pending transactions
|
|
194
194
|
const currentNonce = await web3Instance.eth.getTransactionCount(publicKey, 'pending');
|
|
@@ -208,21 +208,23 @@ export default class BlockchainServiceBase {
|
|
|
208
208
|
);
|
|
209
209
|
|
|
210
210
|
if (pendingTx) {
|
|
211
|
-
// If found, increase gas price of pending tx by
|
|
212
|
-
gasPrice = Math.round(Number(pendingTx.gasPrice) *
|
|
211
|
+
// If found, increase gas price of pending tx by retryTxGasPriceMultiplier
|
|
212
|
+
gasPrice = Math.round(Number(pendingTx.gasPrice) * blockchain.retryTxGasPriceMultiplier);
|
|
213
213
|
} else {
|
|
214
|
-
// If not found, use default/network gas price increased by
|
|
214
|
+
// If not found, use default/network gas price increased by retryTxGasPriceMultiplier
|
|
215
215
|
// Theoretically this should never happen
|
|
216
216
|
gasPrice = Math.round(
|
|
217
|
-
(blockchain.gasPrice || (await this.
|
|
217
|
+
(blockchain.gasPrice || (await this.getSmartGasPrice(blockchain))) * blockchain.retryTxGasPriceMultiplier,
|
|
218
218
|
);
|
|
219
219
|
}
|
|
220
220
|
} else {
|
|
221
|
-
gasPrice = blockchain.gasPrice || (await this.
|
|
221
|
+
gasPrice = blockchain.gasPrice || (await this.getSmartGasPrice(blockchain));
|
|
222
222
|
}
|
|
223
223
|
} else {
|
|
224
|
-
gasPrice = blockchain.gasPrice || (await this.
|
|
225
|
-
}
|
|
224
|
+
gasPrice = blockchain.gasPrice || (await this.getSmartGasPrice(blockchain));
|
|
225
|
+
}*/
|
|
226
|
+
|
|
227
|
+
const gasPrice = blockchain.gasPrice ?? (await this.getSmartGasPrice(blockchain));
|
|
226
228
|
|
|
227
229
|
if (blockchain.simulateTxs) {
|
|
228
230
|
await web3Instance.eth.call({
|
|
@@ -317,7 +319,13 @@ export default class BlockchainServiceBase {
|
|
|
317
319
|
}
|
|
318
320
|
}
|
|
319
321
|
|
|
320
|
-
async waitForEventFinality(
|
|
322
|
+
async waitForEventFinality(
|
|
323
|
+
initialReceipt,
|
|
324
|
+
eventName,
|
|
325
|
+
expectedEventId,
|
|
326
|
+
blockchain,
|
|
327
|
+
confirmations = 1,
|
|
328
|
+
) {
|
|
321
329
|
await this.ensureBlockchainInfo(blockchain);
|
|
322
330
|
const web3Instance = await this.getWeb3Instance(blockchain);
|
|
323
331
|
|
|
@@ -330,7 +338,10 @@ export default class BlockchainServiceBase {
|
|
|
330
338
|
// eslint-disable-next-line no-constant-condition
|
|
331
339
|
while (true) {
|
|
332
340
|
// 1. Wait until the block containing the tx is at the required depth
|
|
333
|
-
while (
|
|
341
|
+
while (
|
|
342
|
+
(await web3Instance.eth.getBlockNumber()) <
|
|
343
|
+
receipt.blockNumber + confirmations
|
|
344
|
+
) {
|
|
334
345
|
await sleepForMilliseconds(polling);
|
|
335
346
|
}
|
|
336
347
|
|
|
@@ -352,7 +363,9 @@ export default class BlockchainServiceBase {
|
|
|
352
363
|
|
|
353
364
|
const idMatches =
|
|
354
365
|
expectedEventId == null ||
|
|
355
|
-
(eventData &&
|
|
366
|
+
(eventData &&
|
|
367
|
+
eventData.id != null &&
|
|
368
|
+
eventData.id.toString() === expectedEventId.toString());
|
|
356
369
|
|
|
357
370
|
if (eventData && idMatches) {
|
|
358
371
|
return { receipt: currentReceipt, eventData };
|
|
@@ -432,39 +445,49 @@ export default class BlockchainServiceBase {
|
|
|
432
445
|
);
|
|
433
446
|
}
|
|
434
447
|
|
|
448
|
+
async needsMoreAllowance(sender, tokenAmount, blockchain, knowledgeCollectionAddress) {
|
|
449
|
+
const allowance = await this.callContractFunction(
|
|
450
|
+
'Token',
|
|
451
|
+
'allowance',
|
|
452
|
+
[sender, knowledgeCollectionAddress],
|
|
453
|
+
blockchain,
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
if (BigInt(allowance) < BigInt(tokenAmount)) return true;
|
|
457
|
+
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
async maxAllowancePerTransaction(sender, blockchain) {
|
|
462
|
+
if (blockchain.maxAllowance) {
|
|
463
|
+
return blockchain.maxAllowance;
|
|
464
|
+
} else {
|
|
465
|
+
return await this.callContractFunction('Token', 'balanceOf', [sender], blockchain);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
435
469
|
async increaseKnowledgeCollectionAllowance(sender, tokenAmount, blockchain) {
|
|
436
470
|
const knowledgeCollectionAddress = await this.getContractAddress(
|
|
437
471
|
'KnowledgeCollection',
|
|
438
472
|
blockchain,
|
|
439
473
|
);
|
|
440
474
|
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
[sender, knowledgeCollectionAddress],
|
|
475
|
+
const needsMoreAllowance = await this.needsMoreAllowance(
|
|
476
|
+
sender,
|
|
477
|
+
tokenAmount,
|
|
445
478
|
blockchain,
|
|
479
|
+
knowledgeCollectionAddress,
|
|
446
480
|
);
|
|
447
481
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
if (allowanceGap > 0) {
|
|
482
|
+
if (needsMoreAllowance) {
|
|
483
|
+
const allowanceThreshold = await this.maxAllowancePerTransaction(sender, blockchain);
|
|
451
484
|
await this.executeContractFunction(
|
|
452
485
|
'Token',
|
|
453
|
-
'
|
|
454
|
-
[knowledgeCollectionAddress,
|
|
486
|
+
'approve',
|
|
487
|
+
[knowledgeCollectionAddress, allowanceThreshold],
|
|
455
488
|
blockchain,
|
|
456
489
|
);
|
|
457
|
-
|
|
458
|
-
return {
|
|
459
|
-
allowanceIncreased: true,
|
|
460
|
-
allowanceGap,
|
|
461
|
-
};
|
|
462
490
|
}
|
|
463
|
-
|
|
464
|
-
return {
|
|
465
|
-
allowanceIncreased: false,
|
|
466
|
-
allowanceGap,
|
|
467
|
-
};
|
|
468
491
|
}
|
|
469
492
|
|
|
470
493
|
// Knowledge assets operations
|
|
@@ -477,19 +500,16 @@ export default class BlockchainServiceBase {
|
|
|
477
500
|
stepHooks = emptyHooks,
|
|
478
501
|
) {
|
|
479
502
|
const sender = await this.getPublicKey(blockchain);
|
|
480
|
-
let allowanceIncreased = false;
|
|
481
|
-
let allowanceGap = 0;
|
|
482
503
|
|
|
483
504
|
try {
|
|
484
505
|
if (requestData?.paymaster && requestData?.paymaster !== ZERO_ADDRESS) {
|
|
485
506
|
// Handle the case when payer is passed
|
|
486
507
|
} else {
|
|
487
|
-
(
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
));
|
|
508
|
+
await this.increaseKnowledgeCollectionAllowance(
|
|
509
|
+
sender,
|
|
510
|
+
requestData.tokenAmount,
|
|
511
|
+
blockchain,
|
|
512
|
+
);
|
|
493
513
|
}
|
|
494
514
|
|
|
495
515
|
stepHooks.afterHook({
|
|
@@ -528,9 +548,7 @@ export default class BlockchainServiceBase {
|
|
|
528
548
|
|
|
529
549
|
return { knowledgeCollectionId: id, receipt };
|
|
530
550
|
} catch (error) {
|
|
531
|
-
|
|
532
|
-
await this.decreaseKnowledgeCollectionAllowance(allowanceGap, blockchain);
|
|
533
|
-
}
|
|
551
|
+
console.error('createKnowledgeCollection failed:', error);
|
|
534
552
|
throw error;
|
|
535
553
|
}
|
|
536
554
|
}
|
|
@@ -1371,6 +1389,111 @@ export default class BlockchainServiceBase {
|
|
|
1371
1389
|
}
|
|
1372
1390
|
}
|
|
1373
1391
|
|
|
1392
|
+
/**
|
|
1393
|
+
* Get fee history from the last N blocks using eth_feeHistory RPC call
|
|
1394
|
+
* @param {Object} blockchain - Blockchain configuration
|
|
1395
|
+
* @param {number} blockCount - Number of blocks to fetch (default: 5)
|
|
1396
|
+
* @returns {Promise<Object>} Fee history data with baseFeePerGas and priorityFees arrays
|
|
1397
|
+
*/
|
|
1398
|
+
async getFeeHistory(blockchain, blockCount = 5) {
|
|
1399
|
+
await this.ensureBlockchainInfo(blockchain);
|
|
1400
|
+
const web3Instance = await this.getWeb3Instance(blockchain);
|
|
1401
|
+
|
|
1402
|
+
try {
|
|
1403
|
+
// eth_feeHistory params: blockCount, newestBlock, rewardPercentiles
|
|
1404
|
+
// [50] = median priority fee per block
|
|
1405
|
+
const feeHistory = await web3Instance.eth.getFeeHistory(blockCount, 'latest', [50]);
|
|
1406
|
+
|
|
1407
|
+
// Extract median priority fees from each block (reward[blockIndex][percentileIndex])
|
|
1408
|
+
const priorityFees = feeHistory.reward
|
|
1409
|
+
? feeHistory.reward.map((blockRewards) => BigInt(blockRewards[0] || 0))
|
|
1410
|
+
: [];
|
|
1411
|
+
|
|
1412
|
+
return {
|
|
1413
|
+
supported: true,
|
|
1414
|
+
oldestBlock: parseInt(feeHistory.oldestBlock, 16),
|
|
1415
|
+
baseFeePerGas: feeHistory.baseFeePerGas.map((bf) => BigInt(bf)),
|
|
1416
|
+
priorityFees,
|
|
1417
|
+
};
|
|
1418
|
+
} catch (error) {
|
|
1419
|
+
// eth_feeHistory not supported on this network
|
|
1420
|
+
return {
|
|
1421
|
+
supported: false,
|
|
1422
|
+
error: error.message,
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* Apply buffer percentage to a gas price
|
|
1429
|
+
* @param {BigInt} gasPrice - Gas price in wei
|
|
1430
|
+
* @param {number} gasPriceBufferPercent - Buffer percentage to add
|
|
1431
|
+
* @returns {BigInt} Gas price with buffer applied
|
|
1432
|
+
*/
|
|
1433
|
+
applyGasPriceBuffer(gasPrice, gasPriceBufferPercent) {
|
|
1434
|
+
if (!gasPriceBufferPercent) return gasPrice;
|
|
1435
|
+
return (gasPrice * BigInt(100 + Number(gasPriceBufferPercent))) / 100n;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
/**
|
|
1439
|
+
* Estimate safe gas price using eth_feeHistory (EIP-1559 style)
|
|
1440
|
+
* Takes max base fee from last N blocks, adds a buffer for volatility,
|
|
1441
|
+
* and includes the priority fee (tip) for validator incentive
|
|
1442
|
+
* @param {Object} blockchain - Blockchain configuration
|
|
1443
|
+
* @returns {Promise<BigInt>} Estimated gas price in wei
|
|
1444
|
+
*/
|
|
1445
|
+
async estimateGasPriceFromFeeHistory(blockchain) {
|
|
1446
|
+
const { gasPriceBufferPercent } = blockchain;
|
|
1447
|
+
const feeHistory = await this.getFeeHistory(blockchain, FEE_HISTORY_BLOCK_COUNT);
|
|
1448
|
+
|
|
1449
|
+
// Fallback to network gas price if feeHistory not supported or empty
|
|
1450
|
+
if (!feeHistory.supported) {
|
|
1451
|
+
return this.applyGasPriceBuffer(
|
|
1452
|
+
BigInt(await this.getNetworkGasPrice(blockchain)),
|
|
1453
|
+
gasPriceBufferPercent,
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
const baseFees = Array.from(feeHistory.baseFeePerGas);
|
|
1458
|
+
const priorityFees = Array.from(feeHistory.priorityFees);
|
|
1459
|
+
|
|
1460
|
+
if (baseFees.length === 0 || priorityFees.length === 0) {
|
|
1461
|
+
return this.applyGasPriceBuffer(
|
|
1462
|
+
BigInt(await this.getNetworkGasPrice(blockchain)),
|
|
1463
|
+
gasPriceBufferPercent,
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// Find max base fee and priority fee from recent blocks
|
|
1468
|
+
const maxBaseFee = baseFees.reduce((max, bf) => (bf > max ? bf : max), 0n);
|
|
1469
|
+
const maxPriorityFee = priorityFees.reduce((max, pf) => (pf > max ? pf : max), 0n);
|
|
1470
|
+
|
|
1471
|
+
return this.applyGasPriceBuffer(maxBaseFee + maxPriorityFee, gasPriceBufferPercent);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
/**
|
|
1475
|
+
* Get gas price with EIP-1559 estimation (with fallback)
|
|
1476
|
+
* Tries eth_feeHistory first, falls back to legacy methods
|
|
1477
|
+
* @param {Object} blockchain - Blockchain configuration
|
|
1478
|
+
* @returns {Promise<string>} Gas price in wei (as string for web3 compatibility)
|
|
1479
|
+
*/
|
|
1480
|
+
async getSmartGasPrice(blockchain) {
|
|
1481
|
+
try {
|
|
1482
|
+
const estimatedPrice = await this.estimateGasPriceFromFeeHistory(blockchain);
|
|
1483
|
+
return estimatedPrice.toString();
|
|
1484
|
+
} catch (eip1559Error) {
|
|
1485
|
+
try {
|
|
1486
|
+
return await this.getNetworkGasPrice(blockchain);
|
|
1487
|
+
} catch (fallbackError) {
|
|
1488
|
+
throw new Error(
|
|
1489
|
+
`All gas price estimation methods failed. ` +
|
|
1490
|
+
`EIP-1559: ${eip1559Error?.message || 'N/A'}. ` +
|
|
1491
|
+
`Fallback: ${fallbackError.message}`,
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1374
1497
|
async getWalletBalances(blockchain) {
|
|
1375
1498
|
await this.ensureBlockchainInfo(blockchain);
|
|
1376
1499
|
const web3Instance = await this.getWeb3Instance(blockchain);
|
|
@@ -192,6 +192,17 @@ export default class InputService {
|
|
|
192
192
|
BLOCKCHAINS[environment][name]?.gasPriceOracleLink ??
|
|
193
193
|
undefined;
|
|
194
194
|
|
|
195
|
+
const maxAllowance =
|
|
196
|
+
options.blockchain?.maxAllowance ?? this.config.blockchain?.maxAllowance ?? undefined;
|
|
197
|
+
const gasPriceBufferPercent =
|
|
198
|
+
options.blockchain?.gasPriceBufferPercent ??
|
|
199
|
+
this.config.blockchain?.gasPriceBufferPercent ??
|
|
200
|
+
undefined;
|
|
201
|
+
const retryTxGasPriceMultiplier =
|
|
202
|
+
options.blockchain?.retryTxGasPriceMultiplier ??
|
|
203
|
+
this.config.blockchain?.retryTxGasPriceMultiplier ??
|
|
204
|
+
DEFAULT_PARAMETERS.RETRY_TX_GAS_PRICE_MULTIPLIER; // e.g., 1.2
|
|
205
|
+
|
|
195
206
|
const blockchainConfig = {
|
|
196
207
|
name,
|
|
197
208
|
rpc,
|
|
@@ -205,6 +216,9 @@ export default class InputService {
|
|
|
205
216
|
simulateTxs,
|
|
206
217
|
forceReplaceTxs,
|
|
207
218
|
gasPriceOracleLink,
|
|
219
|
+
maxAllowance,
|
|
220
|
+
gasPriceBufferPercent,
|
|
221
|
+
retryTxGasPriceMultiplier,
|
|
208
222
|
};
|
|
209
223
|
|
|
210
224
|
if (name && name.startsWith('otp')) {
|