diff --git a/Anchor.toml b/Anchor.toml index 7cfe463..64b39f0 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -1,4 +1,6 @@ [toolchain] +anchor_version = "0.30.1" +package_manager = "yarn" [features] resolution = true @@ -40,3 +42,4 @@ test-feed = "yarn vitest run test/data-feed.test.ts" test-minter = "yarn vitest run test/minter-vault.test.ts" test-redeemer = "yarn vitest run test/redeemer-vault.test.ts" test-tokens = "yarn vitest run test/mtokens.test.ts" +test-squads= "yarn vitest run test/squads.test.ts" diff --git a/common/addresses.ts b/common/addresses.ts index 05f68b5..0fce242 100644 --- a/common/addresses.ts +++ b/common/addresses.ts @@ -6,10 +6,16 @@ import { MProduct, PaymentToken } from './tokenTypes'; export interface NetworkAddresses { acRoleGlobal?: PublicKey; ac?: PublicKey; + timelock?: TimelockAddresses; tokens?: Partial>; feeds?: Partial>; } +export type TimelockAddresses = { + multisig: PublicKey; + vault: PublicKey; +}; + export interface DataFeed { token?: PublicKey; tokenProgram?: PublicKey; @@ -40,6 +46,10 @@ export const addresses: Record = { devnet: { acRoleGlobal: new PublicKey('BW95RL5v9685QqxvuRGjXiM3f6Td9k5QEBiGkXMHwsx1'), ac: new PublicKey('5cMcz3NsbJy6AyvGPpMupBXFiQkwC9qRuMxkTPiTb3Qu'), + timelock: { + multisig: new PublicKey('CNM9uFrjXTun2zk6w7FkXKgryURXezBMZJZ4RSh8NLt3'), + vault: new PublicKey('3XeBT7F1H3cbSETdHvTFDr8Wz47yNZZ6dYZsQJScdm9u'), + }, tokens: { [MProduct.MFONE]: { acRole: new PublicKey('2SAaMSzZd9DuNsR3QWNy8mZjNJPnMqdsC4rXz8wgnfFR'), diff --git a/common/programs.ts b/common/programs.ts new file mode 100644 index 0000000..3c6bf34 --- /dev/null +++ b/common/programs.ts @@ -0,0 +1,13 @@ +import { PublicKey } from '@solana/web3.js'; + +import ACCESS_CONTROL_IDL from '../target/idl/access_control.json' with { type: 'json' }; +import VAULTS_IDL from '../target/idl/midas_vaults.json' with { type: 'json' }; +import TOKEN_AUTHORITY_IDL from '../target/idl/token_authority.json' with { type: 'json' }; +import DATA_FEED_IDL from '../target/idl/data_feed.json' with { type: 'json' }; + +export const programAddresses = { + access_control: new PublicKey(ACCESS_CONTROL_IDL.address), + midas_vaults: new PublicKey(VAULTS_IDL.address), + token_authority: new PublicKey(TOKEN_AUTHORITY_IDL.address), + data_feed: new PublicKey(DATA_FEED_IDL.address), +}; diff --git a/external/squads.so b/external/squads.so new file mode 100644 index 0000000..017fdad Binary files /dev/null and b/external/squads.so differ diff --git a/package.json b/package.json index c150396..e65a1c7 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "format": "prettier --write .", "format:check": "prettier --check .", "docgen": "cargo doc", + "deploy:timelock": "tsx scripts/tasks/deploy/network/deploy-timelock.ts", "deploy:global-ac-role": "tsx scripts/tasks/deploy/network/deploy-global-ac-role.ts", "deploy:global-ac": "tsx scripts/tasks/deploy/network/deploy-global-ac.ts", "deploy:token-ac-role": "tsx scripts/tasks/deploy/deploy-token-ac-role.ts", @@ -31,13 +32,21 @@ "transfer:authority": "tsx scripts/tasks/manage/transfer-authority.ts", "update:data-feed": "tsx scripts/tasks/manage/update-data-feed.ts", "update:manual-feed-price": "tsx scripts/tasks/manage/update-manual-feed-price.ts", - "delegate": "tsx scripts/tasks/manage/delegate.ts" + "delegate": "tsx scripts/tasks/manage/delegate.ts", + "upgrade:calculate-resize": "tsx scripts/tasks/verify/calculate-upgrade-resize.ts", + "upgrade:propose": "tsx scripts/tasks/upgrade/propose-upgrade-program.ts", + "upgrade:execute": "tsx scripts/tasks/upgrade/execute-upgrade-program.ts", + "timelock:cancel-transaction": "tsx scripts/tasks/manage/timelock/cancel-transaction.ts", + "timelock:transfer-upgrade-authority": "tsx scripts/tasks/manage/timelock/transfer-upgrade-authority.ts" }, "dependencies": { "@coral-xyz/anchor": "0.30.1", + "@solana-program/loader-v3": "0.3.0", + "@solana/kit": "6.0.1", "@solana/spl-token": "0.4.9", "@solana/spl-token-metadata": "0.1.6", "@solana/web3.js": "1.95.4", + "@sqds/multisig": "2.1.4", "@switchboard-xyz/on-demand": "1.2.54", "@types/bn.js": "5.1.6", "anchor-bankrun": "0.5.0", @@ -69,4 +78,4 @@ "vitest": "4.0.8" }, "packageManager": "yarn@4.10.3" -} +} \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md index 5ec0ae9..f281f80 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -15,8 +15,9 @@ anchor deploy --provider.cluster Run once per network before deploying any tokens: -1. `yarn deploy:global-ac-role --network ` - Deploy Global AC Role -2. `yarn deploy:global-ac --network ` - Deploy Global AC +1. `yarn deploy:timelock --network ` - Deploy Timelock for upgrade authority +2. `yarn deploy:global-ac-role --network ` - Deploy Global AC Role +3. `yarn deploy:global-ac --network ` - Deploy Global AC ### Token Deployment diff --git a/scripts/configs/loadNetworkConfig.ts b/scripts/configs/loadNetworkConfig.ts new file mode 100644 index 0000000..afc442d --- /dev/null +++ b/scripts/configs/loadNetworkConfig.ts @@ -0,0 +1,15 @@ +import { createUserError } from '@/common/errorHandler'; + +import { NetworkConfig, networkConfigSchema} from './types'; +import { networkConfigs } from './network-config'; + +export function loadNetworkConfig(network: string): NetworkConfig[string] { + const config = networkConfigSchema.parse(networkConfigs); + const networkConfig = config[network]; + + if (!networkConfig) { + throw createUserError(`Network config not found: ${network}`); + } + + return networkConfig; +} diff --git a/scripts/configs/network-config.ts b/scripts/configs/network-config.ts new file mode 100644 index 0000000..eaaa532 --- /dev/null +++ b/scripts/configs/network-config.ts @@ -0,0 +1,18 @@ +import { NetworkConfig } from "./types"; + +export const networkConfigs: NetworkConfig = { + devnet: { + timelock: { + // 15m + delay: 15 * 60, + member: '77F5WP7E9PE3cRbUXGZ8W8S2zvSGvb2WS7QuVGYpavug', + }, + }, + mainnet: { + timelock: { + // 2 days + delay: 2 * 86400, + member: '77F5WP7E9PE3cRbUXGZ8W8S2zvSGvb2WS7QuVGYpavug', + }, + }, +}; \ No newline at end of file diff --git a/scripts/configs/types.ts b/scripts/configs/types.ts index c1dc056..0012e68 100644 --- a/scripts/configs/types.ts +++ b/scripts/configs/types.ts @@ -169,6 +169,11 @@ export const redeemerVaultConfigSchema = z.object({ paymentTokens: z.array(paymentTokenConfigSchema), }); +export const timelockConfigSchema = z.object({ + delay: z.number().int().positive(), + member: publicKeySchema, +}); + export const tokenConfigSchema = z.object({ metadata: tokenMetadataSchema, tokenAuthority: tokenAuthorityConfigSchema, @@ -213,6 +218,13 @@ export const paymentTokenConfigWithNetworksSchema = z.object({ networks: z.record(z.string(), paymentTokenNetworkConfigSchema), }); +export const networkConfigSchema = z.record(z.string(), + z.object({ + timelock: timelockConfigSchema.optional(), + }) +); + +export type NetworkConfig = z.infer; export type DataFeedConfig = z.infer; export type TokenConfig = z.infer; export type TokenConfigWithNetworks = z.infer; diff --git a/scripts/deploy/timelock.ts b/scripts/deploy/timelock.ts new file mode 100644 index 0000000..3e3d529 --- /dev/null +++ b/scripts/deploy/timelock.ts @@ -0,0 +1,462 @@ +import { AccountMeta, AddressLookupTableAccount, Connection, Keypair, PublicKey, SystemProgram, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction } from '@solana/web3.js'; + +import { sendAndWaitForCustomSolanaTxSign } from '@/common/solanaTxHelper'; + +import { CommonParams } from './dataFeed'; +import * as multisig from "@sqds/multisig"; +import { Wallet } from '@coral-xyz/anchor'; + +export interface DeployTimelockConfig { + delay: number; + member: PublicKey; + createKey?: Keypair; +} + +export const getMultisigInfo = async (connection: Connection, multisigPda: PublicKey, required?: T): Promise => { + required ??= true as T; + return await multisig.accounts.Multisig.fromAccountAddress( + connection as any, + multisigPda + ).catch(err => { + if (required) { + throw err; + } + + if (err instanceof Error && err.message.includes('Unable to find Multisig account at') || err.message.includes('Expected to hold a COption')) { + return null; + } + + throw err; + }); +} + +export const wrapTxWithSquadsSigner = async (connection: Connection, { + instructions, + multisigSignerPda, + addressLookupTableAccounts, + member, +}: { + instructions: TransactionInstruction[], + addressLookupTableAccounts?: AddressLookupTableAccount[], + multisigSignerPda: PublicKey, + member: PublicKey +}) => { + // Derive the PDA of the Squads Vault + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigSignerPda, + index: 0, + }); + + // Get deserialized multisig account info + const multisigInfo = await multisig.accounts.Multisig.fromAccountAddress( + connection as any, + multisigSignerPda + ); + // Get the updated transaction index + const newTransactionIndex = BigInt(multisigInfo.transactionIndex.toString()) + 1n; + + const [proposalPda] = multisig.getProposalPda({ + multisigPda: multisigSignerPda, + transactionIndex: newTransactionIndex, + programId: multisig.PROGRAM_ID, + }); + + const [transactionPda] = multisig.getTransactionPda({ + multisigPda: multisigSignerPda, + index: newTransactionIndex, + programId: multisig.PROGRAM_ID, + }); + + const txMessage = new TransactionMessage({ + payerKey: vaultPda, + recentBlockhash: await connection.getLatestBlockhash().then(v => v.blockhash), + instructions: instructions, + }); + + const tx = new Transaction().add( + multisig.instructions.vaultTransactionCreate({ + multisigPda: multisigSignerPda, + transactionIndex: newTransactionIndex, + rentPayer: member, + creator: member, + vaultIndex: 0, + ephemeralSigners: 0, + transactionMessage: txMessage as any, + addressLookupTableAccounts, + }), + multisig.instructions.proposalCreate({ + multisigPda: multisigSignerPda, + transactionIndex: newTransactionIndex, + creator: member, + rentPayer: member, + }), + multisig.instructions.proposalApprove({ + multisigPda: multisigSignerPda, + transactionIndex: newTransactionIndex, + member: member, + }), + ); + + const messageBytes = multisig.utils.transactionMessageToMultisigTransactionMessageBytes({ + message: txMessage as any, + addressLookupTableAccounts, + vaultPda, + }); + + const [messageDecoded] = multisig.types.transactionMessageBeet.deserialize(Buffer.from(messageBytes)); + + const { accountMetas, lookupTableAccounts } = + await multisig.utils.accountsForTransactionExecute({ + connection: connection as any, + message: { + numSigners: messageDecoded.numSigners, + numWritableSigners: messageDecoded.numWritableSigners, + numWritableNonSigners: messageDecoded.numWritableNonSigners, + accountKeys: messageDecoded.accountKeys, + instructions: messageDecoded.instructions.map((instruction) => { + return { + programIdIndex: instruction.programIdIndex, + accountIndexes: new Uint8Array(instruction.accountIndexes), + data: new Uint8Array(instruction.data), + }; + }), + addressTableLookups: messageDecoded.addressTableLookups.map((lookup) => { + return { + accountKey: lookup.accountKey, + writableIndexes: new Uint8Array(lookup.writableIndexes), + readonlyIndexes: new Uint8Array(lookup.readonlyIndexes), + }; + }), + }, + ephemeralSignerBumps: [], + vaultPda, + transactionPda, + programId: multisig.PROGRAM_ID, + }); + + const res = { + instruction: multisig.generated.createVaultTransactionExecuteInstruction( + { + multisig: multisigSignerPda, + member: member, + proposal: proposalPda, + transaction: transactionPda, + anchorRemainingAccounts: accountMetas, + }, + multisig.PROGRAM_ID + ), + lookupTableAccounts, + }; + + const latestBlockhash = await connection.getLatestBlockhash().then(v => v.blockhash); + + const closeTxInstruction = multisig.instructions.vaultTransactionAccountsClose({ + multisigPda: multisigSignerPda, + transactionIndex: newTransactionIndex, + rentCollector: multisigInfo.rentCollector + }); + + const mergeTxs = async () => { + const instructions = [...tx.instructions, res.instruction, closeTxInstruction]; + const txMessage = new TransactionMessage({ + payerKey: member, + recentBlockhash: latestBlockhash, + instructions: instructions, + }); + + return new VersionedTransaction(txMessage.compileToV0Message(res.lookupTableAccounts)); + } + + return mergeTxs(); +} + +export const sendTxWithTimelock = async (connection: Connection, { + instructions, + timelock, + signer, + action +}: { + instructions: TransactionInstruction[]; + timelock: PublicKey; + signer: PublicKey; + action: { type: 'create' } | { type: 'execute', transactionIndex: bigint }; +}) => { + + const payer = signer; + + // Derive the PDA of the Squads Vault + const [vaultPda] = multisig.getVaultPda({ + multisigPda: timelock, + index: 0, + }); + + // Get deserialized multisig account info + const multisigInfo = await getMultisigInfo(connection, timelock); + + if (multisigInfo.members.length !== 1) { + throw new Error('Expected timelock to have only one member, got ' + multisigInfo.members.length); + } + + const multisigSignerPda = multisigInfo.members[0].key; + + const multisigSignerPdaAccount = await getMultisigInfo(connection, multisigSignerPda, false); + + const squadsSigner = multisigSignerPdaAccount !== null; + + if (squadsSigner) { + console.log('Timelock member is a squads signer, timelock txs will be wrapped with squads signer'); + } + + const member = squadsSigner ? multisigSignerPda : payer; + + let tx: Transaction | VersionedTransaction; + let newTransactionIndex: bigint | undefined; + + if (action.type === 'create') { + // Get the updated transaction index + // TODO: get the index from the custom signer + newTransactionIndex = BigInt(multisigInfo.transactionIndex.toString()) + 1n; + + // Build a message with instructions we want to execute + const txMessage = new TransactionMessage({ + payerKey: vaultPda, + recentBlockhash: await connection.getLatestBlockhash().then(v => v.blockhash), + instructions: instructions, + }); + + + const txCreate = new Transaction().add( + multisig.instructions.vaultTransactionCreate({ + multisigPda: timelock, + transactionIndex: newTransactionIndex, + creator: member, + rentPayer: payer, + vaultIndex: 0, + ephemeralSigners: 0, + transactionMessage: txMessage as any, + }), + multisig.instructions.proposalCreate({ + multisigPda: timelock, + transactionIndex: newTransactionIndex, + creator: member, + rentPayer: payer, + }), + multisig.instructions.proposalApprove({ + multisigPda: timelock, + transactionIndex: newTransactionIndex, + member: member, + }), + ); + + tx = txCreate; + + if (squadsSigner) { + tx = await wrapTxWithSquadsSigner(connection, { + instructions: txCreate.instructions, + member: payer, + multisigSignerPda: multisigSignerPda, + }); + } + + } else { + const transactionIndex = action.transactionIndex; + + const [proposalPda] = multisig.getProposalPda({ + multisigPda: timelock, + transactionIndex, + }); + const createdProposal: multisig.accounts.Proposal | null = await multisig.accounts.Proposal.fromAccountAddress(connection as any, proposalPda).catch(err => { + if (err instanceof Error && err.message.includes('Unable to find Proposal account at')) { + return null; + } + throw err; + }); + + if (createdProposal === null) { + throw new Error('Expected proposal to be created, but it was not found'); + } + + if (createdProposal.status.__kind !== 'Approved') { + throw new Error('Expected proposal to be approved, but the status is ' + createdProposal.status.__kind); + } else { + console.log('Proposal is approved, executing transaction'); + } + + const inxExecute = await multisig.instructions.vaultTransactionExecute({ + connection: connection as any, + multisigPda: timelock, + member: member, + transactionIndex, + }) + + tx = new VersionedTransaction(new TransactionMessage({ + payerKey: payer, + recentBlockhash: await connection.getLatestBlockhash().then(v => v.blockhash), + instructions: [inxExecute.instruction], + }).compileToV0Message(inxExecute.lookupTableAccounts)); + + if (squadsSigner) { + tx = await wrapTxWithSquadsSigner(connection, { + instructions: [inxExecute.instruction], + member: payer, + multisigSignerPda: multisigSignerPda, + addressLookupTableAccounts: inxExecute.lookupTableAccounts, + }); + } + } + + return { tx, newTransactionIndex }; +} + +export const getTimelockTransaction = async (connection: Connection, { + timelock, + transactionIndex +}: { + timelock: PublicKey; + transactionIndex: bigint; +}) => { + + const [transactionPda] = multisig.getTransactionPda({ + multisigPda: timelock, + index: transactionIndex, + }); + + const transaction: multisig.accounts.VaultTransaction | null = await multisig.accounts.VaultTransaction.fromAccountAddress(connection as any, transactionPda).catch(err => { + if (err instanceof Error && err.message.includes('Unable to find VaultTransaction account at')) { + return null; + } + throw err; + }); + + const [proposalPda] = multisig.getProposalPda({ + multisigPda: timelock, + transactionIndex: transactionIndex, + }); + + const proposal: multisig.accounts.Proposal | null = await multisig.accounts.Proposal.fromAccountAddress(connection as any, proposalPda).catch(err => { + if (err instanceof Error && err.message.includes('Unable to find Proposal account at')) { + return null; + } + throw err; + }); + + return { proposal, transaction }; +}; + +export function createMultisigCreateV2Instruction( + accounts: multisig.generated.MultisigCreateV2InstructionAccounts, + args: multisig.generated.MultisigCreateV2InstructionArgs +) { + const [data] = multisig.generated.multisigCreateV2Struct.serialize({ + instructionDiscriminator: multisig.generated.multisigCreateV2InstructionDiscriminator, + ...args, + }) + const keys: AccountMeta[] = [ + { + pubkey: accounts.programConfig, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.treasury, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.multisig, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.createKey, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.creator, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + ] + + if (accounts.anchorRemainingAccounts != null) { + for (const acc of accounts.anchorRemainingAccounts) { + keys.push(acc) + } + } + + const ix = new TransactionInstruction({ + programId: multisig.PROGRAM_ID, + keys, + data, + }) + return ix +} + + +export const deployTimelock = async (common: CommonParams, { delay, member, createKey }: DeployTimelockConfig) => { + createKey ??= Keypair.generate(); + const [multisigPda] = multisig.getMultisigPda({ + createKey: createKey.publicKey, + }); + + const programConfigPda = multisig.getProgramConfigPda({})[0]; + + const programConfig = + await multisig.accounts.ProgramConfig.fromAccountAddress( + common.provider.connection as any, + programConfigPda + ); + + const configTreasury = programConfig.treasury; + + const tx = new Transaction().add(createMultisigCreateV2Instruction({ + programConfig: programConfigPda, + treasury: configTreasury, + multisig: multisigPda, + createKey: createKey.publicKey, + creator: common.payer.publicKey, + }, { + args: { + configAuthority: null, + threshold: 1, + members: [{ + key: member, + permissions: multisig.types.Permissions.all(), + }], + timeLock: Number(delay), + rentCollector: common.payer.publicKey, + memo: null, + }, + })); + + + const result = await sendAndWaitForCustomSolanaTxSign(common.provider, tx, [createKey], { + action: 'deployer', + comment: 'Deploy Timelock', + waitForTx: true, + pollingIntervalMs: 1000, + timeoutDurationMs: 120 * 1000, + }); + + if (result.signature) { + console.log(`Transaction signature: ${result.signature}`); + } + + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda, + index: 0, + }); + + return { + multisig: multisigPda as PublicKey, + vault: vaultPda as PublicKey, + }; +}; + diff --git a/scripts/tasks/deploy/deploy-nonce-account.ts b/scripts/tasks/deploy/deploy-nonce-account.ts index 8cd4b09..38363e3 100644 --- a/scripts/tasks/deploy/deploy-nonce-account.ts +++ b/scripts/tasks/deploy/deploy-nonce-account.ts @@ -7,8 +7,7 @@ import { deployNonceAccount } from '../../deploy/nonce-account'; import { getAuthority, getNetwork } from '../../utils/argumentParser'; async function main(provider: AnchorProvider, payer: Wallet, network: string) { - const authorityStr = getAuthority(); - const authority = new PublicKey(authorityStr); + const authority = getAuthority(); console.log(`Deploying nonce account for authority: ${authority.toBase58()}`); console.log(`Network: ${network}`); @@ -25,11 +24,9 @@ async function main(provider: AnchorProvider, payer: Wallet, network: string) { console.log('\nāœ… Nonce account deployed successfully'); console.log(`Authority: ${authority.toBase58()}`); console.log(`Nonce Account: ${nonceAccount.toBase58()}`); - console.log('\nšŸ“ Add this nonce account to the protocol-registry fordefi config:'); - console.log(` nonceAccount: new PublicKey("${nonceAccount.toBase58()}")`); } const network = getNetwork(); executeNetworkScript(network, main, 'deployer'); -// Usage: yarn tsx scripts/tasks/deploy/deploy-nonce-account.ts --authority --network +// Usage: yarn tsx scripts/tasks/deploy/deploy-nonce-account.ts --authority --network diff --git a/scripts/tasks/deploy/network/deploy-timelock.ts b/scripts/tasks/deploy/network/deploy-timelock.ts new file mode 100644 index 0000000..1d349da --- /dev/null +++ b/scripts/tasks/deploy/network/deploy-timelock.ts @@ -0,0 +1,50 @@ +import { AnchorProvider, Wallet } from '@coral-xyz/anchor'; + +import { createUserError } from '@/common/errorHandler'; +import { executeNetworkScript } from '@/common/scriptRunner'; + +import { getTimelockAddress } from '@/scripts/utils/addressQueries'; +import { registerGlobalTimelock } from '@/scripts/utils/addressRegistry'; +import { saveAddressesToFile } from '@/scripts/utils/addressStorage'; +import { getNetwork } from '@/scripts/utils/argumentParser'; +import { loadNetworkConfig } from '@/scripts/configs/loadNetworkConfig'; +import { deployTimelock, DeployTimelockConfig } from '@/scripts/deploy/timelock'; +import { PublicKey } from '@solana/web3.js'; + +async function main(provider: AnchorProvider, payer: Wallet, network: string) { + console.log(`Deploying Timelock for: ${network}, payer: ${payer.publicKey.toBase58()}`); + + const networkConfig = loadNetworkConfig(network); + + if (!networkConfig?.timelock) { + throw createUserError(`Timelock config not found for network ${network}`,); + } + + const existingTimelock = getTimelockAddress(network); + if (existingTimelock) { + console.log('āœ“ Timelock already deployed'); + console.log(`Timelock: ${existingTimelock.multisig.toString()}`); + console.log(`Vault: ${existingTimelock.vault.toString()}`); + return; + } + + const common = { provider, payer, network }; + + const timelockConfig: DeployTimelockConfig = { + delay: networkConfig.timelock.delay, + member: new PublicKey(networkConfig.timelock.member), + }; + + const timelock = await deployTimelock(common, timelockConfig); + + registerGlobalTimelock(network, timelock); + await saveAddressesToFile(); + + console.log('\nāœ… Timelock deployment submitted'); + console.log(`Timelock: ${timelock.multisig.toString()}`); + console.log(`Vault: ${timelock.vault.toString()}`); + return; +} + +const network = getNetwork(); +executeNetworkScript(network, main, 'deployer'); diff --git a/scripts/tasks/manage/timelock/cancel-transaction.ts b/scripts/tasks/manage/timelock/cancel-transaction.ts new file mode 100644 index 0000000..3e45fd5 --- /dev/null +++ b/scripts/tasks/manage/timelock/cancel-transaction.ts @@ -0,0 +1,81 @@ + +import { AnchorProvider, Wallet } from '@coral-xyz/anchor'; + +import { createUserError } from '@/common/errorHandler'; +import { executeNetworkScript } from '@/common/scriptRunner'; + +import { getTimelockAddress } from '@/scripts/utils/addressQueries'; +import { getMultisigTxIndex, getNetwork } from '@/scripts/utils/argumentParser'; +import { Transaction } from '@solana/web3.js'; +import { sendAndWaitForCustomSolanaTxSign } from '@/common/solanaTxHelper'; +import * as multisig from "@sqds/multisig"; +import { getMultisigInfo, wrapTxWithSquadsSigner } from '@/scripts/deploy/timelock'; + +async function main( + provider: AnchorProvider, + payer: Wallet, + network: string, +) { + const multisigTxIndex = getMultisigTxIndex(); + + console.log(`Cancelling transaction ${multisigTxIndex} on network ${network}`); + + const common = { provider, payer, network }; + + const timelock = getTimelockAddress(network); + + if (!timelock) { + throw createUserError(`Timelock not found for network ${network}`,); + } + + const multisigInfo = await getMultisigInfo(common.provider.connection, timelock.multisig); + + const multisigMember = multisigInfo.members[0].key; + const memberMultisigInfo = await getMultisigInfo(common.provider.connection, multisigMember, false); + + const inxs = [ + multisig.instructions.proposalCancel({ + multisigPda: timelock.multisig, + transactionIndex: BigInt(multisigTxIndex), + member: payer.publicKey, + }), + multisig.instructions.vaultTransactionAccountsClose({ + multisigPda: timelock.multisig, + transactionIndex: BigInt(multisigTxIndex), + rentCollector: multisigInfo.rentCollector, + }), + ] + + const tx = memberMultisigInfo ? + await wrapTxWithSquadsSigner( + common.provider.connection, + { + instructions: inxs, + member: payer.publicKey, + multisigSignerPda: multisigMember + }) + : new Transaction().add(...inxs); + + const result = await sendAndWaitForCustomSolanaTxSign(common.provider, tx, [], { + action: 'update-timelock', + comment: `Cancel timelock transaction ${multisigTxIndex} on network ${network}`, + waitForTx: false, + pollingIntervalMs: 1000, + timeoutDurationMs: 120 * 1000, + }); + + console.log(result) + + if (result.signature) { + console.log(`Transaction signature: ${result.signature}`); + } +} + +const network = getNetwork(); + +executeNetworkScript( + network, + (provider, payer, network) => + main(provider, payer as Wallet, network), + 'update-timelock' +); diff --git a/scripts/tasks/manage/timelock/transfer-upgrade-authority.ts b/scripts/tasks/manage/timelock/transfer-upgrade-authority.ts new file mode 100644 index 0000000..c86fc1e --- /dev/null +++ b/scripts/tasks/manage/timelock/transfer-upgrade-authority.ts @@ -0,0 +1,108 @@ + +import { AnchorProvider, Wallet } from '@coral-xyz/anchor'; + +import { createUserError } from '@/common/errorHandler'; +import { executeNetworkScript } from '@/common/scriptRunner'; + +import { getTimelockAddress } from '@/scripts/utils/addressQueries'; +import { getAuthority, getNetwork, getProgram } from '@/scripts/utils/argumentParser'; +import { PublicKey, Transaction } from '@solana/web3.js'; +import { programAddresses } from '@/common/programs'; +import { LOADER_V3_PROGRAM_ADDRESS } from '@solana-program/loader-v3'; +import { sendAndWaitForCustomSolanaTxSign } from '@/common/solanaTxHelper'; +import { getSetAuthorityInstructionIx, getUpgradeAuthority } from '@/scripts/utils/loaderProgramHelpers'; +import { sendTxWithTimelock } from '@/scripts/deploy/timelock'; +import * as multisig from '@sqds/multisig'; + +async function main( + provider: AnchorProvider, + payer: Wallet, + network: string, +) { + let newAuthority = getAuthority(false); + const program = getProgram(); + + const timelock = getTimelockAddress(network); + + if (!timelock) { + throw createUserError(`Timelock not found for network ${network}`,); + } + + if (!newAuthority) { + console.log('Authority not provided, will use timelock vault as new authority'); + newAuthority = timelock.vault; + } + + console.log(`Transferring upgrade authority for program ${program} to ${newAuthority.toBase58()} on network ${network}`); + + const common = { provider, payer, network }; + + const programId = programAddresses[program]; + + if (!programId) { + throw createUserError(`Program id for ${program} not found`,); + } + + const [programDataPda] = PublicKey.findProgramAddressSync( + [programId.toBuffer()], + new PublicKey(LOADER_V3_PROGRAM_ADDRESS) + ); + + const data = await provider.connection.getAccountInfo(programDataPda); + + if (!data) { + throw createUserError(`No program data found for ${program} at address ${programDataPda.toBase58()}`); + } + + const currentAuthority = await getUpgradeAuthority(provider.connection, programId); + + if (!currentAuthority) { + throw createUserError(`No upgrade authority found for ${programId.toBase58()}`); + } + + if (currentAuthority.equals(newAuthority)) { + throw createUserError(`New authority is the same as the current authority`); + } + + if (!newAuthority.equals(timelock.vault) && !newAuthority.equals(payer.publicKey)) { + throw createUserError(`New authority is not the timelock vault or the payer, it's ${newAuthority.toBase58()}`); + } + + const inx = getSetAuthorityInstructionIx({ + bufferOrProgramDataAccount: programDataPda, + currentAuthority: currentAuthority, + newAuthority: newAuthority, + }); + + const { tx, newTransactionIndex } = currentAuthority.equals(timelock.vault) ? await sendTxWithTimelock(common.provider.connection, { + instructions: [inx], + timelock: timelock.multisig, + signer: payer.publicKey, + action: { type: 'create' }, + }) : { tx: new Transaction().add(inx), newTransactionIndex: undefined }; + + const result = await sendAndWaitForCustomSolanaTxSign(common.provider, tx, [], { + action: 'update-timelock', + comment: `Transfer upgrade authority of ${program} to ${newAuthority.toBase58()} on network ${network}`, + waitForTx: false, + pollingIntervalMs: 1000, + timeoutDurationMs: 120 * 1000, + }); + + console.log(`New transaction index: ${newTransactionIndex?.toString()}`); + + console.log(result) + + if (result.signature) { + console.log(`Transaction signature: ${result.signature}`); + } +} + +const network = getNetwork(); + +executeNetworkScript( + network, + (provider, payer, network) => + main(provider, payer as Wallet, network), + 'update-timelock' +); diff --git a/scripts/tasks/upgrade/execute-upgrade-program.ts b/scripts/tasks/upgrade/execute-upgrade-program.ts new file mode 100644 index 0000000..28ecf51 --- /dev/null +++ b/scripts/tasks/upgrade/execute-upgrade-program.ts @@ -0,0 +1,108 @@ + +import { AnchorProvider, Wallet } from '@coral-xyz/anchor'; + +import { createUserError } from '@/common/errorHandler'; +import { executeNetworkScript } from '@/common/scriptRunner'; + +import { getTimelockAddress } from '@/scripts/utils/addressQueries'; +import { getMultisigTxIndex, getNetwork } from '@/scripts/utils/argumentParser'; +import { programAddresses } from '@/common/programs'; +import { sendAndWaitForCustomSolanaTxSign } from '@/common/solanaTxHelper'; +import { getMultisigInfo, getTimelockTransaction, sendTxWithTimelock } from '@/scripts/deploy/timelock'; +import { getUpgradeAuthority } from '@/scripts/utils/loaderProgramHelpers'; +import * as multisig from '@sqds/multisig'; + +async function main( + provider: AnchorProvider, + payer: Wallet, + network: string +) { + const multisigTxIndex = getMultisigTxIndex(); + + console.log(`Executing program upgrade trough the timelock for: ${network}`); + + const existingTimelock = getTimelockAddress(network); + if (!existingTimelock) { + throw createUserError(`Timelock not found for network ${network}`,); + } + + const common = { provider, payer, network }; + + const { transaction, proposal } = await getTimelockTransaction(common.provider.connection, { + timelock: existingTimelock.multisig, + transactionIndex: BigInt(multisigTxIndex), + }); + + const multisigInfo = await getMultisigInfo(common.provider.connection, existingTimelock.multisig); + + + if (!transaction || !proposal) { + throw createUserError(`Transaction or proposal not found for multisig tx index ${multisigTxIndex}. Its either not created or already executed`,); + } + + if (proposal.status.__kind !== 'Approved') { + throw createUserError(`Proposal is not approved for multisig tx index ${multisigTxIndex}`,); + } + + const currentTs = Math.floor(new Date().getTime() / 1000); + + if (BigInt(proposal.status.timestamp.toString()) + BigInt(multisigInfo.timeLock) > BigInt(currentTs)) { + throw createUserError(`Transaction timelock is not yet passed`,); + } + + const programIdIndex = transaction.message.instructions[0].accountIndexes[1]; + const bufferIndex = transaction.message.instructions[0].accountIndexes[2]; + + const programId = transaction.message.accountKeys[programIdIndex]; + const buffer = transaction.message.accountKeys[bufferIndex]; + + console.log(`Program id: ${programId.toBase58()}`); + console.log(`Buffer: ${buffer.toBase58()}`); + + const program = Object.keys(programAddresses).find(key => programAddresses[key as keyof typeof programAddresses].equals(programId)); + + if (!program) { + throw createUserError(`Program id ${programId.toBase58()} not found in program addresses`,); + } + + + console.log(`Program: ${program}`); + + const currentAuthority = await getUpgradeAuthority(common.provider.connection, programId); + + console.log(`Current authority: ${currentAuthority?.toBase58()}`); + + if (!currentAuthority.equals(existingTimelock.vault)) { + throw createUserError(`Current authority is not the timelock vault, it's ${currentAuthority.toBase58()}`); + } + + const { tx } = await sendTxWithTimelock(common.provider.connection, { + instructions: [], + timelock: existingTimelock.multisig, + signer: payer.publicKey, + action: { type: 'execute', transactionIndex: BigInt(multisigTxIndex) }, + }); + + const result = await sendAndWaitForCustomSolanaTxSign(common.provider, tx, [], { + action: 'update-timelock', + comment: `Execute upgrade of ${program} program trough the timelock`, + waitForTx: false, + pollingIntervalMs: 1000, + timeoutDurationMs: 120 * 1000, + }); + + console.log(result) + + if (result.signature) { + console.log(`Transaction signature: ${result.signature}`); + } +} + +const network = getNetwork(); + +executeNetworkScript( + network, + (provider, payer, network) => + main(provider, payer as Wallet, network), + 'update-timelock' +); diff --git a/scripts/tasks/upgrade/propose-upgrade-program.ts b/scripts/tasks/upgrade/propose-upgrade-program.ts new file mode 100644 index 0000000..3f587da --- /dev/null +++ b/scripts/tasks/upgrade/propose-upgrade-program.ts @@ -0,0 +1,121 @@ + +import { AnchorProvider, Wallet } from '@coral-xyz/anchor'; + +import { createUserError } from '@/common/errorHandler'; +import { executeNetworkScript } from '@/common/scriptRunner'; + +import { getTimelockAddress } from '@/scripts/utils/addressQueries'; +import { getAdditionalBytes, getBufferAccount, getNetwork, getProgram } from '@/scripts/utils/argumentParser'; +import { PublicKey, TransactionInstruction } from '@solana/web3.js'; +import { programAddresses } from '@/common/programs'; +import { LOADER_V3_PROGRAM_ADDRESS } from '@solana-program/loader-v3'; +import { sendAndWaitForCustomSolanaTxSign } from '@/common/solanaTxHelper'; +import { sendTxWithTimelock } from '@/scripts/deploy/timelock'; +import { getCloseBufferInx, getExtendProgramInstructionIx, getUpgradeAuthority, getUpgradeInstructionIx } from '@/scripts/utils/loaderProgramHelpers'; +import * as multisig from '@sqds/multisig'; + +async function main( + provider: AnchorProvider, + payer: Wallet, + network: string +) { + const bufferAccount = getBufferAccount(); + const program = getProgram(); + const additionalBytes = getAdditionalBytes(); + + console.log(`Proposing upgrade of ${program} program trough the timelock for: ${network}`); + + const existingTimelock = getTimelockAddress(network); + if (!existingTimelock) { + throw createUserError(`Timelock not found for network ${network}`,); + } + + const common = { provider, payer, network }; + + const programId = programAddresses[program]; + + if (!programId) { + throw createUserError(`Program id for ${program} not found`,); + } + + const [programDataPda] = PublicKey.findProgramAddressSync( + [programId.toBuffer()], + new PublicKey(LOADER_V3_PROGRAM_ADDRESS) + ); + + const data = await provider.connection.getAccountInfo(programDataPda); + + if (!data) { + throw createUserError(`No program data found for ${program} at address ${programDataPda.toBase58()}`); + } + + const currentAuthority = await getUpgradeAuthority(provider.connection, programId); + + + if (!currentAuthority.equals(existingTimelock.vault)) { + throw createUserError(`Current authority is not the timelock vault, it's ${currentAuthority.toBase58()}`); + } + + const instructions: TransactionInstruction[] = []; + + if (additionalBytes > 0) { + // if need to extend the program, extend it + instructions.push(getExtendProgramInstructionIx({ + programId, + programDataPda, + additionalBytes, + payer: existingTimelock.vault, + })); + } + + // upgrade the program + instructions.push(getUpgradeInstructionIx({ + programId, + programDataPda, + bufferAccount, + spillAccount: payer.publicKey, + authority: existingTimelock.vault, + })); + + // close the buffer account to recover the funds + instructions.push(getCloseBufferInx({ + destination: payer.publicKey, + bufferAccount, + authority: existingTimelock.vault, + })); + + const { tx, newTransactionIndex } = await sendTxWithTimelock(common.provider.connection, { + instructions, + timelock: existingTimelock.multisig, + signer: payer.publicKey, + action: { type: 'create' }, + }); + + const result = await sendAndWaitForCustomSolanaTxSign(common.provider, tx, [], { + action: 'update-timelock', + comment: `Propose upgrade of ${program} program trough the timelock. Tx index: ${newTransactionIndex?.toString()}`, + waitForTx: false, + pollingIntervalMs: 1000, + timeoutDurationMs: 120 * 1000, + }); + + if (newTransactionIndex !== undefined) { + console.log(`Timelock transaction index: ${newTransactionIndex.toString()}`); + } + + console.log(result) + + if (result.signature) { + console.log(`Transaction signature: ${result.signature}`); + } +} + +const network = getNetwork(); + + +executeNetworkScript( + network, + (provider, payer, network) => + main(provider, payer as Wallet, network), + 'update-timelock' +); diff --git a/scripts/tasks/verify/calculate-upgrade-resize.ts b/scripts/tasks/verify/calculate-upgrade-resize.ts new file mode 100644 index 0000000..9ce9a77 --- /dev/null +++ b/scripts/tasks/verify/calculate-upgrade-resize.ts @@ -0,0 +1,47 @@ +import { createUserError } from "@/common/errorHandler"; +import { programAddresses } from "@/common/programs"; +import { executeNetworkScript } from "@/common/scriptRunner"; +import { getNetwork, getProgram } from "@/scripts/utils/argumentParser"; +import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; +import { LOADER_V3_PROGRAM_ADDRESS } from "@solana-program/loader-v3"; +import { PublicKey } from "@solana/web3.js"; +import fs from "fs"; + +async function main(provider: AnchorProvider, _payer: Wallet) { + const program = getProgram(); + + const programId = programAddresses[program]; + + if (!programId) { + throw createUserError(`Program id for ${program} not found`,); + } + + const [programDataPda] = PublicKey.findProgramAddressSync( + [programId.toBuffer()], + new PublicKey(LOADER_V3_PROGRAM_ADDRESS) + ); + + const accountInfo = await provider.connection.getAccountInfo(programDataPda); + + if (!accountInfo) { + throw createUserError(`No program data found for ${programId.toBase58()} at address ${programDataPda.toBase58()}`); + } + + const dataLength = accountInfo.data.slice(49).length; + + const newProgramBytes = fs.readFileSync( + `./target/deploy/${program}.so` + ); + + const newProgramLength = newProgramBytes.length; + + const difference = newProgramLength - dataLength; + + const additionalBytes = difference > 0 ? difference : 0; + + console.log(`Bytes difference between new and current program: ${difference} bytes`); + console.log(`Required additional bytes: ${additionalBytes} bytes`); +} + +const network = getNetwork(); +executeNetworkScript(network, main); \ No newline at end of file diff --git a/scripts/utils/addressQueries.ts b/scripts/utils/addressQueries.ts index 4efd3e6..104dbd2 100644 --- a/scripts/utils/addressQueries.ts +++ b/scripts/utils/addressQueries.ts @@ -1,6 +1,6 @@ import { PublicKey } from '@solana/web3.js'; -import { addresses, NetworkAddresses, TokenAddresses, DataFeed } from '@/common/addresses'; +import { addresses, NetworkAddresses, TokenAddresses, DataFeed, TimelockAddresses } from '@/common/addresses'; import { MProduct, PaymentToken } from '@/common/tokenTypes'; // Address getters (return undefined if not found) @@ -25,6 +25,10 @@ export function getAcAddress(network: string): PublicKey | undefined { return addresses[network]?.ac; } +export function getTimelockAddress(network: string): TimelockAddresses | undefined { + return addresses[network]?.timelock; +} + export function getAcRoleGlobalAddress(network: string): PublicKey | undefined { return addresses[network]?.acRoleGlobal; } diff --git a/scripts/utils/addressRegistry.ts b/scripts/utils/addressRegistry.ts index 321180c..5ff8041 100644 --- a/scripts/utils/addressRegistry.ts +++ b/scripts/utils/addressRegistry.ts @@ -1,6 +1,6 @@ import { PublicKey } from '@solana/web3.js'; -import { addresses, TokenAddresses, DataFeed } from '@/common/addresses'; +import { addresses, TokenAddresses, DataFeed, TimelockAddresses } from '@/common/addresses'; import { createUserError } from '@/common/errorHandler'; import { MProduct, PaymentToken } from '@/common/tokenTypes'; @@ -50,6 +50,12 @@ export function registerGlobalAc(network: string, ac: PublicKey): void { networkAddrs.ac = ac; } +export function registerGlobalTimelock(network: string, timelock: TimelockAddresses): void { + ensureNetworkExists(network); + const networkAddrs = addresses[network]; + networkAddrs.timelock = timelock; +} + // Validation helpers function validateValueNotNull(component: string, value: T | null | undefined): void { if (value === null || value === undefined) { diff --git a/scripts/utils/addressStorage.ts b/scripts/utils/addressStorage.ts index 7f5089e..d02e1c5 100644 --- a/scripts/utils/addressStorage.ts +++ b/scripts/utils/addressStorage.ts @@ -86,6 +86,12 @@ function generateNetworkAddressesCode(networkAddrs: NetworkAddresses, indent = ' if (networkAddrs.ac) { parts.push(`ac: ${formatPublicKey(networkAddrs.ac)}`); } + if (networkAddrs.timelock) { + parts.push(`timelock: ${formatObject([ + `multisig: ${formatPublicKey(networkAddrs.timelock.multisig)}`, + `vault: ${formatPublicKey(networkAddrs.timelock.vault)}`, + ])}`); + } if (networkAddrs.tokens && Object.keys(networkAddrs.tokens).length > 0) { const tokenEntries = Object.entries(networkAddrs.tokens) @@ -158,10 +164,16 @@ import { MProduct, PaymentToken } from './tokenTypes'; export interface NetworkAddresses { acRoleGlobal?: PublicKey; ac?: PublicKey; + timelock?: TimelockAddresses; tokens?: Partial>; feeds?: Partial>; } +export interface TimelockAddresses { + multisig: PublicKey; + vault: PublicKey; +} + export interface DataFeed { token?: PublicKey; tokenProgram?: PublicKey; diff --git a/scripts/utils/argumentParser.ts b/scripts/utils/argumentParser.ts index 20f1cba..67ee9de 100644 --- a/scripts/utils/argumentParser.ts +++ b/scripts/utils/argumentParser.ts @@ -5,6 +5,8 @@ import { createUserError } from '@/common/errorHandler'; import { MProduct, isMProduct, PaymentToken, isPaymentToken } from '@/common/tokenTypes'; import { getAvailableNetworks } from './getAvailableNetworks'; +import { PublicKey } from '@solana/web3.js'; +import { programAddresses } from '@/common/programs'; /** Simple cached parser - parses all args once */ let parsedArgs: Record | null = null; @@ -52,6 +54,122 @@ export function getNetwork(): string { return network; } +/** Get network from arguments */ +export function getBufferAccount(): PublicKey { + const argv = getParsedArgs(); + const bufferAccount = (argv.buffer || argv.ba) as string | undefined; + + if (!bufferAccount) { + throw createUserError('Buffer account is required', [ + 'Use --buffer or -b to specify the buffer account', + 'Example: --buffer 33vVYcpTkv7HyEnkFHQVY1ndUSfHNFHxyG9PBqy2MCwmc', + ]); + } + + try { + return new PublicKey(bufferAccount); + } catch { + throw createUserError(`Invalid buffer account '${bufferAccount}'`, [ + 'Must be a valid PublicKey', + ]); + } +} + +/** Get network from arguments */ +export function getProgram(): keyof (typeof programAddresses) { + const argv = getParsedArgs(); + const program = (argv.program || argv.p) as string | undefined; + + if (!program) { + throw createUserError('Program is required', [ + 'Use --program or -p to specify the program', + 'Example: --program access_control', + ]); + } + + const availablePrograms = Object.keys(programAddresses); + + if (!availablePrograms.includes(program)) { + throw createUserError(`Invalid program '${program}'`, [ + `Must be one of: ${availablePrograms.join(', ')}`, + ]); + } + + return program as keyof (typeof programAddresses); +} + + +/** Get network from arguments */ +export function getAdditionalBytes(): number { + const argv = getParsedArgs(); + const additionalBytes = (argv['additional-bytes'] || argv.ab) as string | undefined; + + if (!additionalBytes) { + return 0; + } + + const v = parseInt(additionalBytes); + + if (isNaN(v)) { + throw createUserError(`Invalid additional bytes '${additionalBytes}'`, [ + 'Must be a valid number', + ]); + } + + return v; +} + +/** Get network from arguments */ +export function getMultisigTxIndex(): number { + const argv = getParsedArgs(); + const multisigTxIndex = (argv['multisig-tx-index']) as string | undefined; + + if (!multisigTxIndex) { + throw createUserError('Multisig tx index is required', [ + 'Use --multisig-tx-index to specify the multisig tx index', + 'Example: --multisig-tx-index 1', + ]); + } + + const v = parseInt(multisigTxIndex); + + if (isNaN(v)) { + throw createUserError(`Invalid multisig tx index '${multisigTxIndex}'`, [ + 'Must be a valid number', + ]); + } + + return v; +} + +/** Get network from arguments */ +export function getAuthority(required?: T): T extends true ? PublicKey : PublicKey | undefined { + required ??= true as T; + + const argv = getParsedArgs(); + const authority = (argv['authority']) as string | undefined; + + if (required && !authority) { + throw createUserError('Authority is required', [ + 'Use --authority to specify the authority public key', + 'Example: --authority 33vVYcpTkv7HyEnkFHQVY1ndUSfHNFHxyG9PBqy2MCwmc', + ]); + } + + if(!authority) { + return undefined + } + + try { + return new PublicKey(authority); + } catch { + throw createUserError(`Invalid authority '${authority}'`, [ + 'Must be a valid PublicKey', + ]); + } +} + + /** Get payment token from arguments (optional) */ export function getPaymentToken(): PaymentToken { const argv = getParsedArgs(); @@ -112,17 +230,6 @@ export function getOptionalArg(key: string): string | undefined { return argv[key] as string | undefined; } -/** Get authority public key from arguments */ -export function getAuthority(): string { - const argv = getParsedArgs(); - const authority = argv.authority as string | undefined; - if (!authority) { - throw createUserError('Authority is required', [ - 'Use --authority to specify the authority public key (Fordefi vault address)', - ]); - } - return authority; -} /** Get optional vaults array from arguments */ export function getOptionalVaults(): ('minter' | 'redeemer')[] | undefined { diff --git a/scripts/utils/loaderProgramHelpers.ts b/scripts/utils/loaderProgramHelpers.ts new file mode 100644 index 0000000..5db0c2b --- /dev/null +++ b/scripts/utils/loaderProgramHelpers.ts @@ -0,0 +1,152 @@ +import { createUserError } from "@/common/errorHandler"; +import { getCloseInstruction, getExtendProgramInstruction, getSetAuthorityInstruction, getUpgradeInstruction, LOADER_V3_PROGRAM_ADDRESS } from "@solana-program/loader-v3"; +import { AccountRole, Address, TransactionSigner } from "@solana/kit"; +import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js"; + +export const getUpgradeAuthority = async (connection: Connection, programId: PublicKey): Promise => { + const [programDataPda] = PublicKey.findProgramAddressSync( + [programId.toBuffer()], + new PublicKey(LOADER_V3_PROGRAM_ADDRESS) + ); + + const accountInfo = await connection.getAccountInfo(programDataPda); + + if (!accountInfo) { + throw createUserError(`No program data found for ${programId.toBase58()} at address ${programDataPda.toBase58()}`); + } + + const data = accountInfo.data; + + // state discriminator + const state = data.readUInt32LE(0); + + if (state !== 3) { + throw new Error("Not a ProgramData account"); + } + + const option = data[12]; // 4 + 8 + + let upgradeAuthority: PublicKey | null = null; + + if (option === 1) { + upgradeAuthority = new PublicKey(data.slice(13, 45)); + } + + return upgradeAuthority; +} + +export const getSetAuthorityInstructionIx = ({ + bufferOrProgramDataAccount, + currentAuthority, + newAuthority, +}: { + bufferOrProgramDataAccount: PublicKey; + currentAuthority: PublicKey; + newAuthority: PublicKey; +}) => { + const upgradeIx = getSetAuthorityInstruction({ + bufferOrProgramDataAccount: bufferOrProgramDataAccount.toBase58() as Address, + currentAuthority: currentAuthority.toBase58() as unknown as TransactionSigner, + newAuthority: newAuthority.toBase58() as Address, + }); + + return new TransactionInstruction({ + keys: upgradeIx.accounts.map(v => ({ + isSigner: v.role === AccountRole.READONLY_SIGNER || v.role === AccountRole.WRITABLE_SIGNER, + isWritable: v.role === AccountRole.WRITABLE_SIGNER || v.role === AccountRole.WRITABLE, + pubkey: new PublicKey((v.address as any).publicKey ?? v.address), + })), + programId: new PublicKey(LOADER_V3_PROGRAM_ADDRESS), + data: Buffer.from(upgradeIx.data), + }) +} + + +export const getExtendProgramInstructionIx = ({ + programId, + programDataPda, + additionalBytes, + payer +}: { + programId: PublicKey; + programDataPda: PublicKey; + payer: PublicKey; + additionalBytes: number; +}) => { + const extendIx = getExtendProgramInstruction({ + programAccount: programId.toBase58() as Address, + programDataAccount: programDataPda.toBase58() as Address, + payer: payer.toBase58() as unknown as TransactionSigner, + additionalBytes + }); + + return new TransactionInstruction({ + keys: extendIx.accounts.map(v => ({ + isSigner: v.role === AccountRole.READONLY_SIGNER || v.role === AccountRole.WRITABLE_SIGNER, + isWritable: v.role === AccountRole.WRITABLE_SIGNER || v.role === AccountRole.WRITABLE, + pubkey: new PublicKey((v.address as any).publicKey ?? v.address), + })), + programId: new PublicKey(LOADER_V3_PROGRAM_ADDRESS), + data: Buffer.from(extendIx.data), + }) +} + +export const getUpgradeInstructionIx = ({ + programId, + programDataPda, + bufferAccount, + spillAccount, + authority, +}: { + programId: PublicKey; + programDataPda: PublicKey; + bufferAccount: PublicKey; + spillAccount: PublicKey; + authority: PublicKey; +}) => { + + const upgradeIx = getUpgradeInstruction({ + programAccount: programId.toBase58() as Address, + programDataAccount: programDataPda.toBase58() as Address, + bufferAccount: bufferAccount.toBase58() as Address, + spillAccount: spillAccount.toBase58() as Address, + authority: authority.toBase58() as unknown as TransactionSigner, + }); + + return new TransactionInstruction({ + keys: upgradeIx.accounts.map(v => ({ + isSigner: v.role === AccountRole.READONLY_SIGNER || v.role === AccountRole.WRITABLE_SIGNER, + isWritable: v.role === AccountRole.WRITABLE_SIGNER || v.role === AccountRole.WRITABLE, + pubkey: new PublicKey((v.address as any).publicKey ?? v.address), + })), + programId: new PublicKey(LOADER_V3_PROGRAM_ADDRESS), + data: Buffer.from(upgradeIx.data), + }) +} + +export const getCloseBufferInx = ({ + destination, + bufferAccount, + authority, +}: { + destination: PublicKey; + bufferAccount: PublicKey; + authority: PublicKey; +}) => { + + const closeIx = getCloseInstruction({ + destinationAccount: destination.toBase58() as Address, + bufferOrProgramDataAccount: bufferAccount.toBase58() as Address, + authority: authority.toBase58() as unknown as TransactionSigner, + }); + + return new TransactionInstruction({ + keys: closeIx.accounts.map(v => ({ + isSigner: v.role === AccountRole.READONLY_SIGNER || v.role === AccountRole.WRITABLE_SIGNER, + isWritable: v.role === AccountRole.WRITABLE_SIGNER || v.role === AccountRole.WRITABLE, + pubkey: new PublicKey((v.address as any).publicKey ?? v.address), + })), + programId: new PublicKey(LOADER_V3_PROGRAM_ADDRESS), + data: Buffer.from(closeIx.data), + }) +} \ No newline at end of file diff --git a/test/constants/squads.constant.ts b/test/constants/squads.constant.ts new file mode 100644 index 0000000..21a283e --- /dev/null +++ b/test/constants/squads.constant.ts @@ -0,0 +1,3 @@ +import { PublicKey } from "@solana/web3.js"; + +export const SQUADS_PROGRAM_ID = new PublicKey('SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf'); \ No newline at end of file diff --git a/test/data-feed.test.ts b/test/data-feed.test.ts index a68d524..ff50f03 100644 --- a/test/data-feed.test.ts +++ b/test/data-feed.test.ts @@ -478,7 +478,7 @@ describe('data-feed', () => { const feedUpdatedAtSlot = 348058928n; it('when underlying Switchboard feed is valid', async () => { - const fixture = await vaultsFixture(feedUpdatedAtSlot); + const fixture = await vaultsFixture(undefined, feedUpdatedAtSlot); const feed = await createNewFeed(fixture, { mode: 'switchboard', @@ -512,7 +512,7 @@ describe('data-feed', () => { }); it('should fail: when underlying Switchboard feed is stale', async () => { - const fixture = await vaultsFixture(feedUpdatedAtSlot + 150n); + const fixture = await vaultsFixture(undefined, feedUpdatedAtSlot + 150n); const feed = await createNewFeed(fixture, { mode: 'switchboard', @@ -547,7 +547,7 @@ describe('data-feed', () => { }); it('should fail: when price is > max price', async () => { - const fixture = await vaultsFixture(feedUpdatedAtSlot); + const fixture = await vaultsFixture(undefined, feedUpdatedAtSlot); const feed = await createNewFeed(fixture, { mode: 'switchboard', @@ -582,7 +582,7 @@ describe('data-feed', () => { }); it('should fail: when price is < min price', async () => { - const fixture = await vaultsFixture(feedUpdatedAtSlot); + const fixture = await vaultsFixture(undefined, feedUpdatedAtSlot); const feed = await createNewFeed(fixture, { mode: 'switchboard', diff --git a/test/fixture/ac.fixture.ts b/test/fixture/ac.fixture.ts index caeccdf..e6a1ae0 100644 --- a/test/fixture/ac.fixture.ts +++ b/test/fixture/ac.fixture.ts @@ -6,11 +6,11 @@ import { AccessControl } from 'target/types/access_control'; import ACCESS_CONTROL_IDL from '../../target/idl/access_control.json' with { type: 'json' }; import { AC_ROLES } from '../constants/ac.constants'; import { acRoleToBuffer, generateAcRoleAccount } from '../helpers/ac.helpers'; -import { initBankrun, processTransaction } from '../helpers/common.helpers'; +import { initBankrun, InitBankrunReturnType, processTransaction } from '../helpers/common.helpers'; import { generateAcAccount } from '../helpers/vaults.helpers'; -export const acFixture = async (initSlot?: bigint) => { - const { provider, context, accounts } = await initBankrun(10, initSlot); +export const acFixture = async (fixture?: InitBankrunReturnType, initSlot?: bigint) => { + const { provider, context, accounts } = fixture ?? await initBankrun(10, initSlot); const [authority, ...regularAccounts] = accounts; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/test/fixture/dafa-feed.fixture.ts b/test/fixture/dafa-feed.fixture.ts index 95dcec4..b25bbaf 100644 --- a/test/fixture/dafa-feed.fixture.ts +++ b/test/fixture/dafa-feed.fixture.ts @@ -8,7 +8,7 @@ import DATA_FEED_IDL from '../../target/idl/data_feed.json' with { type: 'json' import { AC_ROLES } from '../constants/ac.constants'; import { DATA_FEED_AC_ROLES } from '../constants/data-feed.constants'; import { acRoleToBuffer, getAccountAcRoleStatePda } from '../helpers/ac.helpers'; -import { formatUnits, parseUnits, processTransaction, toBN } from '../helpers/common.helpers'; +import { formatUnits, InitBankrunReturnType, parseUnits, processTransaction, toBN } from '../helpers/common.helpers'; import { DataFeedMode, generateFeedAcccount, @@ -65,8 +65,8 @@ const initMockedFeeds = async (context: ProgramTestContext) => { }; }; -export const dataFeedFixture = async (initSlot?: bigint) => { - const acF = await acFixture(initSlot); +export const dataFeedFixture = async (fixture?: InitBankrunReturnType, initSlot?: bigint) => { + const acF = await acFixture(fixture, initSlot); const { provider, diff --git a/test/fixture/squads.fixture.ts b/test/fixture/squads.fixture.ts new file mode 100644 index 0000000..90ecbd3 --- /dev/null +++ b/test/fixture/squads.fixture.ts @@ -0,0 +1,135 @@ +import { Program } from '@coral-xyz/anchor'; +import { Keypair, PublicKey, Transaction } from '@solana/web3.js'; + +import { AccessControl } from 'target/types/access_control'; + +import ACCESS_CONTROL_IDL from '../../target/idl/access_control.json' with { type: 'json' }; +import { AC_ROLES } from '../constants/ac.constants'; +import { acRoleToBuffer, generateAcRoleAccount } from '../helpers/ac.helpers'; +import { initBankrun, processTransaction } from '../helpers/common.helpers'; +import { generateAcAccount } from '../helpers/vaults.helpers'; +import { ProgramTestContext } from 'solana-bankrun'; +import { DAY } from '../constants/common.constants'; +import * as multisig from "@sqds/multisig"; +import { SQUADS_PROGRAM_ID } from '../constants/squads.constant'; + +const createMultisig = async (context: ProgramTestContext, { + authority, + timelock = 2n * DAY, + connection, + member +}: { + authority: Keypair, + member?: PublicKey + timelock?: bigint; + connection: any; +}) => { + const createKey = Keypair.generate(); + const [multisigPda] = multisig.getMultisigPda({ + createKey: createKey.publicKey, + }); + + const programConfigPda = multisig.getProgramConfigPda({})[0]; + + const programConfig = + await multisig.accounts.ProgramConfig.fromAccountAddress( + connection, + programConfigPda + ); + + const configTreasury = programConfig.treasury; + + await processTransaction(context, new Transaction().add(multisig.instructions.multisigCreateV2({ + // Must sign the transaction, unless the .rpc method is used. + createKey: createKey.publicKey, + // The creator & fee payer + creator: authority.publicKey, + // The PDA of the multisig you are creating, derived by a random PublicKey + multisigPda, + // Here the config authority will be the system program + configAuthority: null, + // Create without any time-lock + timeLock: Number(timelock), + // List of the members to add to the multisig + members: [{ + // Members Public Key + key: member ?? authority.publicKey, + // Granted Proposer, Voter, and Executor permissions + permissions: multisig.types.Permissions.all(), + }, + + ], + // This means that there needs to be 2 votes for a transaction proposal to be approved + threshold: 1, + // This is for the program config treasury account + treasury: configTreasury, + // Rent reclaim account + rentCollector: null + })), [authority, createKey]); + + return multisigPda as PublicKey; + +} + +export const squadsFixture = async (initSlot?: bigint) => { + const { provider, context, accounts } = await initBankrun(10, initSlot, true); + + const [authority, ...regularAccounts] = accounts; + + + const mockAccounts = [{ + data: Buffer.from('xNJa55CVjD92Raz2HiWPdZHPcd7iP2i7V0ot8H6/wFdsT5Tc2zbKegAAAAAAAAAAPpPXMsRIJCeQ0tu1MaQKvRnxbFXjEyz/UnetydaTq1oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + 'base64'), + publicKey: new PublicKey('BSTq9w3kZwNwpBXJEvTZz2G9ZTNyKBvoSeXMvwb4cNZr'), + owner: SQUADS_PROGRAM_ID, + }] + + for (const account of mockAccounts) { + context.setAccount(account.publicKey, { + data: account.data, + owner: account.owner, + lamports: 10000, + executable: false + }); + } + + + const multisigPda = await createMultisig(context, { + authority, + connection: provider.connection, + }); + + const multisigSignerPda = await createMultisig(context, { + authority, + connection: provider.connection, + timelock: 0n + }); + + const multisigWithSquadsSignerPda = await createMultisig(context, { + authority, + connection: provider.connection, + member: multisigSignerPda + }); + + const squadsConnection = provider.connection as any; + + return { + provider, + context, + authority, + multisigPda, + regularAccounts, + squadsConnection, + accounts, + multisigSignerPda, + multisigWithSquadsSignerPda, + getMutlisigData: async (pda?: PublicKey) => { + return await multisig.accounts.Multisig.fromAccountAddress( + squadsConnection, + pda ?? multisigPda + ); + } + }; +}; + +export type SquadsFixtureReturnType = Awaited>; diff --git a/test/fixture/vaults.fixture.ts b/test/fixture/vaults.fixture.ts index 3c1ecd5..b241233 100644 --- a/test/fixture/vaults.fixture.ts +++ b/test/fixture/vaults.fixture.ts @@ -20,6 +20,7 @@ import { acRoleToBuffer, getAccountAcRoleStatePda } from '../helpers/ac.helpers' import { createMint, getOrCreateAta, + InitBankrunReturnType, parseUnits, processTransaction, toBN, @@ -34,8 +35,8 @@ import { import { dataFeedFixture } from './dafa-feed.fixture'; import { tokenAuthorityFixture } from './token-authority.fixture'; -export const vaultsFixture = async (initSlot?: bigint) => { - const dfFixture = await dataFeedFixture(initSlot); +export const vaultsFixture = async (fixture?: InitBankrunReturnType, initSlot?: bigint) => { + const dfFixture = await dataFeedFixture(fixture, initSlot); const taFixture = await tokenAuthorityFixture(dfFixture); const { diff --git a/test/helpers/common.helpers.ts b/test/helpers/common.helpers.ts index 482df0c..9cb06c0 100644 --- a/test/helpers/common.helpers.ts +++ b/test/helpers/common.helpers.ts @@ -21,11 +21,13 @@ import { Signer, SystemProgram, Transaction, + VersionedTransaction, } from '@solana/web3.js'; import { BankrunProvider } from 'anchor-bankrun'; import BN from 'bn.js'; import { AddedAccount, + AddedProgram, BanksTransactionMeta, Clock, ProgramTestContext, @@ -39,6 +41,7 @@ import { MidasVaults } from '@/target/types/midas_vaults'; import { TokenAuthority } from '@/target/types/token_authority'; import { DEFAULT_PUBKEY } from '../constants/common.constants'; +import { SQUADS_PROGRAM_ID } from '../constants/squads.constant'; // import { ZERO_ADDRESS } from "test/constants/common.constants"; export interface OptionalCommonParams { @@ -57,7 +60,19 @@ export function numToHex(decimalCode: number): string { return hexCode; } -export const initBankrun = async (numAccounts = 10, initSlot?: bigint) => { +export type InitBankrunReturnType = { + context: ProgramTestContext; + provider: BankrunProvider; + accounts: Keypair[]; +}; + +let bunrunReturnCache: InitBankrunReturnType | null = null; + +export const initBankrun = async (numAccounts = 10, initSlot?: bigint, cacheContext = false) => { + if (cacheContext && bunrunReturnCache) { + return bunrunReturnCache; + } + const accounts: Keypair[] = []; const accountsToInject: AddedAccount[] = []; @@ -77,7 +92,11 @@ export const initBankrun = async (numAccounts = 10, initSlot?: bigint) => { }); } - const context = await startAnchor('.', [], [...accountsToInject]); + const context = await startAnchor('.', [{ + name: 'external/squads', + programId: SQUADS_PROGRAM_ID, + }], [...accountsToInject]); + if (initSlot) { await warpToSlot(context, initSlot); } @@ -85,6 +104,12 @@ export const initBankrun = async (numAccounts = 10, initSlot?: bigint) => { anchor.setProvider(provider); + bunrunReturnCache = { + context, + provider, + accounts: bunrunReturnCache?.accounts ?? accounts, + }; + return { context, provider, @@ -118,7 +143,7 @@ export const findPDA = ( export const expectTxReverted = async ( ctx: ProgramTestContext, - transaction: web3.Transaction, + transaction: Transaction | VersionedTransaction, signers: (Keypair | Signer)[], opt?: OptionalCommonParams, ) => { @@ -206,7 +231,7 @@ export const expectEvents = async ( export const expectTxNotReverted = async ( ctx: ProgramTestContext, - transaction: Transaction, + transaction: Transaction | VersionedTransaction, signers: (Keypair | Signer)[], ) => { try { @@ -228,7 +253,7 @@ export const warpToSlot = async (ctx: ProgramTestContext, slot: bigint) => { export const processTransaction = async ( ctx: ProgramTestContext, - transaction: Transaction, + transaction: Transaction | VersionedTransaction, signers: (Keypair | Signer)[], ) => { // Need to generate new blockhash @@ -238,8 +263,12 @@ export const processTransaction = async ( const blockHash = ctx.lastBlockhash; const client = ctx.banksClient; - transaction.recentBlockhash = blockHash; - transaction.sign(...signers); + if (transaction instanceof Transaction) { + transaction.recentBlockhash = blockHash; + transaction.sign(...signers); + } else { + transaction.sign([...signers]); + } return await client.processTransaction(transaction); }; diff --git a/test/squads.test.ts b/test/squads.test.ts new file mode 100644 index 0000000..ce92fad --- /dev/null +++ b/test/squads.test.ts @@ -0,0 +1,357 @@ +import { LAMPORTS_PER_SOL, SystemProgram, Transaction } from "@solana/web3.js"; +import { DAY } from "./constants/common.constants"; +import { squadsFixture } from "./fixture/squads.fixture"; +import { sendSquadsConfigurationTxWithTimelock, sendSquadsTxWithTimelock } from "./testers/squads.testers"; +import * as multisig from "@sqds/multisig"; +import { processTransaction } from "./helpers/common.helpers"; +import { acFixture } from "./fixture/ac.fixture"; +import { grantRole } from "./testers/ac.testers"; +import { AC_ROLES } from "./constants/ac.constants"; +import { acRoleToBuffer } from "./helpers/ac.helpers"; + +describe('Squads multisig', () => { + describe('initializing', () => { + it('should create multisig', async () => { + const { getMutlisigData, authority } = await squadsFixture(); + const data = await getMutlisigData(); + expect(data.members.length).toBe(1); + expect(data.members[0].key.equals(authority.publicKey)).toBe(true); + expect(data.threshold).toBe(1); + expect(data.timeLock).toBe(Number(2n * DAY)); + }); + }); + + describe('timelock', () => { + describe('config transactions', () => { + it('change the timelock configuration when timelock is passed', async () => { + const fixture = await squadsFixture(); + + const { multisigPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + await sendSquadsConfigurationTxWithTimelock(fixture, { + actions: [{ + __kind: 'SetTimeLock', + newTimeLock: 0 + }], + }); + + const data = await getMutlisigData(); + expect(data.timeLock).toBe(0); + }); + + it('change the members configuration when timelock is passed', async () => { + const fixture = await squadsFixture(); + + const { multisigPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + await sendSquadsConfigurationTxWithTimelock(fixture, { + actions: [{ + __kind: 'SetRentCollector', + newRentCollector: regularAccounts[1].publicKey, + }], + }); + + const data = await getMutlisigData(); + expect(data.rentCollector.equals(regularAccounts[1].publicKey)).toBe(true); + }); + + + it('should fail: change the timelock configuration when timelock is not passed', async () => { + const fixture = await squadsFixture(); + + const { multisigPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + await sendSquadsConfigurationTxWithTimelock(fixture, { + actions: [{ + __kind: 'SetTimeLock', + newTimeLock: 0 + }], + waitForTimelock: false + }, { + revertedWithExecute: '0x1785', + }); + }); + + it('should fail: change the members configuration when timelock is not passed', async () => { + const fixture = await squadsFixture(); + + const { multisigPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + await sendSquadsConfigurationTxWithTimelock(fixture, { + actions: [{ + __kind: 'AddMember', + newMember: { + key: regularAccounts[1].publicKey, + permissions: multisig.types.Permissions.all(), + } + }], + waitForTimelock: false + }, { + revertedWithExecute: '0x1785', + }); + }); + }); + + describe('vault transactions', () => { + it('regular sol transfer with timelock', async () => { + const fixture = await squadsFixture(); + const { multisigPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + + const instructions = [ + SystemProgram.transfer({ + fromPubkey: vaultPda, + toPubkey: regularAccounts[0].publicKey, + lamports: LAMPORTS_PER_SOL, + }), + ]; + + await sendSquadsTxWithTimelock(fixture, { + instructions, + }); + }); + + it('custom instruction with timelock', async () => { + const fixture = await squadsFixture(); + const fixtureAc = await acFixture(fixture); + + const { multisigPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + await grantRole(fixtureAc, { + account: vaultPda, + acRole: fixtureAc.acRoleGlobal.publicKey, + role: AC_ROLES.ADMIN, + }); + + + const instructions = [ + await fixtureAc.acProgram.methods.grantRole(acRoleToBuffer(AC_ROLES.UPDATE_ACCOUNT_AC)) + .accounts({ + acRole: fixtureAc.acRoleGlobal.publicKey, + authority: vaultPda, + account: regularAccounts[0].publicKey, + }) + .instruction(), + ]; + + await sendSquadsTxWithTimelock(fixture, { + instructions, + }); + }); + + + it('should fail: sol transfer but timelock is not passed', async () => { + const fixture = await squadsFixture(); + const { multisigPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + + const instructions = [ + SystemProgram.transfer({ + fromPubkey: vaultPda, + toPubkey: regularAccounts[0].publicKey, + lamports: LAMPORTS_PER_SOL, + }), + ]; + + await sendSquadsTxWithTimelock(fixture, { + instructions, + waitForTimelock: false, + }, { + revertedWithExecute: '0x1785', + }); + }); + + it('should fail: sol transfer create from non-member', async () => { + const fixture = await squadsFixture(); + const { multisigPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + + const instructions = [ + SystemProgram.transfer({ + fromPubkey: vaultPda, + toPubkey: regularAccounts[0].publicKey, + lamports: LAMPORTS_PER_SOL, + }), + ]; + + await sendSquadsTxWithTimelock(fixture, { + instructions, + }, { + revertedWithCreate: '0x1775', + fromCreate: regularAccounts[1], + }); + }); + + it('should fail: sol transfer execute from non-member', async () => { + const fixture = await squadsFixture(); + const { multisigPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + + const instructions = [ + SystemProgram.transfer({ + fromPubkey: vaultPda, + toPubkey: regularAccounts[0].publicKey, + lamports: LAMPORTS_PER_SOL, + }), + ]; + + await sendSquadsTxWithTimelock(fixture, { + instructions, + }, { + revertedWithExecute: '0x1775', + fromExecute: regularAccounts[1], + }); + }); + }); + + }); + + describe('squads member', () => { + it('should create and execute tx with squads member', async () => { + const fixture = await squadsFixture(); + const { multisigWithSquadsSignerPda, multisigSignerPda, getMutlisigData, authority, regularAccounts, context } = fixture; + + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigWithSquadsSignerPda as any, + index: 0, + }); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: vaultPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + await processTransaction(context, + new Transaction().add(SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: multisigSignerPda, + lamports: LAMPORTS_PER_SOL, + })), [authority]); + + + const instructions = [ + SystemProgram.transfer({ + fromPubkey: vaultPda, + toPubkey: regularAccounts[0].publicKey, + lamports: LAMPORTS_PER_SOL, + }), + ]; + + await sendSquadsTxWithTimelock(fixture, { + instructions, + multisigPda: multisigWithSquadsSignerPda, + }); + }); + }); +}) \ No newline at end of file diff --git a/test/testers/squads.testers.ts b/test/testers/squads.testers.ts new file mode 100644 index 0000000..df42bdf --- /dev/null +++ b/test/testers/squads.testers.ts @@ -0,0 +1,392 @@ +import { instructions } from "@sqds/multisig"; +import { SquadsFixtureReturnType } from "../fixture/squads.fixture"; +import { AddressLookupTableAccount, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; +import { expectTxNotReverted, expectTxReverted, OptionalCommonParams, timeTravel } from "../helpers/common.helpers"; +import * as multisig from "@sqds/multisig"; +import { DAY } from "../constants/common.constants"; + + +const wrapTxWithSquadsSigner = async (fixture: SquadsFixtureReturnType, { + instructions, + member, + addressLookupTableAccounts, + payer +}: { + instructions: TransactionInstruction[], + addressLookupTableAccounts?: AddressLookupTableAccount[], + member: PublicKey, + payer: PublicKey +}) => { + // Derive the PDA of the Squads Vault + const [vaultPda] = multisig.getVaultPda({ + multisigPda: fixture.multisigSignerPda as any, + index: 0, + }); + + // Get deserialized multisig account info + const multisigInfo = await multisig.accounts.Multisig.fromAccountAddress( + fixture.squadsConnection, + fixture.multisigSignerPda as any + ); + // Get the updated transaction index + const newTransactionIndex = BigInt(multisigInfo.transactionIndex.toString()) + 1n; + + const txMessage = new TransactionMessage({ + payerKey: vaultPda, + recentBlockhash: fixture.context.lastBlockhash, + instructions: instructions, + }); + + const tx = new Transaction().add( + multisig.instructions.vaultTransactionCreate({ + multisigPda: fixture.multisigSignerPda as any, + transactionIndex: newTransactionIndex, + creator: member, + vaultIndex: 0, + ephemeralSigners: 0, + transactionMessage: txMessage as any, + addressLookupTableAccounts, + }), + multisig.instructions.proposalCreate({ + multisigPda: fixture.multisigSignerPda as any, + transactionIndex: newTransactionIndex, + creator: member, + }), + multisig.instructions.proposalApprove({ + multisigPda: fixture.multisigSignerPda as any, + transactionIndex: newTransactionIndex, + member, + }), + ); + + const getTxExecute = async () => { + const { instruction, lookupTableAccounts } = await multisig.instructions.vaultTransactionExecute({ + multisigPda: fixture.multisigSignerPda as any, + member, + transactionIndex: newTransactionIndex, + connection: fixture.squadsConnection, + }); + + const txExecute = new VersionedTransaction(new TransactionMessage({ + payerKey: payer, + recentBlockhash: fixture.context.lastBlockhash, + instructions: [instruction], + }).compileToV0Message(lookupTableAccounts)); + + return txExecute; + } + + + return { txCreate: tx, getTxExecute }; +} + +export const sendSquadsTxWithTimelock = async (fixture: SquadsFixtureReturnType, { + waitForTimelock = true, + instructions, + multisigPda, +}: { + instructions: TransactionInstruction[]; + waitForTimelock?: boolean; + multisigPda?: PublicKey; +}, opt?: { + revertedWithCreate?: string; + revertedWithExecute?: string; + fromCreate?: Keypair; + fromExecute?: Keypair; +}) => { + const { multisigPda: defaultMultisigPda, multisigWithSquadsSignerPda, multisigSignerPda, getMutlisigData, authority, squadsConnection } = fixture; + + const fromCreate = opt?.fromCreate ?? authority; + const fromExecute = opt?.fromExecute ?? authority; + + + multisigPda ??= defaultMultisigPda; + + const squadsSigner = multisigPda.equals(defaultMultisigPda) ? false : true; + + const memberCreate = squadsSigner ? multisigSignerPda : fromCreate.publicKey; + const memberExecute = squadsSigner ? multisigSignerPda : fromExecute.publicKey; + + // Derive the PDA of the Squads Vault + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + // Get deserialized multisig account info + const multisigInfo = await multisig.accounts.Multisig.fromAccountAddress( + squadsConnection, + multisigPda as any + ); + // Get the updated transaction index + const newTransactionIndex = BigInt(multisigInfo.transactionIndex.toString()) + 1n; + + // Build a message with instructions we want to execute + const txMessage = new TransactionMessage({ + payerKey: vaultPda, + recentBlockhash: fixture.context.lastBlockhash, + instructions: instructions, + }); + + + let txCreate = new Transaction().add( + multisig.instructions.vaultTransactionCreate({ + multisigPda: multisigPda as any, + transactionIndex: newTransactionIndex, + creator: memberCreate, + rentPayer: fromCreate.publicKey, + vaultIndex: 0, + ephemeralSigners: 0, + transactionMessage: txMessage as any, + }), + multisig.instructions.proposalCreate({ + multisigPda: multisigPda as any, + transactionIndex: newTransactionIndex, + creator: memberCreate, + rentPayer: fromCreate.publicKey, + }), + multisig.instructions.proposalApprove({ + multisigPda: multisigPda as any, + transactionIndex: newTransactionIndex, + member: memberCreate, + }), + ); + + let getTxCreateExecute: () => Promise; + + if (squadsSigner) { + const res = await wrapTxWithSquadsSigner(fixture, { + instructions: txCreate.instructions, + member: fromCreate.publicKey, + payer: fromCreate.publicKey, + }); + + txCreate = res.txCreate; + getTxCreateExecute = res.getTxExecute; + } + + if (opt?.revertedWithCreate !== undefined) { + await expectTxReverted(fixture.context, txCreate, [authority, fromCreate], { + revertedWith: opt.revertedWithCreate, + }); + return; + } + + await expectTxNotReverted(fixture.context, txCreate, [authority, fromCreate]); + + if (getTxCreateExecute) { + const txCreateExecute = await getTxCreateExecute(); + await expectTxNotReverted(fixture.context, txCreateExecute, [authority, fromCreate]); + + } + + const [proposalPda] = multisig.getProposalPda({ + multisigPda: multisigPda as any, + transactionIndex: newTransactionIndex, + }); + const createdProposal = await multisig.accounts.Proposal.fromAccountAddress(squadsConnection, proposalPda); + + expect(createdProposal.status.__kind).toBe('Approved'); + + const inxExecute = await multisig.instructions.vaultTransactionExecute({ + connection: squadsConnection, + multisigPda: multisigPda as any, + member: memberExecute, + transactionIndex: newTransactionIndex, + }) + + let txExecute: Transaction | VersionedTransaction = new VersionedTransaction(new TransactionMessage({ + payerKey: fromExecute.publicKey, + recentBlockhash: fixture.context.lastBlockhash, + instructions: [inxExecute.instruction], + }).compileToV0Message(inxExecute.lookupTableAccounts)); + + let getTxExecuteExecute: () => Promise; + + if (squadsSigner) { + const res = await wrapTxWithSquadsSigner(fixture, { + instructions: [inxExecute.instruction], + addressLookupTableAccounts: inxExecute.lookupTableAccounts, + member: fromExecute.publicKey, + payer: fromExecute.publicKey, + }); + + txExecute = res.txCreate; + getTxExecuteExecute = res.getTxExecute; + } + + if (waitForTimelock) { + const currentTime = await fixture.context.banksClient.getClock().then(clock => clock.unixTimestamp); + const txApprovedAt = BigInt(createdProposal.status.__kind === 'Approved' ? createdProposal.status.timestamp.toString() : 0); + const timelockDuration = 2n * DAY; + const timelockEndsAt = txApprovedAt + timelockDuration; + const deltaTime = timelockEndsAt - BigInt(currentTime); + await timeTravel(fixture.context, deltaTime); + } + + + if (opt?.revertedWithExecute !== undefined) { + await expectTxReverted(fixture.context, txExecute, [fromExecute], { + revertedWith: opt.revertedWithExecute, + }); + return; + } + + await expectTxNotReverted(fixture.context, txExecute, [fromExecute]); + + if (getTxExecuteExecute) { + const txExecuteExecute = await getTxExecuteExecute(); + await expectTxNotReverted(fixture.context, txExecuteExecute, [fromExecute]); + } + + const proposalStatusAfter = await multisig.accounts.Proposal.fromAccountAddress(squadsConnection, proposalPda); + expect(proposalStatusAfter.status.__kind).toBe('Executed'); +} + +export const sendSquadsConfigurationTxWithTimelock = async (fixture: SquadsFixtureReturnType, { + waitForTimelock = true, + actions, + multisigPda, +}: { + actions: multisig.types.ConfigAction[]; + waitForTimelock?: boolean; + multisigPda?: PublicKey; +}, opt?: { + revertedWithCreate?: string; + revertedWithExecute?: string; + fromCreate?: Keypair; + fromExecute?: Keypair; +}) => { + const { multisigPda: defaultMultisigPda, multisigSignerPda, getMutlisigData, authority, squadsConnection } = fixture; + + const fromCreate = opt?.fromCreate ?? authority; + const fromExecute = opt?.fromExecute ?? authority; + + + multisigPda ??= defaultMultisigPda; + + const squadsSigner = multisigPda.equals(defaultMultisigPda) ? false : true; + + const member = squadsSigner ? multisigSignerPda : authority.publicKey; + + // Derive the PDA of the Squads Vault + const [vaultPda] = multisig.getVaultPda({ + multisigPda: multisigPda as any, + index: 0, + }); + + // Get deserialized multisig account info + const multisigInfo = await multisig.accounts.Multisig.fromAccountAddress( + squadsConnection, + multisigPda as any + ); + // Get the updated transaction index + const newTransactionIndex = BigInt(multisigInfo.transactionIndex.toString()) + 1n; + + let txCreate = new Transaction().add( + multisig.instructions.configTransactionCreate({ + multisigPda: multisigPda as any, + transactionIndex: newTransactionIndex, + creator: member, + rentPayer: fromCreate.publicKey, + actions, + }), + multisig.instructions.proposalCreate({ + multisigPda: multisigPda as any, + transactionIndex: newTransactionIndex, + creator: member, + rentPayer: fromCreate.publicKey, + }), + multisig.instructions.proposalApprove({ + multisigPda: multisigPda as any, + transactionIndex: newTransactionIndex, + member, + }), + ); + + let getTxCreateExecute: () => Promise; + + if (squadsSigner) { + const res = await wrapTxWithSquadsSigner(fixture, { + instructions: txCreate.instructions, + member: fromCreate.publicKey, + payer: fromCreate.publicKey, + }); + + txCreate = res.txCreate; + getTxCreateExecute = res.getTxExecute; + } + + if (opt?.revertedWithCreate !== undefined) { + await expectTxReverted(fixture.context, txCreate, [authority, fromCreate], { + revertedWith: opt.revertedWithCreate, + }); + return; + } + + await expectTxNotReverted(fixture.context, txCreate, [authority, fromCreate]); + + if (getTxCreateExecute) { + const txCreateExecute = await getTxCreateExecute(); + await expectTxNotReverted(fixture.context, txCreateExecute, [authority, fromCreate]); + + } + + const [proposalPda] = multisig.getProposalPda({ + multisigPda: multisigPda as any, + transactionIndex: newTransactionIndex, + }); + const createdProposal = await multisig.accounts.Proposal.fromAccountAddress(squadsConnection, proposalPda); + + expect(createdProposal.status.__kind).toBe('Approved'); + + + if (waitForTimelock) { + const currentTime = await fixture.context.banksClient.getClock().then(clock => clock.unixTimestamp); + const txApprovedAt = BigInt(createdProposal.status.__kind === 'Approved' ? createdProposal.status.timestamp.toString() : 0); + const timelockDuration = 2n * DAY; + const timelockEndsAt = txApprovedAt + timelockDuration; + const deltaTime = timelockEndsAt - BigInt(currentTime); + await timeTravel(fixture.context, deltaTime + 1n); + } + + + const inxExecute = multisig.instructions.configTransactionExecute({ + multisigPda: multisigPda as any, + member, + transactionIndex: newTransactionIndex, + }) + + let txExecute = new Transaction().add(inxExecute); + + let getTxExecuteExecute: () => Promise; + + if (squadsSigner) { + const res = await wrapTxWithSquadsSigner(fixture, { + instructions: txExecute.instructions, + member: fromExecute.publicKey, + payer: fromExecute.publicKey, + }); + + txExecute = res.txCreate; + getTxExecuteExecute = res.getTxExecute; + } + + + if (opt?.revertedWithExecute !== undefined) { + await expectTxReverted(fixture.context, txExecute, [authority, fromExecute], { + revertedWith: opt.revertedWithExecute, + }); + return; + } + + await expectTxNotReverted(fixture.context, txExecute, [authority, fromExecute]); + + if (getTxExecuteExecute) { + const txExecuteExecute = await getTxExecuteExecute(); + await expectTxNotReverted(fixture.context, txExecuteExecute, [authority, fromExecute]); + } + + const proposalStatusAfter = await multisig.accounts.Proposal.fromAccountAddress(squadsConnection, proposalPda); + expect(proposalStatusAfter.status.__kind).toBe('Executed'); +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4621cb4..ac9f564 100644 --- a/yarn.lock +++ b/yarn.lock @@ -447,6 +447,48 @@ __metadata: languageName: node linkType: hard +"@metaplex-foundation/beet-solana@npm:0.4.0": + version: 0.4.0 + resolution: "@metaplex-foundation/beet-solana@npm:0.4.0" + dependencies: + "@metaplex-foundation/beet": "npm:>=0.1.0" + "@solana/web3.js": "npm:^1.56.2" + bs58: "npm:^5.0.0" + debug: "npm:^4.3.4" + checksum: 10c0/52a3b66e51e7c326c3a106cc01129e11ab12e44bfc503725ffbff8f7adfbafd226f4b6e4095a8ae5dc350b88c7156d599ffc44733b5e34ca18d3278b5fb9b3b2 + languageName: node + linkType: hard + +"@metaplex-foundation/beet@npm:0.7.1": + version: 0.7.1 + resolution: "@metaplex-foundation/beet@npm:0.7.1" + dependencies: + ansicolors: "npm:^0.3.2" + bn.js: "npm:^5.2.0" + debug: "npm:^4.3.3" + checksum: 10c0/9b643f519261eac9dab49291a0b66a1ec21189dd90c40a0a3d28197f4b0c7af465848164ab7ed995c72c81c656665bd21f7e71a66d9cb72257ea997f17c1ed19 + languageName: node + linkType: hard + +"@metaplex-foundation/beet@npm:>=0.1.0": + version: 0.7.2 + resolution: "@metaplex-foundation/beet@npm:0.7.2" + dependencies: + ansicolors: "npm:^0.3.2" + assert: "npm:^2.1.0" + bn.js: "npm:^5.2.0" + debug: "npm:^4.3.3" + checksum: 10c0/38c2eb1664b0becf7d1afaa331b8406df5632a7593b8be953bc44f397dadc96c4a1b9b3e0e22aebfd5f8a0c8756cf6172cd76cf29dff5fc723408786da84ebc1 + languageName: node + linkType: hard + +"@metaplex-foundation/cusper@npm:^0.0.2": + version: 0.0.2 + resolution: "@metaplex-foundation/cusper@npm:0.0.2" + checksum: 10c0/0bd4cc82526df04f9db7505c473ea502b816791d8f392fef9f779ed17ee5e8b6375947578039873dcd1c973596b01423acf2cef0a8c240cb2a7ead5c2373e009 + languageName: node + linkType: hard + "@napi-rs/wasm-runtime@npm:^0.2.11": version: 0.2.12 resolution: "@napi-rs/wasm-runtime@npm:0.2.12" @@ -799,6 +841,66 @@ __metadata: languageName: node linkType: hard +"@solana-program/loader-v3@npm:0.3.0": + version: 0.3.0 + resolution: "@solana-program/loader-v3@npm:0.3.0" + peerDependencies: + "@solana/kit": ^6.0.0 + checksum: 10c0/b1acdf7591f96b41e99b3dbd4b64eb77887a4e4c954e35046bc89402acc8c76f7938e8f9be7decb6aaf6bbb9c6c04fce8b3d761f5803d6cddb5294b69d9ee528 + languageName: node + linkType: hard + +"@solana/accounts@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/accounts@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/rpc-spec": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/462af5a698ac9f134f42e20211ef9219c742e511a599465c5a9b86b870fb7bb041bb1c5910389b124d8d63c59efbee6373ccac0a25ee445c18778736f0b4dc47 + languageName: node + linkType: hard + +"@solana/addresses@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/addresses@npm:6.0.1" + dependencies: + "@solana/assertions": "npm:6.0.1" + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/nominal-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/bf8047bde16b08d92be7cb3707041e94372b22c038fd99b97c8ce6ce3dc9705257b46a7cf1f3c76f5b35e32da7be3a7d5f86ea9acf58d6e5a4094ade75216b00 + languageName: node + linkType: hard + +"@solana/assertions@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/assertions@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/caf275e5a5accff387a4498867ab47dcf8c81cf37474609a81cde09091e96e2b67abb65ae8f7d642615b96dd62c8b23ee43b4032a2112be4f25b4d693f38a537 + languageName: node + linkType: hard + "@solana/buffer-layout-utils@npm:^0.2.0": version: 0.2.0 resolution: "@solana/buffer-layout-utils@npm:0.2.0" @@ -831,6 +933,31 @@ __metadata: languageName: node linkType: hard +"@solana/codecs-core@npm:2.3.0": + version: 2.3.0 + resolution: "@solana/codecs-core@npm:2.3.0" + dependencies: + "@solana/errors": "npm:2.3.0" + peerDependencies: + typescript: ">=5.3.3" + checksum: 10c0/efef080b94fe572bcfeac9f1c0b222700203bd2b45c9590e77445b35335d0ed2582d1cc4e533003d2090c385c06eb93dfa05388f9766182aa60ce85eacfd8042 + languageName: node + linkType: hard + +"@solana/codecs-core@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/codecs-core@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/357f74215d3d60cd1b2342a1b0b270acd5c5de587a15bf549f4da4ebe2fbee51d9fb09e345b0f69b0cebdb4ce935e04f8f54df7b0573b150023b565c595737df + languageName: node + linkType: hard + "@solana/codecs-data-structures@npm:2.0.0-rc.1": version: 2.0.0-rc.1 resolution: "@solana/codecs-data-structures@npm:2.0.0-rc.1" @@ -844,6 +971,22 @@ __metadata: languageName: node linkType: hard +"@solana/codecs-data-structures@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/codecs-data-structures@npm:6.0.1" + dependencies: + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-numbers": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/c88dab11c674aa8ce0be5ebbfa592405b8613f82e954df4fe93ec7543ef1b5555e587a598060f87877221c7dd9a074b413e93a44a97650b58dd9e7b602c1e216 + languageName: node + linkType: hard + "@solana/codecs-numbers@npm:2.0.0-rc.1": version: 2.0.0-rc.1 resolution: "@solana/codecs-numbers@npm:2.0.0-rc.1" @@ -856,6 +999,33 @@ __metadata: languageName: node linkType: hard +"@solana/codecs-numbers@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/codecs-numbers@npm:6.0.1" + dependencies: + "@solana/codecs-core": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/ff5c7d44c31537e946f244b7d2d5f0518ab8d6003f59d39d842438c285e158c5f775b4e68b74285565515a27966d6f854c11318c05614a2c20f07057aae4d22b + languageName: node + linkType: hard + +"@solana/codecs-numbers@npm:^2.1.0": + version: 2.3.0 + resolution: "@solana/codecs-numbers@npm:2.3.0" + dependencies: + "@solana/codecs-core": "npm:2.3.0" + "@solana/errors": "npm:2.3.0" + peerDependencies: + typescript: ">=5.3.3" + checksum: 10c0/0780d60771e451cfe22ea614315fed2f37507aa62f83cddb900186f88d4d4532eea298d74796d1dbc8c34321a570b5d9ada25e8f4a5aeadd57aa4e688b4465f5 + languageName: node + linkType: hard + "@solana/codecs-strings@npm:2.0.0-rc.1": version: 2.0.0-rc.1 resolution: "@solana/codecs-strings@npm:2.0.0-rc.1" @@ -870,6 +1040,25 @@ __metadata: languageName: node linkType: hard +"@solana/codecs-strings@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/codecs-strings@npm:6.0.1" + dependencies: + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-numbers": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: ^5.0.0 + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true + checksum: 10c0/dadab2e1b2ae53aac8423516faa2e2b256757ad708d517d42a83c531f9a5ba6a6a0d5399e245042247bffdc9cecd092644e2b25c9615afb1c4c65194f06eeedd + languageName: node + linkType: hard + "@solana/codecs@npm:2.0.0-rc.1": version: 2.0.0-rc.1 resolution: "@solana/codecs@npm:2.0.0-rc.1" @@ -885,6 +1074,24 @@ __metadata: languageName: node linkType: hard +"@solana/codecs@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/codecs@npm:6.0.1" + dependencies: + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-data-structures": "npm:6.0.1" + "@solana/codecs-numbers": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/options": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/b097870b5369f3cc829255895f986b29efd502c1fac4225c9e0361909367f5b581f6d4074441e24ae01282c4820c3ad8ad51c065a4668a6e45c26954e1867825 + languageName: node + linkType: hard + "@solana/errors@npm:2.0.0-rc.1": version: 2.0.0-rc.1 resolution: "@solana/errors@npm:2.0.0-rc.1" @@ -899,6 +1106,181 @@ __metadata: languageName: node linkType: hard +"@solana/errors@npm:2.3.0": + version: 2.3.0 + resolution: "@solana/errors@npm:2.3.0" + dependencies: + chalk: "npm:^5.4.1" + commander: "npm:^14.0.0" + peerDependencies: + typescript: ">=5.3.3" + bin: + errors: bin/cli.mjs + checksum: 10c0/55bef8828b4a6bb5222d3dbfe27162684906ba90753126b9cfd1e8e39c6c29209c0f4f331cfb1d3d1cf43fd456022af92337b4234a145d8de292588197c12c71 + languageName: node + linkType: hard + +"@solana/errors@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/errors@npm:6.0.1" + dependencies: + chalk: "npm:5.6.2" + commander: "npm:14.0.3" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + bin: + errors: bin/cli.mjs + checksum: 10c0/1e479d2ad0971fd713d94dc79855fd564ad9f1d36618ea4396f293d4c7842e824d70b70b5536cb014cb5c0d08f2cd4d9efc2382007484deb3316a98f419be20f + languageName: node + linkType: hard + +"@solana/fast-stable-stringify@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/fast-stable-stringify@npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/9997b1e4b82a2c1ed63106e9c5599329f96f62178e7dea25a6b596b8cb23b2427ee77c56e6a7cf13b61f2eafc091eb9a82b94aa83709e548ddb4ee0b951418a9 + languageName: node + linkType: hard + +"@solana/functional@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/functional@npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/690343ee581b9853873b6fa9a65707e3d8bd8a89e2ba8f97c830b111e7ec66b44e871e21784e22937c81d76196bed77432798ba477415e424aa1dc165dbaebc5 + languageName: node + linkType: hard + +"@solana/instruction-plans@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/instruction-plans@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + "@solana/instructions": "npm:6.0.1" + "@solana/keys": "npm:6.0.1" + "@solana/promises": "npm:6.0.1" + "@solana/transaction-messages": "npm:6.0.1" + "@solana/transactions": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/7032415351b8fa5740d01051c070b47be671e5052e36f2687b29018f000aa748212f43cf65c2c714c9112833d71fb4296ebec3290071db4cf1f43fbdc2544024 + languageName: node + linkType: hard + +"@solana/instructions@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/instructions@npm:6.0.1" + dependencies: + "@solana/codecs-core": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/9f0b0b1ad449d480fd64ea38f1f1e8c2e481badbcd1cad6ca5404332a14e21ecd5be34121885156eb91f51bbb9d07814e120e36e3692fbe1ce7efc833d74be4d + languageName: node + linkType: hard + +"@solana/keys@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/keys@npm:6.0.1" + dependencies: + "@solana/assertions": "npm:6.0.1" + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/nominal-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/85c07bc6722cdb37a9810faa838da0ba2ce5ebd1c7113747f0f6ff4bcc983c4ef913a29abf1099fa51eeb66ea6a1c5cd51b85d68af591f70f3e30c0a669d01e7 + languageName: node + linkType: hard + +"@solana/kit@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/kit@npm:6.0.1" + dependencies: + "@solana/accounts": "npm:6.0.1" + "@solana/addresses": "npm:6.0.1" + "@solana/codecs": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/functional": "npm:6.0.1" + "@solana/instruction-plans": "npm:6.0.1" + "@solana/instructions": "npm:6.0.1" + "@solana/keys": "npm:6.0.1" + "@solana/offchain-messages": "npm:6.0.1" + "@solana/plugin-core": "npm:6.0.1" + "@solana/programs": "npm:6.0.1" + "@solana/rpc": "npm:6.0.1" + "@solana/rpc-api": "npm:6.0.1" + "@solana/rpc-parsed-types": "npm:6.0.1" + "@solana/rpc-spec-types": "npm:6.0.1" + "@solana/rpc-subscriptions": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + "@solana/signers": "npm:6.0.1" + "@solana/sysvars": "npm:6.0.1" + "@solana/transaction-confirmation": "npm:6.0.1" + "@solana/transaction-messages": "npm:6.0.1" + "@solana/transactions": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/daaa2b2ebc914b97c8f921c4c3e5531eeb8ec5a8984e882be5ac095e6f7ae6cc2e383c4c771bfbb05548740ecb1153a719cf5682db899d44b32b997d01210200 + languageName: node + linkType: hard + +"@solana/nominal-types@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/nominal-types@npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/c7e071cb1d0a7ba8efed71678c0e80fb6beb6e3b157abcab56cd2e967e4c6aef935e05a2cbb1ff2e035be78c16edfd3fd8a962c651a2de5909780f8c5919d676 + languageName: node + linkType: hard + +"@solana/offchain-messages@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/offchain-messages@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-data-structures": "npm:6.0.1" + "@solana/codecs-numbers": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/keys": "npm:6.0.1" + "@solana/nominal-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/5432a0c0226434d4f0b97936d0f11593fd94bd0446a53b62d3c50bd25d1396059e628f9442d743974c396e204f5acb9e2318080bf9db11c17c84cfef666001aa + languageName: node + linkType: hard + "@solana/options@npm:2.0.0-rc.1": version: 2.0.0-rc.1 resolution: "@solana/options@npm:2.0.0-rc.1" @@ -914,6 +1296,303 @@ __metadata: languageName: node linkType: hard +"@solana/options@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/options@npm:6.0.1" + dependencies: + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-data-structures": "npm:6.0.1" + "@solana/codecs-numbers": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/f82c0da1ccd0f9083b48efa9b13a99747537a9e559e5d57692ff8b92bc946518c853fbd3aca29c71749b362ae8a21726ed91c4a2fbecfcf494d794e8cb0e2eda + languageName: node + linkType: hard + +"@solana/plugin-core@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/plugin-core@npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/f3869d7202963ad28c1215e6ce1ef65cccc49b129df69f1b4f1e75d30fc8cb28062d666798cfa392eabbf95ef29079f9a244015ae0d551b55df4a850543ccb81 + languageName: node + linkType: hard + +"@solana/programs@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/programs@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/478c3dedcaf9e8569acc99b5b6c7ff2b472301f6c358d3791277d69856b5b16fbdd4eed50518b92154ba748c64e0059e432e0923266aadc32b140ef19568825e + languageName: node + linkType: hard + +"@solana/promises@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/promises@npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/41f509c3cbb1dfd5ed1ad3d86c53368f2b8ec8f1fb04f03c756c4a311d05f0e2076c19bd3572ad8d91be557d5dd376feb323436c185b4288f9edfd3162913766 + languageName: node + linkType: hard + +"@solana/rpc-api@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-api@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/keys": "npm:6.0.1" + "@solana/rpc-parsed-types": "npm:6.0.1" + "@solana/rpc-spec": "npm:6.0.1" + "@solana/rpc-transformers": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + "@solana/transaction-messages": "npm:6.0.1" + "@solana/transactions": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/c0814dc4be9384c9d2fb300d658fb4d2b95392be083b8439374bf811667e06ece5c42dd2f42f8f013fad0e64103f9f3028687006de1ce29643237df8915ab222 + languageName: node + linkType: hard + +"@solana/rpc-parsed-types@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-parsed-types@npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/be4a2f7adbb07d77bba9b455ad4685d16cf9c8e6a5c6bc7b2d1ee355ccc635af7351ee9b22c6824bcf21918c54cc5609bc483793eb1e0fb2c7c3f71f60f8735a + languageName: node + linkType: hard + +"@solana/rpc-spec-types@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-spec-types@npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/ba403297d1e359f654423bb11ba9c0b3f923b1e09bd8e41e643d15a0e0acb849ea0b57f753fa704c9bc503ae97d8f87448d54221ade2f99154c03b9efc5f40e8 + languageName: node + linkType: hard + +"@solana/rpc-spec@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-spec@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + "@solana/rpc-spec-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/bfb90e7f1ca601f8f1908b1e0d20b41668dda69cc310518f3778522b1aad67aae35a59cf3a4835d28673427a872631da97e975e94b5c76e8a0c3e3a3b959077d + languageName: node + linkType: hard + +"@solana/rpc-subscriptions-api@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-subscriptions-api@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/keys": "npm:6.0.1" + "@solana/rpc-subscriptions-spec": "npm:6.0.1" + "@solana/rpc-transformers": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + "@solana/transaction-messages": "npm:6.0.1" + "@solana/transactions": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/e2329564ccae02180b993372d4172a395a4c16f9ee6ccccf216cb7f50d3e6bbca174802f8ddd4fa38550933ffaaf0ef89165c0e0912ede2da840316812d86e1c + languageName: node + linkType: hard + +"@solana/rpc-subscriptions-channel-websocket@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-subscriptions-channel-websocket@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + "@solana/functional": "npm:6.0.1" + "@solana/rpc-subscriptions-spec": "npm:6.0.1" + "@solana/subscribable": "npm:6.0.1" + ws: "npm:^8.19.0" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/ffc53118d80fe60d1596dd5c6c3fb3183b2656f4ddc376d5b6955459f38007f5e12a6557a0bbe60b329b5d22036a57bd0e06a5b36b0b7195474cf53ca61084f8 + languageName: node + linkType: hard + +"@solana/rpc-subscriptions-spec@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-subscriptions-spec@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + "@solana/promises": "npm:6.0.1" + "@solana/rpc-spec-types": "npm:6.0.1" + "@solana/subscribable": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/d1b261f644ad53faff221f69f64f0c6431c0bd3a9f6b96f0e9d65b42f9f418ede7183760021f3ca6dc69c26060e1823286736c4491f984f425ee3a6955fa0cef + languageName: node + linkType: hard + +"@solana/rpc-subscriptions@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-subscriptions@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + "@solana/fast-stable-stringify": "npm:6.0.1" + "@solana/functional": "npm:6.0.1" + "@solana/promises": "npm:6.0.1" + "@solana/rpc-spec-types": "npm:6.0.1" + "@solana/rpc-subscriptions-api": "npm:6.0.1" + "@solana/rpc-subscriptions-channel-websocket": "npm:6.0.1" + "@solana/rpc-subscriptions-spec": "npm:6.0.1" + "@solana/rpc-transformers": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + "@solana/subscribable": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/654ce2f2c1872f357592cd788ed53d8c70a6b1b2d78e7fe608316ea9d21899f5fcfaa961cbbf9bb12acf974db3302ac5044303069c6fdb008e8a62717fdbc700 + languageName: node + linkType: hard + +"@solana/rpc-transformers@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-transformers@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + "@solana/functional": "npm:6.0.1" + "@solana/nominal-types": "npm:6.0.1" + "@solana/rpc-spec-types": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/0d7af662d342121c431b35e86fa2d937b6b8981d1e288571e8109f0042202f542a3e95c42d93e6783c41d8bdefdf4e5368402b59b9c39177e2996ed80536e88c + languageName: node + linkType: hard + +"@solana/rpc-transport-http@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-transport-http@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + "@solana/rpc-spec": "npm:6.0.1" + "@solana/rpc-spec-types": "npm:6.0.1" + undici-types: "npm:^7.20.0" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/6377a6a3cd2c5bb3e318e2071482c6090285d8e91ab31cc93f586ee76ef479156ff5abe6c1ae360951f02764075580ee20fbbf1bf2488a2f3e2fc6c0d7ed7f3f + languageName: node + linkType: hard + +"@solana/rpc-types@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc-types@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-numbers": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/nominal-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/a0eec838585541a797326d19a4f3826b546ff070cca86139ac047393d6ea9c8c1cfb7fca5b360a64e2bd0532d55529d81ec5209f67f9f814adcc37be9003aca5 + languageName: node + linkType: hard + +"@solana/rpc@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/rpc@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + "@solana/fast-stable-stringify": "npm:6.0.1" + "@solana/functional": "npm:6.0.1" + "@solana/rpc-api": "npm:6.0.1" + "@solana/rpc-spec": "npm:6.0.1" + "@solana/rpc-spec-types": "npm:6.0.1" + "@solana/rpc-transformers": "npm:6.0.1" + "@solana/rpc-transport-http": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/b986da6ac2f2482cc2810f8a70e5c1b329f3b9e20cb1905dace187aae0d67e7d36f0397fb1ff2918d0bc9469083f4ff11927172689ab372a4f0f84dd9d8e4ee9 + languageName: node + linkType: hard + +"@solana/signers@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/signers@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/codecs-core": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/instructions": "npm:6.0.1" + "@solana/keys": "npm:6.0.1" + "@solana/nominal-types": "npm:6.0.1" + "@solana/offchain-messages": "npm:6.0.1" + "@solana/transaction-messages": "npm:6.0.1" + "@solana/transactions": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/7eaa0257dddeba5aad1db30803e7688fa5845d4c85a87ec958fc0fe2ec24a5c37c932e9e865eefb6397d07f62a7c1afed4cb97d9fa074c4f51c058e986f6bd6e + languageName: node + linkType: hard + "@solana/spl-token-group@npm:^0.0.7": version: 0.0.7 resolution: "@solana/spl-token-group@npm:0.0.7" @@ -946,22 +1625,123 @@ __metadata: "@solana/spl-token-metadata": "npm:^0.1.6" buffer: "npm:^6.0.3" peerDependencies: - "@solana/web3.js": ^1.95.3 - checksum: 10c0/66f22a026fbc34a5e28391fc75c9a902e852fadf6538d18464b4f036d95d75ecccb2d96f07cbebdc6cb530558566c4296e613761969431fef549ec10e8d4024f + "@solana/web3.js": ^1.95.3 + checksum: 10c0/66f22a026fbc34a5e28391fc75c9a902e852fadf6538d18464b4f036d95d75ecccb2d96f07cbebdc6cb530558566c4296e613761969431fef549ec10e8d4024f + languageName: node + linkType: hard + +"@solana/spl-token@npm:^0.3.4, @solana/spl-token@npm:^0.3.6": + version: 0.3.11 + resolution: "@solana/spl-token@npm:0.3.11" + dependencies: + "@solana/buffer-layout": "npm:^4.0.0" + "@solana/buffer-layout-utils": "npm:^0.2.0" + "@solana/spl-token-metadata": "npm:^0.1.2" + buffer: "npm:^6.0.3" + peerDependencies: + "@solana/web3.js": ^1.88.0 + checksum: 10c0/eae23e1ced21c8cc117a43d4c6e1b1f0f9aa412619e22f7fed96e15a833e350dddd6ec1b1498f5274a9542d8b0fdbd9ce93a5b79ed77863f7170951aeacb18fe + languageName: node + linkType: hard + +"@solana/subscribable@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/subscribable@npm:6.0.1" + dependencies: + "@solana/errors": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/59f409b485a42e22d0ccc6d1af4681a9ecd14e23bb095ffd440ce41668cc746247bff54739324723aaca7e0140b1ed24dcd25b883328a1f8e84d02746a486136 + languageName: node + linkType: hard + +"@solana/sysvars@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/sysvars@npm:6.0.1" + dependencies: + "@solana/accounts": "npm:6.0.1" + "@solana/codecs": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/052301c8e153fdd080fedebeaf3b432bd3c23e6510ac36df9f78c4d8725b5874f41ca15ba2ddc65082e017ecf53324f61f2ddcf17b0e3d0b06947df4ffc65b0a + languageName: node + linkType: hard + +"@solana/transaction-confirmation@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/transaction-confirmation@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/keys": "npm:6.0.1" + "@solana/promises": "npm:6.0.1" + "@solana/rpc": "npm:6.0.1" + "@solana/rpc-subscriptions": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + "@solana/transaction-messages": "npm:6.0.1" + "@solana/transactions": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/2e5ecc5f020c22433229da297690f7d9b9923edbe4f5b6a8530ee582d08a0cc95c313fc9c7c05b0e5fa00270762870ee37175e2f09fdbee33f2d9ca92dd63d82 + languageName: node + linkType: hard + +"@solana/transaction-messages@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/transaction-messages@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-data-structures": "npm:6.0.1" + "@solana/codecs-numbers": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/functional": "npm:6.0.1" + "@solana/instructions": "npm:6.0.1" + "@solana/nominal-types": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/705ab940d14dbc1c748599d1b52a61ad6d601271d224346f69b55bcd8caf5a2450bc949334f540979281a6a05d0d5051b6eef945a93e3e5463773e1277a2bc9e languageName: node linkType: hard -"@solana/spl-token@npm:^0.3.4": - version: 0.3.11 - resolution: "@solana/spl-token@npm:0.3.11" - dependencies: - "@solana/buffer-layout": "npm:^4.0.0" - "@solana/buffer-layout-utils": "npm:^0.2.0" - "@solana/spl-token-metadata": "npm:^0.1.2" - buffer: "npm:^6.0.3" +"@solana/transactions@npm:6.0.1": + version: 6.0.1 + resolution: "@solana/transactions@npm:6.0.1" + dependencies: + "@solana/addresses": "npm:6.0.1" + "@solana/codecs-core": "npm:6.0.1" + "@solana/codecs-data-structures": "npm:6.0.1" + "@solana/codecs-numbers": "npm:6.0.1" + "@solana/codecs-strings": "npm:6.0.1" + "@solana/errors": "npm:6.0.1" + "@solana/functional": "npm:6.0.1" + "@solana/instructions": "npm:6.0.1" + "@solana/keys": "npm:6.0.1" + "@solana/nominal-types": "npm:6.0.1" + "@solana/rpc-types": "npm:6.0.1" + "@solana/transaction-messages": "npm:6.0.1" peerDependencies: - "@solana/web3.js": ^1.88.0 - checksum: 10c0/eae23e1ced21c8cc117a43d4c6e1b1f0f9aa412619e22f7fed96e15a833e350dddd6ec1b1498f5274a9542d8b0fdbd9ce93a5b79ed77863f7170951aeacb18fe + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/ffa03c5978cfa33d8d929f7473202a8ed91d8f3c03ada147ce9f4037a8db8399892a84ba989cd486ec4f7d8dd993cd21a759164bcbf3d49ce18943ecec1fa69d languageName: node linkType: hard @@ -988,6 +1768,29 @@ __metadata: languageName: node linkType: hard +"@solana/web3.js@npm:^1.56.2, @solana/web3.js@npm:^1.70.3": + version: 1.98.4 + resolution: "@solana/web3.js@npm:1.98.4" + dependencies: + "@babel/runtime": "npm:^7.25.0" + "@noble/curves": "npm:^1.4.2" + "@noble/hashes": "npm:^1.4.0" + "@solana/buffer-layout": "npm:^4.0.1" + "@solana/codecs-numbers": "npm:^2.1.0" + agentkeepalive: "npm:^4.5.0" + bn.js: "npm:^5.2.1" + borsh: "npm:^0.7.0" + bs58: "npm:^4.0.1" + buffer: "npm:6.0.3" + fast-stable-stringify: "npm:^1.0.0" + jayson: "npm:^4.1.1" + node-fetch: "npm:^2.7.0" + rpc-websockets: "npm:^9.0.2" + superstruct: "npm:^2.0.2" + checksum: 10c0/73bf7b6b5b65c7f264587182bbfd65327775b4f3e4831750de6356f58858e57d49213098eec671650940bb7a9bbaa1f352e0710c4075f126d903d72ddddcbdbc + languageName: node + linkType: hard + "@solana/web3.js@npm:^1.95.3, @solana/web3.js@npm:^1.95.8": version: 1.95.8 resolution: "@solana/web3.js@npm:1.95.8" @@ -1036,6 +1839,24 @@ __metadata: languageName: node linkType: hard +"@sqds/multisig@npm:2.1.4": + version: 2.1.4 + resolution: "@sqds/multisig@npm:2.1.4" + dependencies: + "@metaplex-foundation/beet": "npm:0.7.1" + "@metaplex-foundation/beet-solana": "npm:0.4.0" + "@metaplex-foundation/cusper": "npm:^0.0.2" + "@solana/spl-token": "npm:^0.3.6" + "@solana/web3.js": "npm:^1.70.3" + "@types/bn.js": "npm:^5.1.1" + assert: "npm:^2.0.0" + bn.js: "npm:^5.2.1" + buffer: "npm:6.0.3" + invariant: "npm:2.2.4" + checksum: 10c0/a70e608a4b8db84b44778bb8da8671be6f7d7e4c02e0ea8a713fb61a80cdf36957eeb448ea522000ecce99edb3ca1c768c2b73b24d7ec6ef37d64fd843f00233 + languageName: node + linkType: hard + "@standard-schema/spec@npm:^1.0.0": version: 1.0.0 resolution: "@standard-schema/spec@npm:1.0.0" @@ -1107,6 +1928,15 @@ __metadata: languageName: node linkType: hard +"@types/bn.js@npm:^5.1.1": + version: 5.2.0 + resolution: "@types/bn.js@npm:5.2.0" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/7a36114b8e61faba5c28b433c3e5aabded261745dabb8f3fe41b2d84e8c4c2b8282e52a88a842bd31a565ff5dbf685145ccd91171f1a8d657fb249025c17aa85 + languageName: node + linkType: hard + "@types/chai@npm:5.0.1": version: 5.0.1 resolution: "@types/chai@npm:5.0.1" @@ -1731,6 +2561,13 @@ __metadata: languageName: node linkType: hard +"ansicolors@npm:^0.3.2": + version: 0.3.2 + resolution: "ansicolors@npm:0.3.2" + checksum: 10c0/e202182895e959c5357db6c60791b2abaade99fcc02221da11a581b26a7f83dc084392bc74e4d3875c22f37b3c9ef48842e896e3bfed394ec278194b8003e0ac + languageName: node + linkType: hard + "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -1738,6 +2575,19 @@ __metadata: languageName: node linkType: hard +"assert@npm:^2.0.0, assert@npm:^2.1.0": + version: 2.1.0 + resolution: "assert@npm:2.1.0" + dependencies: + call-bind: "npm:^1.0.2" + is-nan: "npm:^1.3.2" + object-is: "npm:^1.1.5" + object.assign: "npm:^4.1.4" + util: "npm:^0.12.5" + checksum: 10c0/7271a5da883c256a1fa690677bf1dd9d6aa882139f2bed1cd15da4f9e7459683e1da8e32a203d6cc6767e5e0f730c77a9532a87b896b4b0af0dd535f668775f0 + languageName: node + linkType: hard + "assertion-error@npm:^2.0.1": version: 2.0.1 resolution: "assertion-error@npm:2.0.1" @@ -1745,6 +2595,20 @@ __metadata: languageName: node linkType: hard +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 10c0/669a32c2cb7e45091330c680e92eaeb791bc1d4132d827591e499cd1f776ff5a873e77e5f92d0ce795a8d60f10761dec9ddfe7225a5de680f5d357f67b1aac73 + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 10c0/2c50ef856c543ad500d8d8777d347e3c1ba623b93e99c9263ecc5f965c1b12d2a140e2ab6e43c3d0b85366110696f28114649411cbcd10b452a92a2318394186 + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -1752,6 +2616,15 @@ __metadata: languageName: node linkType: hard +"available-typed-arrays@npm:^1.0.7": + version: 1.0.7 + resolution: "available-typed-arrays@npm:1.0.7" + dependencies: + possible-typed-array-names: "npm:^1.0.0" + checksum: 10c0/d07226ef4f87daa01bd0fe80f8f310982e345f372926da2e5296aecc25c41cab440916bbaa4c5e1034b453af3392f67df5961124e4b586df1e99793a1374bdb2 + languageName: node + linkType: hard + "axios@npm:^1.7.8": version: 1.7.9 resolution: "axios@npm:1.7.9" @@ -1953,6 +2826,38 @@ __metadata: languageName: node linkType: hard +"call-bind-apply-helpers@npm:^1.0.0, call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10c0/47bd9901d57b857590431243fea704ff18078b16890a6b3e021e12d279bbf211d039155e27d7566b374d49ee1f8189344bac9833dec7a20cdec370506361c938 + languageName: node + linkType: hard + +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": + version: 1.0.8 + resolution: "call-bind@npm:1.0.8" + dependencies: + call-bind-apply-helpers: "npm:^1.0.0" + es-define-property: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.2" + checksum: 10c0/a13819be0681d915144467741b69875ae5f4eba8961eb0bf322aab63ec87f8250eb6d6b0dcbb2e1349876412a56129ca338592b3829ef4343527f5f18a0752d4 + languageName: node + linkType: hard + +"call-bound@npm:^1.0.2, call-bound@npm:^1.0.3, call-bound@npm:^1.0.4": + version: 1.0.4 + resolution: "call-bound@npm:1.0.4" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + get-intrinsic: "npm:^1.3.0" + checksum: 10c0/f4796a6a0941e71c766aea672f63b72bc61234c4f4964dc6d7606e3664c307e7d77845328a8f3359ce39ddb377fed67318f9ee203dea1d47e46165dcf2917644 + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -1987,6 +2892,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:5.6.2, chalk@npm:^5.4.1": + version: 5.6.2 + resolution: "chalk@npm:5.6.2" + checksum: 10c0/99a4b0f0e7991796b1e7e3f52dceb9137cae2a9dfc8fc0784a550dc4c558e15ab32ed70b14b21b52beb2679b4892b41a0aa44249bcb996f01e125d58477c6976 + languageName: node + linkType: hard + "chalk@npm:^4.0.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -2054,6 +2966,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:14.0.3, commander@npm:^14.0.0": + version: 14.0.3 + resolution: "commander@npm:14.0.3" + checksum: 10c0/755652564bbf56ff2ff083313912b326450d3f8d8c85f4b71416539c9a05c3c67dbd206821ca72635bf6b160e2afdefcb458e86b317827d5cb333b69ce7f1a24 + languageName: node + linkType: hard + "commander@npm:^12.1.0": version: 12.1.0 resolution: "commander@npm:12.1.0" @@ -2116,7 +3035,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.1, debug@npm:^4.4.3": +"debug@npm:4, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.4.1, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -2161,6 +3080,28 @@ __metadata: languageName: node linkType: hard +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10c0/dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 + languageName: node + linkType: hard + +"define-properties@npm:^1.1.3, define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" + dependencies: + define-data-property: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.0" + object-keys: "npm:^1.1.1" + checksum: 10c0/88a152319ffe1396ccc6ded510a3896e77efac7a1bfbaa174a7b00414a1747377e0bb525d303794a47cf30e805c2ec84e575758512c6e44a993076d29fd4e6c3 + languageName: node + linkType: hard + "delay@npm:^5.0.0": version: 5.0.0 resolution: "delay@npm:5.0.0" @@ -2192,6 +3133,17 @@ __metadata: languageName: node linkType: hard +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -2243,6 +3195,20 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.0, es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10c0/3f54eb49c16c18707949ff25a1456728c883e81259f045003499efba399c08bad00deebf65cccde8c0e07908c1a225c9d472b7107e558f2a48e28d530e34527c + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + "es-module-lexer@npm:^1.7.0": version: 1.7.0 resolution: "es-module-lexer@npm:1.7.0" @@ -2250,6 +3216,15 @@ __metadata: languageName: node linkType: hard +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10c0/65364812ca4daf48eb76e2a3b7a89b3f6a2e62a1c420766ce9f692665a29d94fe41fe88b65f24106f449859549711e4b40d9fb8002d862dfd7eb1c512d10be0c + languageName: node + linkType: hard + "es6-promise@npm:^4.0.3": version: 4.2.8 resolution: "es6-promise@npm:4.2.8" @@ -2763,6 +3738,15 @@ __metadata: languageName: node linkType: hard +"for-each@npm:^0.3.5": + version: 0.3.5 + resolution: "for-each@npm:0.3.5" + dependencies: + is-callable: "npm:^1.2.7" + checksum: 10c0/0e0b50f6a843a282637d43674d1fb278dda1dd85f4f99b640024cfb10b85058aac0cc781bf689d5fe50b4b7f638e91e548560723a4e76e04fe96ae35ef039cee + languageName: node + linkType: hard + "foreground-child@npm:^3.1.0": version: 3.3.1 resolution: "foreground-child@npm:3.3.1" @@ -2812,6 +3796,20 @@ __metadata: languageName: node linkType: hard +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 10c0/8a9f59df0f01cfefafdb3b451b80555e5cf6d76487095db91ac461a0e682e4ff7a9dbce15f4ecec191e53586d59eece01949e05a4b4492879600bbbe8e28d6b8 + languageName: node + linkType: hard + "get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" @@ -2826,6 +3824,37 @@ __metadata: languageName: node linkType: hard +"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.3.0": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: "npm:^1.0.0" + async-generator-function: "npm:^1.0.0" + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + generator-function: "npm:^2.0.0" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10c0/9f4ab0cf7efe0fd2c8185f52e6f637e708f3a112610c88869f8f041bb9ecc2ce44bf285dfdbdc6f4f7c277a5b88d8e94a432374d97cca22f3de7fc63795deb5d + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c + languageName: node + linkType: hard + "get-tsconfig@npm:^4.10.1, get-tsconfig@npm:^4.7.5": version: 4.13.0 resolution: "get-tsconfig@npm:4.13.0" @@ -2883,6 +3912,13 @@ __metadata: languageName: node linkType: hard +"gopd@npm:^1.0.1, gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10c0/50fff1e04ba2b7737c097358534eacadad1e68d24cccee3272e04e007bed008e68d2614f3987788428fd192a5ae3889d08fb2331417e4fc4a9ab366b2043cead + languageName: node + linkType: hard + "graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -2904,6 +3940,40 @@ __metadata: languageName: node linkType: hard +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10c0/253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10c0/dde0a734b17ae51e84b10986e651c664379018d10b91b6b0e9b293eddb32f0f069688c841fb40f19e9611546130153e0a2a48fd7f512891fb000ddfa36f5a20e + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c + languageName: node + linkType: hard + +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.2.0 resolution: "http-cache-semantics@npm:4.2.0" @@ -2994,6 +4064,15 @@ __metadata: languageName: node linkType: hard +"invariant@npm:2.2.4": + version: 2.2.4 + resolution: "invariant@npm:2.2.4" + dependencies: + loose-envify: "npm:^1.0.0" + checksum: 10c0/5af133a917c0bcf65e84e7f23e779e7abc1cd49cb7fdc62d00d1de74b0d8c1b5ee74ac7766099fb3be1b05b26dfc67bab76a17030d2fe7ea2eef867434362dfc + languageName: node + linkType: hard + "ip-address@npm:^10.0.1": version: 10.0.1 resolution: "ip-address@npm:10.0.1" @@ -3001,6 +4080,16 @@ __metadata: languageName: node linkType: hard +"is-arguments@npm:^1.0.4": + version: 1.2.0 + resolution: "is-arguments@npm:1.2.0" + dependencies: + call-bound: "npm:^1.0.2" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/6377344b31e9fcb707c6751ee89b11f132f32338e6a782ec2eac9393b0cbd32235dad93052998cda778ee058754860738341d8114910d50ada5615912bb929fc + languageName: node + linkType: hard + "is-bun-module@npm:^2.0.0": version: 2.0.0 resolution: "is-bun-module@npm:2.0.0" @@ -3010,6 +4099,13 @@ __metadata: languageName: node linkType: hard +"is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 10c0/ceebaeb9d92e8adee604076971dd6000d38d6afc40bb843ea8e45c5579b57671c3f3b50d7f04869618242c6cee08d1b67806a8cb8edaaaf7c0748b3720d6066f + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -3024,6 +4120,19 @@ __metadata: languageName: node linkType: hard +"is-generator-function@npm:^1.0.7": + version: 1.1.2 + resolution: "is-generator-function@npm:1.1.2" + dependencies: + call-bound: "npm:^1.0.4" + generator-function: "npm:^2.0.0" + get-proto: "npm:^1.0.1" + has-tostringtag: "npm:^1.0.2" + safe-regex-test: "npm:^1.1.0" + checksum: 10c0/83da102e89c3e3b71d67b51d47c9f9bc862bceb58f87201727e27f7fa19d1d90b0ab223644ecaee6fc6e3d2d622bb25c966fbdaf87c59158b01ce7c0fe2fa372 + languageName: node + linkType: hard + "is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": version: 4.0.3 resolution: "is-glob@npm:4.0.3" @@ -3033,6 +4142,16 @@ __metadata: languageName: node linkType: hard +"is-nan@npm:^1.3.2": + version: 1.3.2 + resolution: "is-nan@npm:1.3.2" + dependencies: + call-bind: "npm:^1.0.0" + define-properties: "npm:^1.1.3" + checksum: 10c0/8bfb286f85763f9c2e28ea32e9127702fe980ffd15fa5d63ade3be7786559e6e21355d3625dd364c769c033c5aedf0a2ed3d4025d336abf1b9241e3d9eddc5b0 + languageName: node + linkType: hard + "is-number@npm:^7.0.0": version: 7.0.0 resolution: "is-number@npm:7.0.0" @@ -3040,6 +4159,27 @@ __metadata: languageName: node linkType: hard +"is-regex@npm:^1.2.1": + version: 1.2.1 + resolution: "is-regex@npm:1.2.1" + dependencies: + call-bound: "npm:^1.0.2" + gopd: "npm:^1.2.0" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10c0/1d3715d2b7889932349241680032e85d0b492cfcb045acb75ffc2c3085e8d561184f1f7e84b6f8321935b4aea39bc9c6ba74ed595b57ce4881a51dfdbc214e04 + languageName: node + linkType: hard + +"is-typed-array@npm:^1.1.3": + version: 1.1.15 + resolution: "is-typed-array@npm:1.1.15" + dependencies: + which-typed-array: "npm:^1.1.16" + checksum: 10c0/415511da3669e36e002820584e264997ffe277ff136643a3126cc949197e6ca3334d0f12d084e83b1994af2e9c8141275c741cf2b7da5a2ff62dd0cac26f76c4 + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -3114,6 +4254,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^3.0.0 || ^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed + languageName: node + linkType: hard + "js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" @@ -3232,6 +4379,17 @@ __metadata: languageName: node linkType: hard +"loose-envify@npm:^1.0.0": + version: 1.4.0 + resolution: "loose-envify@npm:1.4.0" + dependencies: + js-tokens: "npm:^3.0.0 || ^4.0.0" + bin: + loose-envify: cli.js + checksum: 10c0/655d110220983c1a4b9c0c679a2e8016d4b67f6e9c7b5435ff5979ecdb20d0813f4dec0a08674fcbdd4846a3f07edbb50a36811fd37930b94aaa0d9daceb017e + languageName: node + linkType: hard + "loupe@npm:^3.1.0": version: 3.1.2 resolution: "loupe@npm:3.1.2" @@ -3283,6 +4441,13 @@ __metadata: languageName: node linkType: hard +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f + languageName: node + linkType: hard + "merge2@npm:^1.3.0": version: 1.4.1 resolution: "merge2@npm:1.4.1" @@ -3551,6 +4716,37 @@ __metadata: languageName: node linkType: hard +"object-is@npm:^1.1.5": + version: 1.1.6 + resolution: "object-is@npm:1.1.6" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + checksum: 10c0/506af444c4dce7f8e31f34fc549e2fb8152d6b9c4a30c6e62852badd7f520b579c679af433e7a072f9d78eb7808d230dc12e1cf58da9154dfbf8813099ea0fe0 + languageName: node + linkType: hard + +"object-keys@npm:^1.1.1": + version: 1.1.1 + resolution: "object-keys@npm:1.1.1" + checksum: 10c0/b11f7ccdbc6d406d1f186cdadb9d54738e347b2692a14439ca5ac70c225fa6db46db809711b78589866d47b25fc3e8dee0b4c722ac751e11180f9380e3d8601d + languageName: node + linkType: hard + +"object.assign@npm:^4.1.4": + version: 4.1.7 + resolution: "object.assign@npm:4.1.7" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + has-symbols: "npm:^1.1.0" + object-keys: "npm:^1.1.1" + checksum: 10c0/3b2732bd860567ea2579d1567525168de925a8d852638612846bd8082b3a1602b7b89b67b09913cbb5b9bd6e95923b2ae73580baa9d99cb4e990564e8cbf5ddc + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.4 resolution: "optionator@npm:0.9.4" @@ -3692,6 +4888,13 @@ __metadata: languageName: node linkType: hard +"possible-typed-array-names@npm:^1.0.0": + version: 1.1.0 + resolution: "possible-typed-array-names@npm:1.1.0" + checksum: 10c0/c810983414142071da1d644662ce4caebce890203eb2bc7bf119f37f3fe5796226e117e6cca146b521921fa6531072674174a3325066ac66fce089a53e1e5196 + languageName: node + linkType: hard + "postcss@npm:^8.5.6": version: 8.5.6 resolution: "postcss@npm:8.5.6" @@ -3919,9 +5122,12 @@ __metadata: dependencies: "@coral-xyz/anchor": "npm:0.30.1" "@eslint/js": "npm:9.39.1" + "@solana-program/loader-v3": "npm:0.3.0" + "@solana/kit": "npm:6.0.1" "@solana/spl-token": "npm:0.4.9" "@solana/spl-token-metadata": "npm:0.1.6" "@solana/web3.js": "npm:1.95.4" + "@sqds/multisig": "npm:2.1.4" "@switchboard-xyz/on-demand": "npm:1.2.54" "@types/bn.js": "npm:5.1.6" "@types/chai": "npm:5.0.1" @@ -3989,6 +5195,17 @@ __metadata: languageName: node linkType: hard +"safe-regex-test@npm:^1.1.0": + version: 1.1.0 + resolution: "safe-regex-test@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + is-regex: "npm:^1.2.1" + checksum: 10c0/f2c25281bbe5d39cddbbce7f86fca5ea9b3ce3354ea6cd7c81c31b006a5a9fff4286acc5450a3b9122c56c33eba69c56b9131ad751457b2b4a585825e6a10665 + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -4005,6 +5222,20 @@ __metadata: languageName: node linkType: hard +"set-function-length@npm:^1.2.2": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -4465,6 +5696,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:^7.20.0": + version: 7.22.0 + resolution: "undici-types@npm:7.22.0" + checksum: 10c0/5e6f2513c41d07404c719eb7c1c499b8d4cde042f1269b3bc2be335f059e6ef53eb04f316696c728d5e8064c4d522b98f63d7ae8938adf6317351e07ae9c943e + languageName: node + linkType: hard + "undici-types@npm:~5.26.4": version: 5.26.5 resolution: "undici-types@npm:5.26.5" @@ -4597,6 +5835,19 @@ __metadata: languageName: node linkType: hard +"util@npm:^0.12.5": + version: 0.12.5 + resolution: "util@npm:0.12.5" + dependencies: + inherits: "npm:^2.0.3" + is-arguments: "npm:^1.0.4" + is-generator-function: "npm:^1.0.7" + is-typed-array: "npm:^1.1.3" + which-typed-array: "npm:^1.1.2" + checksum: 10c0/c27054de2cea2229a66c09522d0fa1415fb12d861d08523a8846bf2e4cbf0079d4c3f725f09dcb87493549bcbf05f5798dce1688b53c6c17201a45759e7253f3 + languageName: node + linkType: hard + "uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" @@ -4769,6 +6020,21 @@ __metadata: languageName: node linkType: hard +"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.2": + version: 1.1.20 + resolution: "which-typed-array@npm:1.1.20" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.4" + for-each: "npm:^0.3.5" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/16fcdada95c8afb821cd1117f0ab50b4d8551677ac08187f21d4e444530913c9ffd2dac634f0c1183345f96344b69280f40f9a8bc52164ef409e555567c2604b + languageName: node + linkType: hard + "which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -4873,6 +6139,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^8.19.0": + version: 8.19.0 + resolution: "ws@npm:8.19.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/4741d9b9bc3f9c791880882414f96e36b8b254e34d4b503279d6400d9a4b87a033834856dbdd94ee4b637944df17ea8afc4bce0ff4a1560d2166be8855da5b04 + languageName: node + linkType: hard + "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8"