From 7de3b903b112d90b92bf9bc0df963d9bc29fea87 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 23 Jun 2022 10:38:26 +0900 Subject: [PATCH] Supports dynamic base fee in the common architecture features --- packages/caver-core-method/src/index.js | 55 +- .../transactionTypes/abstractTransaction.js | 42 +- .../accountUpdate/accountUpdate.js | 2 +- .../feeDelegatedAccountUpdate.js | 2 +- .../feeDelegatedAccountUpdateWithRatio.js | 2 +- .../src/transactionTypes/cancel/cancel.js | 2 +- .../cancel/feeDelegatedCancel.js | 2 +- .../cancel/feeDelegatedCancelWithRatio.js | 2 +- .../chainDataAnchoring/chainDataAnchoring.js | 2 +- .../feeDelegatedChainDataAnchoring.js | 2 +- ...feeDelegatedChainDataAnchoringWithRatio.js | 2 +- .../ethereumAccessList.js | 2 +- .../ethereumDynamicFee.js | 10 +- .../legacyTransaction/legacyTransaction.js | 2 +- .../feeDelegatedSmartContractDeploy.js | 2 +- ...eeDelegatedSmartContractDeployWithRatio.js | 2 +- .../smartContractDeploy.js | 2 +- .../feeDelegatedSmartContractExecution.js | 2 +- ...elegatedSmartContractExecutionWithRatio.js | 2 +- .../smartContractExecution.js | 2 +- .../feeDelegatedValueTransfer.js | 2 +- .../feeDelegatedValueTransferWithRatio.js | 2 +- .../valueTransfer/valueTransfer.js | 2 +- .../feeDelegatedValueTransferMemo.js | 2 +- .../feeDelegatedValueTransferMemoWithRatio.js | 2 +- .../valueTransferMemo/valueTransferMemo.js | 2 +- test/dynamicBaseFee.js | 571 ++++++++++++++++++ 27 files changed, 680 insertions(+), 44 deletions(-) create mode 100644 test/dynamicBaseFee.js diff --git a/packages/caver-core-method/src/index.js b/packages/caver-core-method/src/index.js index 22d9838d7..533c162f5 100644 --- a/packages/caver-core-method/src/index.js +++ b/packages/caver-core-method/src/index.js @@ -449,7 +449,7 @@ const buildSendRequestFunc = (defer, sendSignedTx, sendTxCallback) => (payload, return method.requestManager.send(payload, sendTxCallback) } -const buildSendFunc = (method, isSendTx) => (...args) => { +const buildSendFunc = (method, isSendTx) => async (...args) => { const defer = utils.promiEvent(!isSendTx) const payload = method.toPayload(args) @@ -459,27 +459,66 @@ const buildSendFunc = (method, isSendTx) => (...args) => { const isGasPriceInputMissing = isSendTx && _.isObject(payload.params[0]) && payload.params[0].gasPrice === undefined - // If gasPrice input is missing, call getGasPrice rpc - if (!isGasPriceInputMissing) { + // The TxTypeEthereumDynamicFee transaction does not use the gasPrice field, + // so we need to check `maxPriorityFeePerGas` and `maxFeePerGas` field instead of `gasPrice`. + const isDynamicFeeTx = payload.params[0].type === TX_TYPE_STRING.TxTypeEthereumDynamicFee + const filledDynamicGasFeeTx = + isDynamicFeeTx && payload.params[0].maxPriorityFeePerGas !== undefined && payload.params[0].maxFeePerGas !== undefined + + // If gasPrice is missing, have to fill gasPrice field before sending tx + if (!isGasPriceInputMissing || filledDynamicGasFeeTx) { sendRequest(payload, method) return defer.eventEmitter } + const getHeader = new Method({ + name: 'getHeader', + call: 'klay_getHeaderByNumber', + params: 1, + }).createFunction(method.requestManager) + const getGasPrice = new Method({ name: 'getGasPrice', call: 'klay_gasPrice', params: 0, }).createFunction(method.requestManager) - getGasPrice((err, gasPrice) => { + const getMaxPriorityFeePerGas = new Method({ + name: 'getMaxPriorityFeePerGas', + call: 'klay_maxPriorityFeePerGas', + params: 0, + }).createFunction(method.requestManager) + + const header = await getHeader('latest') + const bf = utils.hexToNumber(header.baseFeePerGas || '0x0') + + // The baseFeePerGas is bigger than 0 means that Klaytn uses dynamic gas price. + if (bf > 0) { + if (!isDynamicFeeTx) { + payload.params[0].gasPrice = bf * 2 + } else { + // If maxFeePerGas is undefined, set maxFeePerGas with `baseFee * 2`. + payload.params[0].maxFeePerGas = payload.params[0].maxFeePerGas || bf * 2 + // If maxPriorityFeePerGas is undefined, call `klay_maxPriorityFeePerGas`. + if (payload.params[0].maxPriorityFeePerGas === undefined) { + const maxPriorityFeePerGas = await getMaxPriorityFeePerGas() + payload.params[0].maxPriorityFeePerGas = maxPriorityFeePerGas + } + } + } else { + // If baseFeePerGas is not defined or 0, we need to use unit price for the `gasPrice` field. + const gp = await getGasPrice() // The TxTypeEthereumDynamicFee transaction does not use the gasPrice field, // so the gas price default is not set for TxTypeEthereumDynamicFee. - if (payload.params[0].type !== TX_TYPE_STRING.TxTypeEthereumDynamicFee) { - payload.params[0].gasPrice = gasPrice || payload.params[0].gasPrice + if (!isDynamicFeeTx) { + payload.params[0].gasPrice = payload.params[0].gasPrice || gp + } else { + payload.params[0].maxPriorityFeePerGas = payload.params[0].maxPriorityFeePerGas || gp + payload.params[0].maxFeePerGas = payload.params[0].maxFeePerGas || gp } - sendRequest(payload, method) - }) + } + sendRequest(payload, method) /** * attaching `.on('receipt')` is possible by returning defer.eventEmitter */ diff --git a/packages/caver-transaction/src/transactionTypes/abstractTransaction.js b/packages/caver-transaction/src/transactionTypes/abstractTransaction.js index fa4c5c625..ec63bde7d 100644 --- a/packages/caver-transaction/src/transactionTypes/abstractTransaction.js +++ b/packages/caver-transaction/src/transactionTypes/abstractTransaction.js @@ -157,7 +157,7 @@ class AbstractTransaction { * Calls `klay_chainID` klay rpc call. * * @example - * const result = tx.getChainId() + * const result = await tx.getChainId() * * @return {string} chain id */ @@ -166,11 +166,40 @@ class AbstractTransaction { return chainId } + /** + * Returns a suggested gas price to use in the transaction. + * If `baseFee` is bigger than `0` in the header, + * then returns `baseFee * 2`. + * If not, calls `klay_gasPrice` to return unit price of the gas. + * + * @example + * const result = await tx.getSuggestedGasPrice() + * + * @return {string} gas price + */ + async getSuggestedGasPrice() { + const bfStr = await this.getBaseFee() + const baseFee = utils.hexToNumber(bfStr) + + let suggestedGasPrice + if (baseFee > 0) { + // After hard KIP-71 fork, set gasPrice (or maxFeePerGas) with baseFee * 2 + suggestedGasPrice = baseFee * 2 + suggestedGasPrice = utils.toHex(suggestedGasPrice) + } else { + // Before hard KIP-71 fork, set gasPrice (or maxFeePerGas) with gas unit price + suggestedGasPrice = await this.klaytnCall.getGasPrice() + } + return suggestedGasPrice + } + /** * Calls `klay_gasPrice` klay rpc call. + * Note that when Klaytn network use dynamic gas fee, + * you need to use `tx.getSuggestedGasPrice` function in the gasPrice field. * * @example - * const result = tx.getGasPrice() + * const result = await tx.getGasPrice() * * @return {string} gas price */ @@ -183,7 +212,7 @@ class AbstractTransaction { * Calls `klay_getTransactionCount` klay rpc call. * * @example - * const result = tx.getNonce('0x{from address}') + * const result = await tx.getNonce('0x{from address}') * * @return {string} nonce */ @@ -194,22 +223,23 @@ class AbstractTransaction { /** * Calls `klay_getHeaderByNumber` klay rpc call to get `baseFeePerGas` in header. + * If `baseFeePerGas` is not existed, returns '0x0'. * * @example - * const result = tx.getBaseFee() + * const result = await tx.getBaseFee() * * @return {string} base fee */ async getBaseFee() { const header = await this.klaytnCall.getHeaderByNumber('latest') - return header.baseFeePerGas + return header.baseFeePerGas || '0x0' } /** * Calls `klay_maxPriorityFeePerGas` klay rpc call. * * @example - * const result = tx.getMaxPriorityFeePerGas() + * const result = await tx.getMaxPriorityFeePerGas() * * @return {string} suggested max priority fee per gas */ diff --git a/packages/caver-transaction/src/transactionTypes/accountUpdate/accountUpdate.js b/packages/caver-transaction/src/transactionTypes/accountUpdate/accountUpdate.js index 91a267a24..4218e399f 100644 --- a/packages/caver-transaction/src/transactionTypes/accountUpdate/accountUpdate.js +++ b/packages/caver-transaction/src/transactionTypes/accountUpdate/accountUpdate.js @@ -180,7 +180,7 @@ class AccountUpdate extends AbstractTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/accountUpdate/feeDelegatedAccountUpdate.js b/packages/caver-transaction/src/transactionTypes/accountUpdate/feeDelegatedAccountUpdate.js index 747b8353b..d7aaadc2c 100644 --- a/packages/caver-transaction/src/transactionTypes/accountUpdate/feeDelegatedAccountUpdate.js +++ b/packages/caver-transaction/src/transactionTypes/accountUpdate/feeDelegatedAccountUpdate.js @@ -186,7 +186,7 @@ class FeeDelegatedAccountUpdate extends AbstractFeeDelegatedTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/accountUpdate/feeDelegatedAccountUpdateWithRatio.js b/packages/caver-transaction/src/transactionTypes/accountUpdate/feeDelegatedAccountUpdateWithRatio.js index b1087df23..b2e107999 100644 --- a/packages/caver-transaction/src/transactionTypes/accountUpdate/feeDelegatedAccountUpdateWithRatio.js +++ b/packages/caver-transaction/src/transactionTypes/accountUpdate/feeDelegatedAccountUpdateWithRatio.js @@ -190,7 +190,7 @@ class FeeDelegatedAccountUpdateWithRatio extends AbstractFeeDelegatedWithRatioTr async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/cancel/cancel.js b/packages/caver-transaction/src/transactionTypes/cancel/cancel.js index 539042b15..70c67a0cb 100644 --- a/packages/caver-transaction/src/transactionTypes/cancel/cancel.js +++ b/packages/caver-transaction/src/transactionTypes/cancel/cancel.js @@ -154,7 +154,7 @@ class Cancel extends AbstractTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/cancel/feeDelegatedCancel.js b/packages/caver-transaction/src/transactionTypes/cancel/feeDelegatedCancel.js index 06965d1ed..ad7fa5304 100644 --- a/packages/caver-transaction/src/transactionTypes/cancel/feeDelegatedCancel.js +++ b/packages/caver-transaction/src/transactionTypes/cancel/feeDelegatedCancel.js @@ -159,7 +159,7 @@ class FeeDelegatedCancel extends AbstractFeeDelegatedTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/cancel/feeDelegatedCancelWithRatio.js b/packages/caver-transaction/src/transactionTypes/cancel/feeDelegatedCancelWithRatio.js index 2b3350244..f17cade6e 100644 --- a/packages/caver-transaction/src/transactionTypes/cancel/feeDelegatedCancelWithRatio.js +++ b/packages/caver-transaction/src/transactionTypes/cancel/feeDelegatedCancelWithRatio.js @@ -164,7 +164,7 @@ class FeeDelegatedCancelWithRatio extends AbstractFeeDelegatedWithRatioTransacti async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/chainDataAnchoring.js b/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/chainDataAnchoring.js index 935aa03df..34f5d3c63 100644 --- a/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/chainDataAnchoring.js +++ b/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/chainDataAnchoring.js @@ -174,7 +174,7 @@ class ChainDataAnchoring extends AbstractTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/feeDelegatedChainDataAnchoring.js b/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/feeDelegatedChainDataAnchoring.js index 12c9ec474..79fd3b26e 100644 --- a/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/feeDelegatedChainDataAnchoring.js +++ b/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/feeDelegatedChainDataAnchoring.js @@ -181,7 +181,7 @@ class FeeDelegatedChainDataAnchoring extends AbstractFeeDelegatedTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/feeDelegatedChainDataAnchoringWithRatio.js b/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/feeDelegatedChainDataAnchoringWithRatio.js index aa646f561..9e29eadec 100644 --- a/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/feeDelegatedChainDataAnchoringWithRatio.js +++ b/packages/caver-transaction/src/transactionTypes/chainDataAnchoring/feeDelegatedChainDataAnchoringWithRatio.js @@ -184,7 +184,7 @@ class FeeDelegatedChainDataAnchoringWithRatio extends AbstractFeeDelegatedWithRa async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/ethereumTypedTransaction/ethereumAccessList.js b/packages/caver-transaction/src/transactionTypes/ethereumTypedTransaction/ethereumAccessList.js index 734e0dd24..81c31e387 100644 --- a/packages/caver-transaction/src/transactionTypes/ethereumTypedTransaction/ethereumAccessList.js +++ b/packages/caver-transaction/src/transactionTypes/ethereumTypedTransaction/ethereumAccessList.js @@ -373,7 +373,7 @@ class EthereumAccessList extends AbstractTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/ethereumTypedTransaction/ethereumDynamicFee.js b/packages/caver-transaction/src/transactionTypes/ethereumTypedTransaction/ethereumDynamicFee.js index 96ddb68aa..920f69d15 100644 --- a/packages/caver-transaction/src/transactionTypes/ethereumTypedTransaction/ethereumDynamicFee.js +++ b/packages/caver-transaction/src/transactionTypes/ethereumTypedTransaction/ethereumDynamicFee.js @@ -390,21 +390,17 @@ class EthereumDynamicFee extends AbstractTransaction { async fillTransaction() { const isNotMaxPriorityFeePerGas = isNot(this.maxPriorityFeePerGas) const isNotMaxFeePerGas = isNot(this.maxFeePerGas) - const [chainId, maxPriorityFeePerGas, nonce, baseFee] = await Promise.all([ + const [chainId, maxPriorityFeePerGas, nonce, maxFeePerGas] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, isNotMaxPriorityFeePerGas ? this.getMaxPriorityFeePerGas() : this.maxPriorityFeePerGas, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, - isNotMaxPriorityFeePerGas || isNotMaxFeePerGas ? this.getBaseFee() : undefined, + isNotMaxFeePerGas ? this.getSuggestedGasPrice() : this.maxFeePerGas, ]) this.chainId = chainId this.nonce = nonce this.maxPriorityFeePerGas = maxPriorityFeePerGas - - // Set maxFeePerGas with `block.baseFeePerGas.mul(2).add(maxPriorityFeePerGas)` - if (isNotMaxFeePerGas) { - this.maxFeePerGas = utils.hexToNumber(baseFee) * 2 + utils.hexToNumber(this.maxPriorityFeePerGas) - } + this.maxFeePerGas = maxFeePerGas } /** diff --git a/packages/caver-transaction/src/transactionTypes/legacyTransaction/legacyTransaction.js b/packages/caver-transaction/src/transactionTypes/legacyTransaction/legacyTransaction.js index beab96c4a..b15ba88df 100644 --- a/packages/caver-transaction/src/transactionTypes/legacyTransaction/legacyTransaction.js +++ b/packages/caver-transaction/src/transactionTypes/legacyTransaction/legacyTransaction.js @@ -268,7 +268,7 @@ class LegacyTransaction extends AbstractTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/smartContractDeploy/feeDelegatedSmartContractDeploy.js b/packages/caver-transaction/src/transactionTypes/smartContractDeploy/feeDelegatedSmartContractDeploy.js index fc522db9b..42140aa9b 100644 --- a/packages/caver-transaction/src/transactionTypes/smartContractDeploy/feeDelegatedSmartContractDeploy.js +++ b/packages/caver-transaction/src/transactionTypes/smartContractDeploy/feeDelegatedSmartContractDeploy.js @@ -260,7 +260,7 @@ class FeeDelegatedSmartContractDeploy extends AbstractFeeDelegatedTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/smartContractDeploy/feeDelegatedSmartContractDeployWithRatio.js b/packages/caver-transaction/src/transactionTypes/smartContractDeploy/feeDelegatedSmartContractDeployWithRatio.js index ec9d21331..6bcd90c6f 100644 --- a/packages/caver-transaction/src/transactionTypes/smartContractDeploy/feeDelegatedSmartContractDeployWithRatio.js +++ b/packages/caver-transaction/src/transactionTypes/smartContractDeploy/feeDelegatedSmartContractDeployWithRatio.js @@ -275,7 +275,7 @@ class FeeDelegatedSmartContractDeployWithRatio extends AbstractFeeDelegatedWithR async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/smartContractDeploy/smartContractDeploy.js b/packages/caver-transaction/src/transactionTypes/smartContractDeploy/smartContractDeploy.js index eb4d3b24b..9604f9292 100644 --- a/packages/caver-transaction/src/transactionTypes/smartContractDeploy/smartContractDeploy.js +++ b/packages/caver-transaction/src/transactionTypes/smartContractDeploy/smartContractDeploy.js @@ -254,7 +254,7 @@ class SmartContractDeploy extends AbstractTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/smartContractExecution/feeDelegatedSmartContractExecution.js b/packages/caver-transaction/src/transactionTypes/smartContractExecution/feeDelegatedSmartContractExecution.js index ee5e03e3a..fae3ca679 100644 --- a/packages/caver-transaction/src/transactionTypes/smartContractExecution/feeDelegatedSmartContractExecution.js +++ b/packages/caver-transaction/src/transactionTypes/smartContractExecution/feeDelegatedSmartContractExecution.js @@ -226,7 +226,7 @@ class FeeDelegatedSmartContractExecution extends AbstractFeeDelegatedTransaction async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/smartContractExecution/feeDelegatedSmartContractExecutionWithRatio.js b/packages/caver-transaction/src/transactionTypes/smartContractExecution/feeDelegatedSmartContractExecutionWithRatio.js index 85be8e4e3..172e94ec2 100644 --- a/packages/caver-transaction/src/transactionTypes/smartContractExecution/feeDelegatedSmartContractExecutionWithRatio.js +++ b/packages/caver-transaction/src/transactionTypes/smartContractExecution/feeDelegatedSmartContractExecutionWithRatio.js @@ -229,7 +229,7 @@ class FeeDelegatedSmartContractExecutionWithRatio extends AbstractFeeDelegatedWi async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/smartContractExecution/smartContractExecution.js b/packages/caver-transaction/src/transactionTypes/smartContractExecution/smartContractExecution.js index c87e59483..22b7d868b 100644 --- a/packages/caver-transaction/src/transactionTypes/smartContractExecution/smartContractExecution.js +++ b/packages/caver-transaction/src/transactionTypes/smartContractExecution/smartContractExecution.js @@ -219,7 +219,7 @@ class SmartContractExecution extends AbstractTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/valueTransfer/feeDelegatedValueTransfer.js b/packages/caver-transaction/src/transactionTypes/valueTransfer/feeDelegatedValueTransfer.js index aae479661..68aad344f 100644 --- a/packages/caver-transaction/src/transactionTypes/valueTransfer/feeDelegatedValueTransfer.js +++ b/packages/caver-transaction/src/transactionTypes/valueTransfer/feeDelegatedValueTransfer.js @@ -193,7 +193,7 @@ class FeeDelegatedValueTransfer extends AbstractFeeDelegatedTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/valueTransfer/feeDelegatedValueTransferWithRatio.js b/packages/caver-transaction/src/transactionTypes/valueTransfer/feeDelegatedValueTransferWithRatio.js index c49829163..8c590e87b 100644 --- a/packages/caver-transaction/src/transactionTypes/valueTransfer/feeDelegatedValueTransferWithRatio.js +++ b/packages/caver-transaction/src/transactionTypes/valueTransfer/feeDelegatedValueTransferWithRatio.js @@ -196,7 +196,7 @@ class FeeDelegatedValueTransferWithRatio extends AbstractFeeDelegatedWithRatioTr async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/valueTransfer/valueTransfer.js b/packages/caver-transaction/src/transactionTypes/valueTransfer/valueTransfer.js index b77ced0c0..2bbc54cf3 100644 --- a/packages/caver-transaction/src/transactionTypes/valueTransfer/valueTransfer.js +++ b/packages/caver-transaction/src/transactionTypes/valueTransfer/valueTransfer.js @@ -187,7 +187,7 @@ class ValueTransfer extends AbstractTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/valueTransferMemo/feeDelegatedValueTransferMemo.js b/packages/caver-transaction/src/transactionTypes/valueTransferMemo/feeDelegatedValueTransferMemo.js index f7cb31920..64a31b516 100644 --- a/packages/caver-transaction/src/transactionTypes/valueTransferMemo/feeDelegatedValueTransferMemo.js +++ b/packages/caver-transaction/src/transactionTypes/valueTransferMemo/feeDelegatedValueTransferMemo.js @@ -224,7 +224,7 @@ class FeeDelegatedValueTransferMemo extends AbstractFeeDelegatedTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/valueTransferMemo/feeDelegatedValueTransferMemoWithRatio.js b/packages/caver-transaction/src/transactionTypes/valueTransferMemo/feeDelegatedValueTransferMemoWithRatio.js index 09ea66ea9..53212cc8f 100644 --- a/packages/caver-transaction/src/transactionTypes/valueTransferMemo/feeDelegatedValueTransferMemoWithRatio.js +++ b/packages/caver-transaction/src/transactionTypes/valueTransferMemo/feeDelegatedValueTransferMemoWithRatio.js @@ -227,7 +227,7 @@ class FeeDelegatedValueTransferMemoWithRatio extends AbstractFeeDelegatedWithRat async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/packages/caver-transaction/src/transactionTypes/valueTransferMemo/valueTransferMemo.js b/packages/caver-transaction/src/transactionTypes/valueTransferMemo/valueTransferMemo.js index ea8d917a3..1a9db8952 100644 --- a/packages/caver-transaction/src/transactionTypes/valueTransferMemo/valueTransferMemo.js +++ b/packages/caver-transaction/src/transactionTypes/valueTransferMemo/valueTransferMemo.js @@ -217,7 +217,7 @@ class ValueTransferMemo extends AbstractTransaction { async fillTransaction() { const [chainId, gasPrice, nonce] = await Promise.all([ isNot(this.chainId) ? this.getChainId() : this.chainId, - isNot(this.gasPrice) ? this.getGasPrice() : this.gasPrice, + isNot(this.gasPrice) ? this.getSuggestedGasPrice() : this.gasPrice, isNot(this.nonce) ? this.getNonce(this.from) : this.nonce, ]) diff --git a/test/dynamicBaseFee.js b/test/dynamicBaseFee.js new file mode 100644 index 000000000..70dd7243d --- /dev/null +++ b/test/dynamicBaseFee.js @@ -0,0 +1,571 @@ +/* + Copyright 2022 The caver-js Authors + This file is part of the caver-js library. + + The caver-js library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The caver-js library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the caver-js. If not, see . +*/ + +const { expect } = require('chai') + +const Caver = require('../index') +const testRPCURL = require('./testrpc') + +const { kip7JsonInterface, kip7ByteCode } = require('../packages/caver-kct/src/kctHelper') + +const caver = new Caver(testRPCURL) + +let sender +let feePayer +let password + +before(() => { + const senderPrvKey = + process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 + ? `0x${process.env.privateKey}` + : process.env.privateKey + const feePayerPrvKey = + process.env.privateKey2 && String(process.env.privateKey2).indexOf('0x') === -1 + ? `0x${process.env.privateKey2}` + : process.env.privateKey2 + + // Add keyrings to `caver.wallet` (common architecture) + sender = caver.wallet.add(caver.wallet.keyring.createFromPrivateKey(senderPrvKey)) + feePayer = caver.wallet.add(caver.wallet.keyring.createFromPrivateKey(feePayerPrvKey)) + + // Add accounts to `caver.klay.accounts.wallet` (before common architecture) + caver.klay.accounts.wallet.add(senderPrvKey) + caver.klay.accounts.wallet.add(feePayerPrvKey) + + password = process.env.password ? process.env.password : 'password' +}) + +async function generateTxsBomb(num = 100) { + const txs = [] + const input = + '0x608060405234801561001057600080fd5b506101de806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a39d8ef81146100805780636353586b146100a757806370a08231146100ca578063fd6b7ef8146100f8575b3360009081526001602052604081208054349081019091558154019055005b34801561008c57600080fd5b5061009561010d565b60408051918252519081900360200190f35b6100c873ffffffffffffffffffffffffffffffffffffffff60043516610113565b005b3480156100d657600080fd5b5061009573ffffffffffffffffffffffffffffffffffffffff60043516610147565b34801561010457600080fd5b506100c8610159565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604081208054349081019091558154019055565b60016020526000908152604090205481565b336000908152600160205260408120805490829055908111156101af57604051339082156108fc029083906000818181858888f193505050501561019c576101af565b3360009081526001602052604090208190555b505600a165627a7a72305820627ca46bb09478a015762806cc00c431230501118c7c26c30ac58c4e09e51c4f0029' + + let senderNonce = caver.utils.hexToNumber(await caver.rpc.klay.getTransactionCount(sender.address)) + for (let i = 0; i < num; i++) { + const tx = caver.transaction.smartContractDeploy.create({ + from: sender.address, + input, + gas: 10000000, + nonce: senderNonce, + }) + await caver.wallet.sign(sender.address, tx) + txs.push(tx.getRLPEncoding()) + senderNonce++ + } + + await Promise.all( + Object.keys(txs).map(async signedTx => { + caver.rpc.klay.sendRawTransaction(signedTx) + }) + ) +} + +async function validateGasFeeWithReceipt(receipt) { + const gasPriceInReceipt = caver.utils.hexToNumber(receipt.gasPrice) + const gasPriceAtParentBlock = await caver.rpc.klay.getGasPriceAt(caver.utilsl.hexToNumber(receipt.blockNumber) - 1) // Klaytn will return baseFee + const gasPriceAtReceiptBlock = await caver.rpc.klay.getGasPriceAt(receipt.blockNumber) // Klaytn will return baseFee + + // To process a transaction, the gasPrice of the tx should be equal or bigger than baseFee(effectiveGasPrice) + if (caver.utils.hexToNumber(receipt.effectiveGasPrice) > gasPriceInReceipt) return false + + // effectiveGasPrice should be defined by baseFee used gas price when tx is processed + if (receipt.effectiveGasPrice !== gasPriceAtReceiptBlock) return false + + // Set gasPrice with `baseFee * 2` + if (caver.utils.hexToNumber(gasPriceAtParentBlock) * 2 !== gasPriceInReceipt) return false + return true +} + +async function validateDynamicFeeTxWithReceipt(tx, receipt) { + const maxFeePerGas = caver.utils.hexToNumber(receipt.maxFeePerGas) + const gasPriceAtParentBlock = await caver.rpc.klay.getGasPriceAt(caver.utilsl.hexToNumber(receipt.blockNumber) - 1) // Klaytn will return baseFee + + // To process a transaction, the maxFeePerGas of the tx should be equal or bigger than baseFee(effectiveGasPrice) + if (caver.utils.hexToNumber(receipt.effectiveGasPrice) > maxFeePerGas) return false + + // Set gasPrice with `baseFee * 2` + if (caver.utils.hexToNumber(gasPriceAtParentBlock) * 2 !== maxFeePerGas) return false + return true +} + +async function validateGasPrice(tx) { + // Klaytn will return baseFee + const gasPriceAtCurrentBlock = caver.utils.hexToNumber(await caver.rpc.klay.getGasPrice()) + + // If transaction type is TxTypeEthereumDynamicFee, + // validate `maxPriorityFeePerGas` and `maxFeePerGas`. + if (tx.type.incldues('DynamicFee')) { + const maxPriorityFeePerGas = await caver.rpc.klay.getMaxPriorityFeePerGas() + if (tx.maxPriorityFeePerGas !== maxPriorityFeePerGas) return false + // maxFeePerGas will be set with `baseFee * 2`, so maxFeePerGas cannnot be smaller than current base fee + if (caver.utils.hexToNumber(tx.maxFeePerGas) < gasPriceAtCurrentBlock) return false + return true + } + + // gasPrice will be set with `baseFee * 2`, so gasPrice cannnot be smaller than current base fee + if (caver.utils.hexToNumber(tx.gasPrice) < gasPriceAtCurrentBlock) return false + return true +} + +describe('Have to set correct value optional fields named gasPrice, maxFeePerGas or maxPriorityFeePerGas', () => { + it('CAVERJS-UNIT-ETC-405: caver.contract operates with optional gasPrice value', async () => { + // Generate many txs to increase baseFee + await generateTxsBomb() + + // Deploy a contract without optional gasPrice field + const contract = caver.contract.create(kip7JsonInterface) + const receipt = await contract.deploy( + { + from: sender.address, + gas: 50000000, + contractDeployFormatter: receipt => { + return receipt + }, + }, + kip7ByteCode, + 'Jamie', + 'JME', + 18, + '10000000000000000' + ) + expect(receipt).not.to.be.undefined + expect(receipt.status).to.equal(true) + expect(receipt.contractAddress).not.to.be.undefined + let isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + + // Generate many txs to increase baseFee + await generateTxsBomb() + + // Execute a contract without optional gasPrice field + contract.options.address = receipt.contractAddress + const minterAddedReceipt = await contract.send( + { from: sender.address, gas: 50000000 }, + 'addMinter', + caver.wallet.keyring.generate().address + ) + expect(minterAddedReceipt).not.to.be.undefined + expect(minterAddedReceipt.status).to.equal(true) + expect(minterAddedReceipt.to).to.equal(receipt.contractAddress) + isValid = await validateGasFeeWithReceipt(minterAddedReceipt) + expect(isValid).to.be.true + + // Sign a transaction to execute the smart contract without optional gasPrice field (basic tx) + let signedTx = await contract.sign({ from: sender.address, gas: 50000000 }, 'addMinter', caver.wallet.keyring.generate().address) + expect(signedTx.type).to.equal('TxTypeSmartContractExecution') + expect(caver.utils.isEmptySig(signedTx.signatures)).to.be.false + isValid = await validateGasPrice(signedTx) + expect(isValid).to.be.true + + // Sign a transaction to execute the smart contract without optional gasPrice field (fd tx) + signedTx = await contract.sign( + { from: sender.address, gas: 50000000, feeDelegation: true }, + 'addMinter', + caver.wallet.keyring.generate().address + ) + expect(signedTx.type).to.equal('TxTypeFeeDelegatedSmartContractExecution') + expect(caver.utils.isEmptySig(signedTx.signatures)).to.be.false + isValid = await validateGasPrice(signedTx) + expect(isValid).to.be.true + + // Sign a transaction to execute the smart contract without optional gasPrice field (fdr tx) + signedTx = await contract.sign( + { from: sender.address, gas: 50000000, feeDelegation: true, feeRatio: 30 }, + 'addMinter', + caver.wallet.keyring.generate().address + ) + expect(signedTx.type).to.equal('TxTypeFeeDelegatedSmartContractExecutionWithRatio') + expect(caver.utils.isEmptySig(signedTx.signatures)).to.be.false + isValid = await validateGasPrice(signedTx) + expect(isValid).to.be.true + + // Sign a transaction as a fee payer to execute the smart contract without optional gasPrice field (fd tx) + signedTx = await contract.signAsFeePayer( + { from: sender.address, gas: 50000000, feeDelegation: true, feePayer: feePayer.address }, + 'addMinter', + caver.wallet.keyring.generate().address + ) + expect(signedTx.type).to.equal('TxTypeFeeDelegatedSmartContractExecution') + expect(caver.utils.isEmptySig(signedTx.feePayerSignatures)).to.be.false + isValid = await validateGasPrice(signedTx) + expect(isValid).to.be.true + + // Sign a transaction as a fee payer to execute the smart contract without optional gasPrice field (fdr tx) + signedTx = await contract.signAsFeePayer( + { from: sender.address, gas: 50000000, feeDelegation: true, feePayer: feePayer.address, feeRatio: 30 }, + 'addMinter', + caver.wallet.keyring.generate().address + ) + expect(signedTx.type).to.equal('TxTypeFeeDelegatedSmartContractExecutionWithRatio') + expect(caver.utils.isEmptySig(signedTx.feePayerSignatures)).to.be.false + isValid = await validateGasPrice(signedTx) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-ETC-406: caver.klay.Contract operates with optional gasPrice value', async () => { + // Generate many txs to increase baseFee + await generateTxsBomb() + + // Deploy a contract without optional gasPrice field + const contract = new caver.klay.Contract(kip7JsonInterface) + const receipt = await contract + .deploy({ + data: kip7ByteCode, + arguments: ['Jamie', 'JME', 18, '10000000000000000'], + }) + .send({ + from: sender.address, + gas: 50000000, + contractDeployFormatter: receipt => { + return receipt + }, + }) + expect(receipt).not.to.be.undefined + expect(receipt.status).to.equal(true) + expect(receipt.contractAddress).not.to.be.undefined + let isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + + // Generate many txs to increase baseFee + await generateTxsBomb() + + // Execute a contract without optional gasPrice field + contract.options.address = receipt.contractAddress + const minterAddedReceipt = await contract.methods + .addMinter(caver.wallet.keyring.generate().address) + .send({ from: sender.address, gas: 50000000 }) + expect(minterAddedReceipt).not.to.be.undefined + expect(minterAddedReceipt.status).to.equal(true) + expect(minterAddedReceipt.to).to.equal(receipt.contractAddress) + isValid = await validateGasFeeWithReceipt(minterAddedReceipt) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-TRANSACTION-556: caver.transaction sign and signAsFeePayer signs with optional gasPrice value', async () => { + // Generate many txs to increase baseFee + await generateTxsBomb() + + // Test transaction.sign with basic tx + let tx = caver.transaction.valueTransfer.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + }) + await tx.sign(sender) + let isValid = await validateGasPrice(tx) + expect(isValid).to.be.true + let receipt = await caver.rpc.klay.sendRawTransaction(tx) + isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + + // Test transaction.signAsFeePayer with fee delegation tx + tx = caver.transaction.feeDelegatedValueTransfer.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + feePayer: feePayer.address, + }) + await tx.signAsFeePayer(feePayer) + await tx.sign(sender) + isValid = await validateGasPrice(tx) + expect(isValid).to.be.true + receipt = await caver.rpc.klay.sendRawTransaction(tx) + isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + + // Test transaction.sign with ethereum dynamic fee tx + tx = caver.transaction.ethereumDynamicFee.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 900000, + accessList: [], + }) + await tx.sign(sender) + isValid = await validateGasPrice(tx) + expect(isValid).to.be.true + receipt = await caver.rpc.klay.sendRawTransaction(tx) + isValid = await validateDynamicFeeTxWithReceipt(receipt) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-WALLET-431: caver.wallet sign and signAsFeePayer signs with optional gasPrice value', async () => { + // Generate many txs to increase baseFee + await generateTxsBomb() + + // Test caver.wallet.sign with basic tx + let tx = caver.transaction.valueTransfer.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + }) + await caver.wallet.sign(sender.address, tx) + let isValid = await validateGasPrice(tx) + expect(isValid).to.be.true + let receipt = await caver.rpc.klay.sendRawTransaction(tx) + isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + + // Test caver.wallet.signAsFeePayer with fee delegation tx + tx = caver.transaction.feeDelegatedValueTransfer.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + feePayer: feePayer.address, + }) + await caver.wallet.signAsFeePayer(feePayer.address, tx) + await caver.wallet.sign(sender.address, tx) + isValid = await validateGasPrice(tx) + expect(isValid).to.be.true + receipt = await caver.rpc.klay.sendRawTransaction(tx) + isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + + // Test caver.wallet.sign with ethereum dynamic fee tx + tx = caver.transaction.ethereumDynamicFee.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 900000, + accessList: [], + }) + await caver.wallet.sign(sender.address, tx) + isValid = await validateGasPrice(tx) + expect(isValid).to.be.true + receipt = await caver.rpc.klay.sendRawTransaction(tx) + isValid = await validateDynamicFeeTxWithReceipt(receipt) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-RPC-030: caver.rpc.klay.sendTransaction sends a tx with optional gasPrice value (use keystore in Klaytn Node)', async () => { + // Generate many txs to increase baseFee + await generateTxsBomb() + + const isUnlock = await caver.klay.personal.unlockAccount(sender.address, password) + expect(isUnlock).to.be.true + + let tx = caver.transaction.valueTransfer.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + }) + let receipt = await caver.rpc.klay.sendTransaction(tx) + let isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + + tx = caver.transaction.ethereumDynamicFee.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 900000, + accessList: [], + }) + receipt = await caver.rpc.klay.sendTransaction(tx) + isValid = await validateDynamicFeeTxWithReceipt(receipt) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-RPC-031: caver.rpc.klay.sendTransactionAsFeepayer sends a fee delegation tx with optional gasPrice value (use keystore in Klaytn Node)', async () => { + // Generate many txs to increase baseFee + await generateTxsBomb() + + const isUnlock = await caver.klay.personal.unlockAccount(feePayer.address, password) + expect(isUnlock).to.be.true + + const tx = caver.transaction.feeDelegatedValueTransfer.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + feePayer: feePayer.address, + }) + await caver.wallet.sign(sender, tx) + const receipt = await caver.rpc.klay.sendTransactionAsFeePayer(tx) + const isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-RPC-032: caver.rpc.klay.signTransaction signs a tx with optional gasPrice value (use keystore in Klaytn Node)', async () => { + const isUnlock = await caver.klay.personal.unlockAccount(sender.address, password) + expect(isUnlock).to.be.true + + let tx = caver.transaction.valueTransfer.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + }) + let signed = await caver.rpc.klay.signTransaction(tx) + let decodedTx = caver.transaction.decode(signed.raw) + let isValid = await validateGasPrice(decodedTx) + expect(isValid).to.be.true + + tx = caver.transaction.ethereumDynamicFee.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 900000, + accessList: [], + }) + signed = await caver.rpc.klay.signTransaction(tx) + decodedTx = caver.transaction.decode(signed.raw) + isValid = await validateGasPrice(decodedTx) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-RPC-033: caver.rpc.klay.signTransactionAsFeepayer signs a fee delegation tx with optional gasPrice value (use keystore in Klaytn Node)', async () => { + const isUnlock = await caver.klay.personal.unlockAccount(feePayer.address, password) + expect(isUnlock).to.be.true + + const tx = caver.transaction.feeDelegatedValueTransfer.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + feePayer: feePayer.address, + }) + const signed = await caver.rpc.klay.sendTransactionAsFeepayer(tx) + const decodedTx = caver.transaction.decode(signed.raw) + const isValid = await validateGasPrice(decodedTx) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-WALLET-432: caver.klay.accounts.signTransaction signs with optional gasPrice value', async () => { + const tx = { + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + } + const senderAccount = caver.klay.accounts.wallet[sender.address] + const signed = await caver.klay.accounts.signTransaction(tx, senderAccount.privateKey) + const decodedTx = caver.transaction.decode(signed.rawTransaction) + let isValid = await validateGasPrice(decodedTx) + expect(isValid).to.be.true + const receipt = await caver.rpc.klay.sendRawTransaction(signed.rawTransaction) + isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-WALLET-433: caver.klay.accounts.feePayerSignTransaction signs as fee payer with optional gasPrice value', async () => { + const tx = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: sender.address, + feePayer: feePayer.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + } + const feePayerAccount = caver.klay.accounts.wallet[feePayer.address] + const signed = await caver.klay.accounts.feePayerSignTransaction(tx, feePayerAccount.privateKey) + const decodedTx = caver.transaction.decode(signed.rawTransaction) + const isValid = await validateGasPrice(decodedTx) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-ETC-407: caver.klay.personal.sendTransaction sends a tx with optional gasPrice value', async () => { + // Generate many txs to increase baseFee + await generateTxsBomb() + + let tx = caver.transaction.valueTransfer.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + }) + let receipt = await caver.klay.personal.sendTransaction(tx, password) + let isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + + tx = caver.transaction.ethereumDynamicFee.create({ + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + accessList: [], + }) + receipt = await caver.klay.personal.sendTransaction(tx, password) + isValid = await validateDynamicFeeTxWithReceipt(receipt) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-ETC-408: caver.klay.personal.sendValueTransfer sends a value transfer tx with optional gasPrice value', async () => { + // Generate many txs to increase baseFee + await generateTxsBomb() + + const receipt = await caver.klay.personal.sendValueTransfer( + { + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + }, + password + ) + const isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-ETC-409: caver.klay.personal.sendAccountUpdate sends a value transfer tx with optional gasPrice value', async () => { + // Generate many txs to increase baseFee + await generateTxsBomb() + + const receipt = await caver.klay.personal.sendAccountUpdate( + { + from: sender.address, + gas: 2500000, + key: '0x01c0', + }, + password + ) + const isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + }).timeout(100000) + + it('CAVERJS-UNIT-RPC-034: caver.klay.sendTransaction sends a tx with optional gasPrice value', async () => { + // Sign a tx with an account in the in-memory wallet and send to network. + let tx = { + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + } + let receipt = await caver.klay.sendTransaction(tx) + let isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + + // Remove an account from in-memory wallet to send tx with keystore in the Node. + caver.klay.accounts.wallet.remove(sender.address) + const isUnlock = await caver.klay.personal.unlockAccount(sender.address, password) + expect(isUnlock).to.be.true + tx = { + from: sender.address, + to: caver.wallet.keyring.generate().address, + value: 1, + gas: 2500000, + } + receipt = await caver.klay.sendTransaction(tx) + isValid = await validateGasFeeWithReceipt(receipt) + expect(isValid).to.be.true + }).timeout(100000) +})