From 39978a2c62e7132a7ed41fc4dfd543317b77b171 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 2 May 2024 09:38:01 -0400 Subject: [PATCH] Add options to override deployment transaction parameters (#10) --- CHANGELOG.md | 4 +++ README.md | 4 +++ package.json | 2 +- src/commands/deploy.ts | 43 +++++++++++++++++++++++++++++--- src/internal/deploy-contract.ts | 4 ++- test/deploy.js | 29 ++++++++++++++++++++- test/deploy.js.md | 4 +++ test/deploy.js.snap | Bin 853 -> 991 bytes 8 files changed, 84 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1207a32..4454fac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.0.1-alpha.7 (2024-05-02) + +- Add options to enable overriding deployment transaction parameters. + ## 0.0.1-alpha.6 (2024-04-16) - Update documentation and error message about supported values for `--licenseType` option. ([#9](https://github.com/OpenZeppelin/defender-deploy-client-cli/pull/9)) diff --git a/README.md b/README.md index 3363de3..af09de4 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,10 @@ Additional options: --relayerId Relayer ID to use for deployment. Defaults to the relayer configured for your deployment environment on Defender. --salt Salt to use for CREATE2 deployment. Defaults to a random salt. --createFactoryAddress Address of the CREATE2 factory to use for deployment. Defaults to the factory provided by Defender. + --gasLimit Maximum amount of gas to allow the deployment transaction to use. + --gasPrice Gas price for legacy transactions, in wei. + --maxFeePerGas Maximum total fee per gas, in wei. + --maxPriorityFeePerGas Maximum priority fee per gas, in wei. ``` ### Proposing an upgrade diff --git a/package.json b/package.json index 644bb9f..1cb76d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/defender-deploy-client-cli", - "version": "0.0.1-alpha.6", + "version": "0.0.1-alpha.7", "description": "CLI for deployments using OpenZeppelin Defender SDK", "repository": "https://github.com/OpenZeppelin/defender-deploy-client-cli", "license": "MIT", diff --git a/src/commands/deploy.ts b/src/commands/deploy.ts index a533364..72cb4e7 100644 --- a/src/commands/deploy.ts +++ b/src/commands/deploy.ts @@ -2,7 +2,7 @@ import minimist from 'minimist'; import { FunctionArgs, deployContract } from '../internal/deploy-contract'; import { getDeployClient } from '../internal/client'; import { USAGE_COMMAND_PREFIX, getAndValidateString, getNetwork } from '../internal/utils'; -import { DeployClient } from '@openzeppelin/defender-sdk-deploy-client'; +import { DeployClient, TxOverrides } from '@openzeppelin/defender-sdk-deploy-client'; import { NetworkClient } from '@openzeppelin/defender-sdk-network-client'; const USAGE = `${USAGE_COMMAND_PREFIX} deploy --contractName --contractPath --chainId --buildInfoFile [--constructorBytecode ] [--licenseType ] [--verifySourceCode ] [--relayerId ] [--salt ] [--createFactoryAddress ]`; @@ -22,6 +22,10 @@ Additional options: --relayerId Relayer ID to use for deployment. Defaults to the relayer configured for your deployment environment on Defender. --salt Salt to use for CREATE2 deployment. Defaults to a random salt. --createFactoryAddress Address of the CREATE2 factory to use for deployment. Defaults to the factory provided by Defender. + --gasLimit Maximum amount of gas to allow the deployment transaction to use. + --gasPrice Gas price for legacy transactions, in wei. + --maxFeePerGas Maximum total fee per gas, in wei. + --maxPriorityFeePerGas Maximum priority fee per gas, in wei. `; export async function deploy(args: string[], deployClient?: DeployClient, networkClient?: NetworkClient): Promise { @@ -42,7 +46,8 @@ function parseArgs(args: string[]) { 'help', 'verifySourceCode', ], - string: ['contractName', 'contractPath', 'chainId', 'buildInfoFile', 'licenseType', 'constructorBytecode', 'relayerId', 'salt', 'createFactoryAddress'], + string: ['contractName', 'contractPath', 'chainId', 'buildInfoFile', 'licenseType', 'constructorBytecode', 'relayerId', 'salt', 'createFactoryAddress', 'gasLimit', 'gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas'], + alias: { h: 'help' }, default: { verifySourceCode: true }, }); @@ -86,9 +91,16 @@ async function getFunctionArgs(parsedArgs: minimist.ParsedArgs, extraArgs: strin const salt = getAndValidateString(parsedArgs, 'salt'); const createFactoryAddress = getAndValidateString(parsedArgs, 'createFactoryAddress'); + const txOverrides: TxOverrides = { + gasLimit: parseNumberOrUndefined(getAndValidateString(parsedArgs, 'gasLimit')), + gasPrice: parseHexOrUndefined(getAndValidateString(parsedArgs, 'gasPrice')), + maxFeePerGas: parseHexOrUndefined(getAndValidateString(parsedArgs, 'maxFeePerGas')), + maxPriorityFeePerGas: parseHexOrUndefined(getAndValidateString(parsedArgs, 'maxPriorityFeePerGas')), + }; + checkInvalidArgs(parsedArgs); - return { contractName, contractPath, network, buildInfoFile, licenseType, constructorBytecode, verifySourceCode, relayerId, salt, createFactoryAddress }; + return { contractName, contractPath, network, buildInfoFile, licenseType, constructorBytecode, verifySourceCode, relayerId, salt, createFactoryAddress, txOverrides }; } } @@ -109,9 +121,34 @@ function checkInvalidArgs(parsedArgs: minimist.ParsedArgs) { 'relayerId', 'salt', 'createFactoryAddress', + 'gasLimit', + 'gasPrice', + 'maxFeePerGas', + 'maxPriorityFeePerGas', ].includes(key), ); if (invalidArgs.length > 0) { throw new Error(`Invalid options: ${invalidArgs.join(', ')}`); } } + +function parseHexOrUndefined(value?: string): string | undefined { + if (value !== undefined) { + // If not a hex string, convert from decimal to hex as a string + if (!value.startsWith('0x')) { + return '0x' + Number(value).toString(16); + } else { + return value; + } + } else { + return undefined; + } +} + +function parseNumberOrUndefined(value?: string): number | undefined { + if (value !== undefined) { + return Number(value); + } else { + return undefined; + } +} \ No newline at end of file diff --git a/src/internal/deploy-contract.ts b/src/internal/deploy-contract.ts index e2caeb7..6aac19e 100644 --- a/src/internal/deploy-contract.ts +++ b/src/internal/deploy-contract.ts @@ -1,7 +1,7 @@ import { promises as fs } from 'fs'; import { Network } from '@openzeppelin/defender-sdk-base-client'; -import { DeployClient, DeployContractRequest, DeploymentResponse, SourceCodeLicense } from '@openzeppelin/defender-sdk-deploy-client'; +import { DeployClient, DeployContractRequest, DeploymentResponse, SourceCodeLicense, TxOverrides } from '@openzeppelin/defender-sdk-deploy-client'; export interface FunctionArgs { contractName: string; @@ -14,6 +14,7 @@ export interface FunctionArgs { relayerId?: string; salt?: string; createFactoryAddress?: string; + txOverrides?: TxOverrides; } export async function deployContract(args: FunctionArgs, client: DeployClient) { @@ -30,6 +31,7 @@ export async function deployContract(args: FunctionArgs, client: DeployClient) { relayerId: args.relayerId, salt: args.salt, createFactoryAddress: args.createFactoryAddress, + txOverrides: args.txOverrides, }; let deployment: DeploymentResponse; diff --git a/test/deploy.js b/test/deploy.js index 3579d90..78e2482 100644 --- a/test/deploy.js +++ b/test/deploy.js @@ -75,11 +75,32 @@ test('deploy required args', async t => { relayerId: undefined, salt: undefined, createFactoryAddress: undefined, + txOverrides: { + gasLimit: undefined, + gasPrice: undefined, + maxFeePerGas: undefined, + maxPriorityFeePerGas: undefined + }, }); }); test('deploy all args', async t => { - const args = ['--contractName', 'MyContract', '--contractPath', 'contracts/MyContract.sol', '--chainId', FAKE_CHAIN_ID, '--buildInfoFile', 'test/input/build-info.json', '--constructorBytecode', '0x1234', '--licenseType', 'MIT', '--verifySourceCode', 'false', '--relayerId', 'my-relayer-id', '--salt', '0x4567', '--createFactoryAddress', '0x0000000000000000000000000000000000098765']; + const args = [ + '--contractName', 'MyContract', + '--contractPath', 'contracts/MyContract.sol', + '--chainId', FAKE_CHAIN_ID, + '--buildInfoFile', 'test/input/build-info.json', + '--constructorBytecode', '0x1234', + '--licenseType', 'MIT', + '--verifySourceCode', 'false', + '--relayerId', 'my-relayer-id', + '--salt', '0x4567', + '--createFactoryAddress', '0x0000000000000000000000000000000000098765', + '--gasLimit', '1000000', + '--gasPrice', '1000000000', // 1 gwei + '--maxFeePerGas', '2000000000', // 2 gwei + '--maxPriorityFeePerGas', '500000000', // 0.5 gwei + ]; await deploy(args, t.context.fakeDeployClient, t.context.fakeNetworkClient); @@ -96,5 +117,11 @@ test('deploy all args', async t => { relayerId: 'my-relayer-id', salt: '0x4567', createFactoryAddress: '0x0000000000000000000000000000000000098765', + txOverrides: { + gasLimit: 1000000, + gasPrice: '0x3b9aca00', + maxFeePerGas: '0x77359400', + maxPriorityFeePerGas: '0x1dcd6500', + } }); }); \ No newline at end of file diff --git a/test/deploy.js.md b/test/deploy.js.md index 787d592..6c5254b 100644 --- a/test/deploy.js.md +++ b/test/deploy.js.md @@ -25,5 +25,9 @@ Generated by [AVA](https://avajs.dev). --relayerId Relayer ID to use for deployment. Defaults to the relayer configured for your deployment environment on Defender.␊ --salt Salt to use for CREATE2 deployment. Defaults to a random salt.␊ --createFactoryAddress Address of the CREATE2 factory to use for deployment. Defaults to the factory provided by Defender.␊ + --gasLimit Maximum amount of gas to allow the deployment transaction to use.␊ + --gasPrice Gas price for legacy transactions, in wei.␊ + --maxFeePerGas Maximum total fee per gas, in wei.␊ + --maxPriorityFeePerGas Maximum priority fee per gas, in wei.␊ ␊ ` diff --git a/test/deploy.js.snap b/test/deploy.js.snap index 23506f67d4fc4d5d3de7029c10d2ad3dddd04553..b0a7b4f7faedfe436ea08e288d7ea4834e580616 100644 GIT binary patch literal 991 zcmV<510eiCRzVtSmr!ga z1X>83O&^O000000000A(RKafJHV{o)6ajqe9}IG?onntc5LB_{tN_P0Bn7Qo6b2)W zY$7ztl2l?Bz3iuKKd^sLphHS_lsLQH>LQ6V^XARG8Gfwpg(@EGhriHqC-9*}Ez}Oy zC^WZ{X}ea=e)w3H%(2DkKi{03p7`ghZ%@Aa`tQ>_%T{;+Qg7i$rBVKYTBG3dyu=D+ ziKgG+^$WpKI{$(;L%&}r=?p8}lx+|$hqGy(M#FqLjc((sj(N`9x?`S4`TJmAGcJ>o zEbpUax=cp?@V@4vOk}0TToC5}J54T=WEfAgxUoE<;nglvwJC5I5C{_gteCLaco-CHhsLu^ z<4LrLQ_nM)EE5hcvuKhx|Aj&3aLhcVT~w9^ttCon9OdzH9C=cUWi%S4aYn>?y;10* z1qS;*L2bEQ!Hi`1rIi6jtqg-+FU8+$ZmFVg)5nGgY-LIRyYjDmF4)V$jH&q|pWB~gD#GwvJ9#8HPoKWf*DJRZ(QzTZD=``Tc|w{2Ymtu?3R@uFm&; z+|%g$G!2riwN}Ov)FTr$9eNpR&t!e*Nu_G(#e{nTc|d2s2OO*haCV#IXQ8~$?kd1f z>p;gB)_8V#zft4BZx*YCvtgbHs_Ej}3%~DS+F1QWQg$0ck4Z;&P@ZyPv)&1HqDLm?vtz-7!p8jz{9pn{C}=4fW1e% z)cb^06aS^1*}H3_9(l7Puj*bgJK-BnwckY9a+1(2bQ^HXwtQ1>fNf}aoM%G;p+X_l zQ^?WjFioell#=$kWq3$3H#DwjCqR z(fGK_+vw-zIHuc+rijq5o;f?^=$ff$n=}mvaX5k@Qp(UB_6m*&&e9}bd;(XuQEx-{ N{skXCaUd-S005U#<}m;O literal 853 zcmV-b1FHN%RzV%HI00000000A(R84Q(MhtaRGy(S3e?a)$T^BhTK~Q;Dn+VwK29k`zErP{Z zqE-XTGn1K-<1KQ@&&V-3`UWpbG>zUv zjg65`3FB(*-MbHEDZDs)`03Y!M+f};^4r1h58gjKbz+Cdpv*P=r46e8&=_o_Ix4Y3 zRid4Cc+;Yhs64-*&oG@9T6rr9zZRDWPv)C-o<{S0yN+JOPen!dpS7>Fm&va`GoCgGg#J81b9CjWS8?i^r zEYo-uoy95h3??TU4^Ogam3RMzMd5KNn9@EfON-7Cr8JK6c)N_4)Y&##EYdh5Vw1@t zbkTu;p-<2{sdlg-8UE>IfJHCEY%)plbt^5FppBPQ{_zBUqydCeU4gIhOB%0x31_zu z?v)9`DMj2us7RbX_^ybfXUzF}?2(|ad9UmPbk+QB3Y9{s3 z(8qzsl+$!bt~EwmPf%B)Y3Yz