diff --git a/xc-admin/package-lock.json b/xc-admin/package-lock.json index 6d87ac6d..796d3cef 100644 --- a/xc-admin/package-lock.json +++ b/xc-admin/package-lock.json @@ -732,6 +732,62 @@ "follow-redirects": "^1.14.4" } }, + "node_modules/@coral-xyz/anchor": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.26.0.tgz", + "integrity": "sha512-PxRl+wu5YyptWiR9F2MBHOLLibm87Z4IMUBPreX+DYBtPM+xggvcPi0KAN7+kIL4IrIhXI8ma5V0MCXxSN1pHg==", + "dependencies": { + "@coral-xyz/borsh": "^0.26.0", + "@solana/web3.js": "^1.68.0", + "base64-js": "^1.5.1", + "bn.js": "^5.1.2", + "bs58": "^4.0.1", + "buffer-layout": "^1.2.2", + "camelcase": "^6.3.0", + "cross-fetch": "^3.1.5", + "crypto-hash": "^1.3.0", + "eventemitter3": "^4.0.7", + "js-sha256": "^0.9.0", + "pako": "^2.0.3", + "snake-case": "^3.0.4", + "superstruct": "^0.15.4", + "toml": "^3.0.0" + }, + "engines": { + "node": ">=11" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/@coral-xyz/borsh": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.26.0.tgz", + "integrity": "sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==", + "dependencies": { + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@solana/web3.js": "^1.68.0" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/superstruct": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.15.5.tgz", + "integrity": "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==" + }, "node_modules/@coral-xyz/borsh": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.2.6.tgz", @@ -3949,6 +4005,41 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "node_modules/@pythnetwork/client": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@pythnetwork/client/-/client-2.9.0.tgz", + "integrity": "sha512-2CyDmTwPWW+JCQgRKUpwMR/31oiLWH6I3GA0h2ZXIcbt/hWxcr5TXyKlWuyi+l+jh73WWh88gq8NXLoIgRcvkA==", + "dependencies": { + "buffer": "^6.0.1" + }, + "peerDependencies": { + "@coral-xyz/anchor": "^0.26.0", + "@solana/web3.js": "^1.30.2" + } + }, + "node_modules/@pythnetwork/client/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/@scure/base": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", @@ -13531,6 +13622,10 @@ } } }, + "node_modules/xc-admin-cli": { + "resolved": "packages/xc-admin-cli", + "link": true + }, "node_modules/xc-admin-common": { "resolved": "packages/xc-admin-common", "link": true @@ -13633,6 +13728,27 @@ "zen-observable": "0.8.15" } }, + "packages/xc-admin-cli": { + "version": "0.0.0", + "license": "ISC", + "dependencies": { + "@coral-xyz/anchor": "^0.26.0", + "@pythnetwork/client": "^2.9.0", + "@solana/web3.js": "^1.73.0", + "@sqds/mesh": "^1.0.6", + "commander": "^9.5.0", + "typescript": "^4.9.4", + "xc-admin-common": "*" + } + }, + "packages/xc-admin-cli/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "engines": { + "node": "^12.20.0 || >=14" + } + }, "packages/xc-admin-common": { "version": "0.0.0", "license": "ISC", @@ -14212,6 +14328,49 @@ } } }, + "@coral-xyz/anchor": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.26.0.tgz", + "integrity": "sha512-PxRl+wu5YyptWiR9F2MBHOLLibm87Z4IMUBPreX+DYBtPM+xggvcPi0KAN7+kIL4IrIhXI8ma5V0MCXxSN1pHg==", + "requires": { + "@coral-xyz/borsh": "^0.26.0", + "@solana/web3.js": "^1.68.0", + "base64-js": "^1.5.1", + "bn.js": "^5.1.2", + "bs58": "^4.0.1", + "buffer-layout": "^1.2.2", + "camelcase": "^6.3.0", + "cross-fetch": "^3.1.5", + "crypto-hash": "^1.3.0", + "eventemitter3": "^4.0.7", + "js-sha256": "^0.9.0", + "pako": "^2.0.3", + "snake-case": "^3.0.4", + "superstruct": "^0.15.4", + "toml": "^3.0.0" + }, + "dependencies": { + "@coral-xyz/borsh": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.26.0.tgz", + "integrity": "sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==", + "requires": { + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "superstruct": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.15.5.tgz", + "integrity": "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==" + } + } + }, "@coral-xyz/borsh": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.2.6.tgz", @@ -16698,6 +16857,25 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "@pythnetwork/client": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@pythnetwork/client/-/client-2.9.0.tgz", + "integrity": "sha512-2CyDmTwPWW+JCQgRKUpwMR/31oiLWH6I3GA0h2ZXIcbt/hWxcr5TXyKlWuyi+l+jh73WWh88gq8NXLoIgRcvkA==", + "requires": { + "buffer": "^6.0.1" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + } + } + }, "@scure/base": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", @@ -24127,6 +24305,25 @@ "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "requires": {} }, + "xc-admin-cli": { + "version": "file:packages/xc-admin-cli", + "requires": { + "@coral-xyz/anchor": "^0.26.0", + "@pythnetwork/client": "^2.9.0", + "@solana/web3.js": "^1.73.0", + "@sqds/mesh": "*", + "commander": "^9.5.0", + "typescript": "^4.9.4", + "xc-admin-common": "*" + }, + "dependencies": { + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" + } + } + }, "xc-admin-common": { "version": "file:packages/xc-admin-common", "requires": { diff --git a/xc-admin/packages/xc-admin-cli/package.json b/xc-admin/packages/xc-admin-cli/package.json new file mode 100644 index 00000000..1237f201 --- /dev/null +++ b/xc-admin/packages/xc-admin-cli/package.json @@ -0,0 +1,29 @@ +{ + "name": "xc-admin-cli", + "version": "0.0.0", + "description": "", + "author": "", + "homepage": "https://github.com/pyth-network/pyth-crosschain", + "license": "ISC", + "main": "src/index.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/pyth-network/pyth-crosschain.git" + }, + "bugs": { + "url": "https://github.com/pyth-network/pyth-crosschain/issues" + }, + "scripts": { + "build": "tsc", + "format": "prettier --write \"src/**/*.ts\"" + }, + "dependencies": { + "@coral-xyz/anchor": "^0.26.0", + "@pythnetwork/client": "^2.9.0", + "@solana/web3.js": "^1.73.0", + "@sqds/mesh": "^1.0.6", + "commander": "^9.5.0", + "typescript": "^4.9.4", + "xc-admin-common": "*" + } +} diff --git a/xc-admin/packages/xc-admin-cli/src/index.ts b/xc-admin/packages/xc-admin-cli/src/index.ts new file mode 100644 index 00000000..e2c5091f --- /dev/null +++ b/xc-admin/packages/xc-admin-cli/src/index.ts @@ -0,0 +1,98 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import { program } from "commander"; +import { PythCluster } from "@pythnetwork/client/lib/cluster"; +import { getPythClusterApiUrl } from "@pythnetwork/client/lib/cluster"; +import { AnchorError, AnchorProvider, Program } from "@coral-xyz/anchor"; +import fs from "fs"; +import SquadsMesh from "@sqds/mesh"; +import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; +import { proposeInstructions } from "xc-admin-common"; + +const PROGRAM_AUTHORITY_ESCROW = new PublicKey( + "escMHe7kSqPcDHx4HU44rAHhgdTLBZkUrU39aN8kMcL" +); +const BPF_UPGRADABLE_LOADER = new PublicKey( + "BPFLoaderUpgradeab1e11111111111111111111111" +); + +const mutlisigCommand = (name: string, description: string) => + program + .command(name) + .description(description) + .requiredOption("-c, --cluster ", "solana cluster to use") + .requiredOption("-w, --wallet ", "path to the operations key") + .requiredOption("-v, --vault ", "multisig address"); + +program + .name("xc-admin-cli") + .description("CLI for interacting with Pyth's xc-admin") + .version("0.1.0"); + +mutlisigCommand( + "accept-authority", + "Accept authority from the program authority escrow" +) + .requiredOption( + "-p, --program-id ", + "program whose authority we want to transfer" + ) + .requiredOption( + "-a, --current ", + "current authority (before the transfer)" + ) + + .action(async (options: any) => { + const wallet = new NodeWallet( + Keypair.fromSecretKey( + Uint8Array.from(JSON.parse(fs.readFileSync(options.wallet, "ascii"))) + ) + ); + const cluster: PythCluster = options.cluster; + const programId: PublicKey = new PublicKey(options.programId); + const current: PublicKey = new PublicKey(options.current); + const vault: PublicKey = new PublicKey(options.vault); + + const squad = SquadsMesh.endpoint(getPythClusterApiUrl(cluster), wallet); + const msAccount = await squad.getMultisig(vault); + const vaultAuthority = squad.getAuthorityPDA( + msAccount.publicKey, + msAccount.authorityIndex + ); + + const programAuthorityEscrowIdl = await Program.fetchIdl( + PROGRAM_AUTHORITY_ESCROW, + new AnchorProvider( + squad.connection, + squad.wallet, + AnchorProvider.defaultOptions() + ) + ); + const programAuthorityEscrow = new Program( + programAuthorityEscrowIdl!, + PROGRAM_AUTHORITY_ESCROW, + new AnchorProvider( + squad.connection, + squad.wallet, + AnchorProvider.defaultOptions() + ) + ); + const programDataAccount = PublicKey.findProgramAddressSync( + [programId.toBuffer()], + BPF_UPGRADABLE_LOADER + )[0]; + + const proposalInstruction = await programAuthorityEscrow.methods + .accept() + .accounts({ + currentAuthority: current, + newAuthority: vaultAuthority, + programAccount: programId, + programDataAccount, + bpfUpgradableLoader: BPF_UPGRADABLE_LOADER, + }) + .instruction(); + + await proposeInstructions(squad, vault, [proposalInstruction], false); + }); + +program.parse(); diff --git a/xc-admin/packages/xc-admin-cli/tsconfig.json b/xc-admin/packages/xc-admin-cli/tsconfig.json new file mode 100644 index 00000000..28632883 --- /dev/null +++ b/xc-admin/packages/xc-admin-cli/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "declaration": true, + "target": "es2016", + "module": "commonjs", + "outDir": "lib", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "noErrorTruncation": true + }, + "include": ["src/**/*.ts"], + "exclude": ["src/__tests__/"] +}