-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
668 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: Test | ||
|
||
on: | ||
push: | ||
branches: [main] | ||
pull_request: {} | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version: 18.x | ||
cache: 'yarn' | ||
- name: Install dependencies | ||
run: yarn install | ||
- name: Run tests | ||
run: yarn test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Changelog | ||
|
||
## 0.0.1-alpha.3 (2024-02-09) | ||
|
||
- Support proposing upgrades. ([#2](https://github.com/OpenZeppelin/defender-deploy-client-cli/pull/2)) | ||
|
||
## 0.0.1-alpha.2 (2024-02-01) | ||
|
||
- Enable constructorBytecode argument. ([#1](https://github.com/OpenZeppelin/defender-deploy-client-cli/pull/1)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
snapshotDir: '.', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import minimist from "minimist"; | ||
import { deploy } from './commands/deploy'; | ||
import { proposeUpgrade } from './commands/propose-upgrade'; | ||
|
||
const USAGE = 'Usage: npx @openzeppelin/defender-deploy-client-cli <COMMAND> <OPTIONS>'; | ||
const DETAILS = ` | ||
Performs actions using OpenZeppelin Defender. | ||
Available commands: | ||
deploy Deploys a contract. | ||
proposeUpgrade Proposes an upgrade. | ||
Run 'npx @openzeppelin/defender-deploy-client-cli <COMMAND> --help' for more information on a command. | ||
`; | ||
|
||
export async function main(args: string[]): Promise<void> { | ||
const regularArgs = minimist(args)._; | ||
|
||
if (regularArgs.length === 0) { | ||
console.log(USAGE); | ||
console.log(DETAILS); | ||
} else { | ||
if (regularArgs[0] === 'deploy') { | ||
await deploy(args.slice(1)); | ||
} else if (regularArgs[0] === 'proposeUpgrade') { | ||
await proposeUpgrade(args.slice(1)); | ||
} else { | ||
throw new Error(`\ | ||
Unknown command: ${regularArgs[0]} | ||
Run 'npx @openzeppelin/defender-deploy-client-cli --help' for usage.\ | ||
`); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import minimist from 'minimist'; | ||
import { FunctionArgs, upgradeContract } from '../internal/upgrade-contract'; | ||
import { getDeployClient } from '../internal/client'; | ||
import { getAndValidateString, getNetwork } from '../internal/utils'; | ||
import { DeployClient } from '@openzeppelin/defender-sdk-deploy-client'; | ||
|
||
const USAGE = 'Usage: npx @openzeppelin/defender-deploy-client-cli proposeUpgrade --proxyAddress <PROXY_ADDRESS> --newImplementationAddress <NEW_IMPLEMENTATION_ADDRESS> --chainId <CHAIN_ID> [--proxyAdminAddress <PROXY_ADMIN_ADDRESS>] [--abiFile <CONTRACT_ARTIFACT_FILE_PATH>] [--approvalProcessId <UPGRADE_APPROVAL_PROCESS_ID>]'; | ||
const DETAILS = ` | ||
Proposes an upgrade using OpenZeppelin Defender. | ||
Required options: | ||
--proxyAddress <PROXY_ADDRESS> Address of the proxy to upgrade. | ||
--newImplementationAddress <NEW_IMPLEMENTATION_ADDRESS> Address of the new implementation contract. | ||
--chainId <CHAIN_ID> Chain ID of the network to use. | ||
Additional options: | ||
--proxyAdminAddress <PROXY_ADMIN_ADDRESS> Address of the proxy's admin. Required if the proxy is a transparent proxy. | ||
--abiFile <CONTRACT_ARTIFACT_FILE_PATH> Path to a JSON file that contains an "abi" entry, where its value will be used as the new implementation ABI. | ||
--approvalProcessId <UPGRADE_APPROVAL_PROCESS_ID> The ID of the upgrade approval process. Defaults to the upgrade approval process configured for your deployment environment on Defender. | ||
`; | ||
|
||
export async function proposeUpgrade(args: string[], deployClient?: DeployClient): Promise<void> { | ||
const { parsedArgs, extraArgs } = parseArgs(args); | ||
|
||
if (!help(parsedArgs, extraArgs)) { | ||
const functionArgs = getFunctionArgs(parsedArgs, extraArgs); | ||
const client = deployClient ?? getDeployClient(); | ||
const upgradeResponse = await upgradeContract(functionArgs, client); | ||
|
||
console.log(`Upgrade proposal created.`); | ||
console.log(`Proposal ID: ${upgradeResponse.proposalId}`); | ||
if (upgradeResponse.externalUrl !== undefined) { | ||
console.log(`Proposal URL: ${upgradeResponse.externalUrl}`); | ||
} | ||
} | ||
} | ||
|
||
function parseArgs(args: string[]) { | ||
const parsedArgs = minimist(args, { | ||
boolean: [ | ||
'help', | ||
], | ||
string: ['proxyAddress', 'newImplementationAddress', 'chainId', 'proxyAdminAddress', 'abiFile', 'approvalProcessId'], | ||
alias: { h: 'help' }, | ||
}); | ||
const extraArgs = parsedArgs._; | ||
return { parsedArgs, extraArgs }; | ||
} | ||
|
||
function help(parsedArgs: minimist.ParsedArgs, extraArgs: string[]): boolean { | ||
if (parsedArgs['help']) { | ||
console.log(USAGE); | ||
console.log(DETAILS); | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* Gets and validates function arguments and options. | ||
* @returns Function arguments | ||
* @throws Error if any arguments or options are invalid. | ||
*/ | ||
export function getFunctionArgs(parsedArgs: minimist.ParsedArgs, extraArgs: string[]): FunctionArgs { | ||
if (extraArgs.length !== 0) { | ||
throw new Error('The proposeUpgrade command does not take any arguments, only options.'); | ||
} else { | ||
// Required options | ||
const proxyAddress = getAndValidateString(parsedArgs, 'proxyAddress', true)!; | ||
const newImplementationAddress = getAndValidateString(parsedArgs, 'newImplementationAddress', true)!; | ||
|
||
const networkString = getAndValidateString(parsedArgs, 'chainId', true)!; | ||
const network = getNetwork(parseInt(networkString)); | ||
|
||
// Additional options | ||
const proxyAdminAddress = getAndValidateString(parsedArgs, 'proxyAdminAddress'); | ||
const abiFile = getAndValidateString(parsedArgs, 'abiFile'); | ||
const approvalProcessId = getAndValidateString(parsedArgs, 'approvalProcessId'); | ||
|
||
checkInvalidArgs(parsedArgs); | ||
|
||
return { proxyAddress, newImplementationAddress, network, proxyAdminAddress, abiFile, approvalProcessId }; | ||
} | ||
} | ||
|
||
|
||
|
||
function checkInvalidArgs(parsedArgs: minimist.ParsedArgs) { | ||
const invalidArgs = Object.keys(parsedArgs).filter( | ||
key => | ||
![ | ||
'help', | ||
'h', | ||
'_', | ||
'proxyAddress', | ||
'newImplementationAddress', | ||
'chainId', | ||
'proxyAdminAddress', | ||
'abiFile', | ||
'approvalProcessId', | ||
].includes(key), | ||
); | ||
if (invalidArgs.length > 0) { | ||
throw new Error(`Invalid options: ${invalidArgs.join(', ')}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { DeployClient } from "@openzeppelin/defender-sdk-deploy-client"; | ||
|
||
export function getDeployClient(): DeployClient { | ||
require('dotenv').config(); | ||
const apiKey = process.env.DEFENDER_KEY as string; | ||
const apiSecret = process.env.DEFENDER_SECRET as string; | ||
|
||
if (apiKey === undefined || apiSecret === undefined) { | ||
throw new Error('DEFENDER_KEY and DEFENDER_SECRET must be set in environment variables.'); | ||
} | ||
|
||
return new DeployClient({ apiKey, apiSecret }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { promises as fs } from 'fs'; | ||
|
||
import { Network } from '@openzeppelin/defender-sdk-base-client'; | ||
import { DeployClient, UpgradeContractRequest, UpgradeContractResponse } from '@openzeppelin/defender-sdk-deploy-client'; | ||
|
||
export interface FunctionArgs { | ||
proxyAddress: string; | ||
newImplementationAddress: string; | ||
network: Network; | ||
proxyAdminAddress?: string; | ||
abiFile?: string; | ||
approvalProcessId?: string; | ||
} | ||
|
||
export async function upgradeContract(args: FunctionArgs, client: DeployClient): Promise<UpgradeContractResponse> { | ||
let newImplementationABI: string | undefined; | ||
if (args.abiFile !== undefined) { | ||
const artifactObject = JSON.parse(await fs.readFile(args.abiFile, 'utf8')); | ||
newImplementationABI = JSON.stringify(artifactObject.abi); | ||
} | ||
|
||
const deploymentRequest: UpgradeContractRequest = { | ||
proxyAddress: args.proxyAddress, | ||
newImplementationAddress: args.newImplementationAddress, | ||
network: args.network, | ||
proxyAdminAddress: args.proxyAdminAddress, | ||
newImplementationABI: newImplementationABI, | ||
approvalProcessId: args.approvalProcessId, | ||
}; | ||
|
||
return client.upgradeContract(deploymentRequest); | ||
} |
Oops, something went wrong.