From 1c82a4ba65f7a3eb1ac972aef48bedced591cd54 Mon Sep 17 00:00:00 2001 From: Sergey Demyanov Date: Thu, 23 Mar 2023 12:35:49 -0700 Subject: [PATCH] Fixed transaction signature, use native autogas --- package.json | 2 +- src/gasProvider.ts | 202 --------------------------------------------- src/index.ts | 28 +++---- src/provider.ts | 21 ++--- 4 files changed, 23 insertions(+), 230 deletions(-) delete mode 100644 src/gasProvider.ts diff --git a/package.json b/package.json index 5a201c1..9c33f37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@conduitxyz/hardhat-gcp-kms-signer", - "version": "1.1.5", + "version": "1.1.6", "description": "Sign Hardhat deployment transactions using KMS key", "repository": "github:conduitxyz/hardhat-gcp-kms-signer", "author": "Conduit XYZ", diff --git a/src/gasProvider.ts b/src/gasProvider.ts deleted file mode 100644 index 34787a4..0000000 --- a/src/gasProvider.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { - numberToRpcQuantity, - rpcQuantityToBN, -} from "hardhat/internal/core/jsonrpc/types/base-types"; -import { BigNumber, BigNumberish } from "@ethersproject/bignumber"; -import { ProviderWrapper } from "hardhat/internal/core/providers/wrapper"; -import { EIP1193Provider, RequestArguments } from "hardhat/types"; -import { BN} from "ethereumjs-util"; - -export class AutomaticGasPriceProvider extends ProviderWrapper { - // We pay the max base fee that can be required if the next - // EIP1559_BASE_FEE_MAX_FULL_BLOCKS_PREFERENCE are full. - public static readonly EIP1559_BASE_FEE_MAX_FULL_BLOCKS_PREFERENCE: number = 3; - - // See eth_feeHistory for an explanation of what this means - public static readonly EIP1559_REWARD_PERCENTILE: number = 0.5; - - private _nodeHasFeeHistory?: boolean; - private _nodeSupportsEIP1559?: boolean; - private _minMaxFeePerGas?: BN; - private _minMaxPriorityFeePerGas?: BN; - - constructor( - provider: EIP1193Provider, - minMaxFeePerGas?: string | number, - minMaxPriorityFeePerGas?: string | number - ) { - super(provider); - this._minMaxFeePerGas = - minMaxFeePerGas !== undefined ? new BN(minMaxFeePerGas) : undefined; - this._minMaxPriorityFeePerGas = - minMaxPriorityFeePerGas !== undefined - ? new BN(minMaxPriorityFeePerGas) - : undefined; - } - - public async request(args: RequestArguments): Promise { - if (args.method !== "eth_sendTransaction") { - return this._wrappedProvider.request(args); - } - - const params = this._getParams(args); - - // TODO: Should we validate this type? - const tx = params[0]; - - if (tx === undefined) { - return this._wrappedProvider.request(args); - } - - // We don't need to do anything in these cases - if ( - tx.gasPrice !== undefined || - (tx.maxFeePerGas !== undefined && tx.maxPriorityFeePerGas !== undefined) - ) { - if ( - tx.maxFeePerGas !== undefined && - this._minMaxFeePerGas !== undefined - ) { - const maxFeePerGasBN = rpcQuantityToBN(tx.maxFeePerGas); - tx.maxFeePerGas = numberToRpcQuantity( - this._validateMinMaxGas(maxFeePerGasBN, this._minMaxFeePerGas) - ); - } - - if ( - tx.maxPriorityFeePerGas !== undefined && - this._minMaxPriorityFeePerGas !== undefined - ) { - const maxPriorityFeePerGasBN = rpcQuantityToBN(tx.maxPriorityFeePerGas); - tx.maxPriorityFeePerGas = numberToRpcQuantity( - this._validateMinMaxGas( - maxPriorityFeePerGasBN, - this._minMaxPriorityFeePerGas - ) - ); - } - - return this._wrappedProvider.request(args); - } - - let suggestedEip1559Values = await this._suggestEip1559FeePriceValues(); - - // eth_feeHistory failed, so we send a legacy one - if ( - tx.maxFeePerGas === undefined && - tx.maxPriorityFeePerGas === undefined && - suggestedEip1559Values === undefined - ) { - tx.gasPrice = numberToRpcQuantity(await this._getGasPrice()); - return this._wrappedProvider.request(args); - } - - // If eth_feeHistory failed, but the user still wants to send an EIP-1559 tx - // we use the gasPrice as default values. - if (suggestedEip1559Values === undefined) { - const gasPrice = await this._getGasPrice(); - - suggestedEip1559Values = { - maxFeePerGas: gasPrice, - maxPriorityFeePerGas: gasPrice, - }; - } - - let maxFeePerGas = - tx.maxFeePerGas !== undefined - ? rpcQuantityToBN(tx.maxFeePerGas) - : suggestedEip1559Values.maxFeePerGas; - - let maxPriorityFeePerGas = - tx.maxPriorityFeePerGas !== undefined - ? rpcQuantityToBN(tx.maxPriorityFeePerGas) - : suggestedEip1559Values.maxPriorityFeePerGas; - - if (this._minMaxFeePerGas) { - maxFeePerGas = this._validateMinMaxGas( - maxFeePerGas, - this._minMaxFeePerGas - ); - } - - if (this._minMaxPriorityFeePerGas) { - maxPriorityFeePerGas = this._validateMinMaxGas( - maxPriorityFeePerGas, - this._minMaxPriorityFeePerGas - ); - } - - if (maxFeePerGas < maxPriorityFeePerGas) { - maxFeePerGas = maxFeePerGas.add(maxPriorityFeePerGas); - } - - tx.maxFeePerGas = numberToRpcQuantity(maxFeePerGas); - tx.maxPriorityFeePerGas = numberToRpcQuantity(maxPriorityFeePerGas); - - return this._wrappedProvider.request(args); - } - - private _validateMinMaxGas = (initialMaxGas: BN, minMaxGas: BN) => { - return initialMaxGas < minMaxGas ? minMaxGas : initialMaxGas; - }; - - private async _getGasPrice(): Promise { - const response = (await this._wrappedProvider.request({ - method: "eth_gasPrice", - })) as string; - - return rpcQuantityToBN(response); - } - - private async _suggestEip1559FeePriceValues(): Promise< - | { - maxFeePerGas: BN; - maxPriorityFeePerGas: BN; - } - | undefined - > { - if (this._nodeSupportsEIP1559 === undefined) { - const block = (await this._wrappedProvider.request({ - method: "eth_getBlockByNumber", - params: ["latest", false], - })) as any; - this._nodeSupportsEIP1559 = block.baseFeePerGas !== undefined; - } - - if ( - this._nodeHasFeeHistory === false || - this._nodeSupportsEIP1559 === false - ) { - return; - } - - try { - const response = (await this._wrappedProvider.request({ - method: "eth_feeHistory", - params: [ - "0x1", - "latest", - [AutomaticGasPriceProvider.EIP1559_REWARD_PERCENTILE], - ], - })) as { baseFeePerGas: string[]; reward: string[][] }; - - return { - // Each block increases the base fee by 1/8 at most, when full. - // We have the next block's base fee, so we compute a cap for the - // next N blocks here. - maxFeePerGas: rpcQuantityToBN(response.baseFeePerGas[1]).mul( - new BN(9).pow(new BN(AutomaticGasPriceProvider.EIP1559_BASE_FEE_MAX_FULL_BLOCKS_PREFERENCE - 1)) - ).div ( - new BN(8).pow(new BN(AutomaticGasPriceProvider.EIP1559_BASE_FEE_MAX_FULL_BLOCKS_PREFERENCE -1)) - ).add( - rpcQuantityToBN(response.reward[0][0]) - ), - maxPriorityFeePerGas: rpcQuantityToBN(response.reward[0][0]), - }; - } catch (_error) { - this._nodeHasFeeHistory = false; - - return undefined; - } - } -} diff --git a/src/index.ts b/src/index.ts index ffe7d8a..03b44c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import { extendConfig, extendEnvironment } from "hardhat/config"; import { BackwardsCompatibilityProviderAdapter } from "hardhat/internal/core/providers/backwards-compatibility"; -import { AutomaticGasProvider } from "hardhat/internal/core/providers/gas-providers"; +import { AutomaticGasPriceProvider, AutomaticGasProvider, GanacheGasMultiplierProvider } from "hardhat/internal/core/providers/gas-providers"; import { HttpProvider } from "hardhat/internal/core/providers/http"; import { EIP1193Provider, @@ -9,7 +9,6 @@ import { HttpNetworkUserConfig, } from "hardhat/types"; -import { AutomaticGasPriceProvider } from "./gasProvider"; import { KMSSigner } from "./provider"; import "./type-extensions"; @@ -40,22 +39,23 @@ extendEnvironment((hre) => { httpNetConfig.httpHeaders, httpNetConfig.timeout ); - let wrappedProvider: EIP1193Provider; + let wrappedProvider: EIP1193Provider = eip1193Provider; + wrappedProvider = new KMSSigner( eip1193Provider, hre.network.config.gcpKmsKeyName ); - if (hre.network.config.minMaxFeePerGas || hre.network.config.minMaxPriorityFeePerGas) { - wrappedProvider = new AutomaticGasProvider( - wrappedProvider, - hre.network.config.gasMultiplier - ); - wrappedProvider = new AutomaticGasPriceProvider( - wrappedProvider, - hre.network.config.minMaxFeePerGas, - hre.network.config.minMaxPriorityFeePerGas - ); - } + + wrappedProvider = new AutomaticGasProvider( + wrappedProvider, + hre.network.config.gasMultiplier + ); + + wrappedProvider = new AutomaticGasPriceProvider( + wrappedProvider, + ); + wrappedProvider = new GanacheGasMultiplierProvider(wrappedProvider); + hre.network.provider = new BackwardsCompatibilityProviderAdapter( wrappedProvider ); diff --git a/src/provider.ts b/src/provider.ts index c06fb73..5187943 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -1,4 +1,4 @@ -import { GcpKmsSigner, GcpKmsSignerCredentials} from "ethers-gcp-kms-signer"; +import { GcpKmsSigner, GcpKmsSignerCredentials } from "ethers-gcp-kms-signer"; import { BigNumber, utils } from "ethers"; import { keccak256 } from "ethers/lib/utils"; @@ -13,7 +13,7 @@ import { assert } from "console"; export class KMSSigner extends ProviderWrapperWithChainId { public kmsSigner: GcpKmsSigner; public kmsCredentials: GcpKmsSignerCredentials; - + constructor(provider: EIP1193Provider, gcpKmsKeyName: string) { super(provider); @@ -24,12 +24,13 @@ export class KMSSigner extends ProviderWrapperWithChainId { public async request(args: RequestArguments): Promise { const method = args.method; const params = this._getParams(args); - const sender = await this._getSender(); + const sender = await this._getSender() + if (method === "eth_sendTransaction") { const [txRequest] = validateParams(params, rpcTransactionRequest); const tx = await utils.resolveProperties(txRequest); const nonce = tx.nonce ?? (await this._getNonce(sender)); - const baseTx: utils.UnsignedTransaction = { + const baseTx = { chainId: (await this._getChainId()) || undefined, data: tx.data, gasLimit: tx.gas?.toString(), @@ -51,13 +52,7 @@ export class KMSSigner extends ProviderWrapperWithChainId { delete baseTx.maxPriorityFeePerGas; } - console.log("TX", baseTx); - - const unsignedTx = utils.serializeTransaction(baseTx); - const hash = keccak256(utils.arrayify(unsignedTx)); - const sig = await this.kmsSigner.signMessage(hash); - - const rawTx = utils.serializeTransaction(baseTx, sig); + const rawTx = await this.kmsSigner.signTransaction(baseTx); return this._wrappedProvider.request({ method: "eth_sendRawTransaction", @@ -87,10 +82,10 @@ export class KMSSigner extends ProviderWrapperWithChainId { } } -function parseKmsKey(gcpKmsKeyName: string): GcpKmsSignerCredentials { +function parseKmsKey(gcpKmsKeyName: string): GcpKmsSignerCredentials { let parts = gcpKmsKeyName.split("/"); assert(gcpKmsKeyName, "gcpKmsKeyName is missing.") - assert(parts.length === 10, `Incorrect gcp kms key format: ${ gcpKmsKeyName }. ` + + assert(parts.length === 10, `Incorrect gcp kms key format: ${gcpKmsKeyName}. ` + `Expected: projects//locations//keyRings//cryptoKeys//cryptoKeyVersions/`); return { projectId: parts[1], // your project id in gcp