diff --git a/src/tools/create_multisig.ts b/src/tools/create_multisig.ts deleted file mode 100644 index 7da0e0a..0000000 --- a/src/tools/create_multisig.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { PublicKey } from "@solana/web3.js"; -import * as multisig from "@sqds/multisig"; -import { SolanaAgentKit } from "../agent"; - -/** - * Creates a new Squads multisig account. - * - * @param agent - The SolanaAgentKit instance containing the connection and wallet information. - * @param creator - The public key of the creator who will be a member of the multisig. - * @returns A promise that resolves to the transaction ID of the multisig creation transaction. - * - * @throws Will throw an error if the transaction fails. - */ -export async function create_squads_multisig( - agent: SolanaAgentKit, - creator: PublicKey, -): Promise { - const connection = agent.connection; - const createKey = agent.wallet; // can be any keypair, using the agent wallet as only one multisig is required - - 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; - const tx = multisig.transactions.multisigCreateV2({ - blockhash: (await connection.getLatestBlockhash()).blockhash, - treasury: configTreasury, - createKey: createKey.publicKey, - creator: agent.wallet.publicKey, - multisigPda, - configAuthority: null, - timeLock: 0, - threshold: 2, - rentCollector: null, - members: [ - { - key: agent.wallet.publicKey, - permissions: multisig.types.Permissions.all(), - }, - { - key: creator, - permissions: multisig.types.Permissions.all(), - }, - ], - }); - - tx.sign([agent.wallet, createKey]); - - const txId = connection.sendRawTransaction(tx.serialize()); - - return txId; -} diff --git a/src/tools/index.ts b/src/tools/index.ts index 79407c1..3b1a1eb 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -3,13 +3,6 @@ export * from "./close_empty_token_accounts"; export * from "./create_3land_collectible"; export * from "./create_gibwork_task"; export * from "./create_image"; -export * from "./create_multisig"; -export * from "./multisig_approve_proposal"; -export * from "./multisig_create_proposal"; -export * from "./multisig_deposit_to_treasury"; -export * from "./multisig_execute_proposal"; -export * from "./multisig_reject_proposal"; -export * from "./multisig_transfer_from_treasury"; export * from "./create_tiplinks"; export * from "./deploy_collection"; export * from "./deploy_token"; @@ -31,6 +24,7 @@ export * from "./launch_pumpfun_token"; export * from "./lend"; export * from "./manifest_trade"; export * from "./mint_nft"; +export * from "./multisig_tools"; export * from "./openbook_create_market"; export * from "./orca_close_position"; export * from "./orca_create_clmm"; diff --git a/src/tools/multisig_approve_proposal.ts b/src/tools/multisig_approve_proposal.ts deleted file mode 100644 index 7b74a4a..0000000 --- a/src/tools/multisig_approve_proposal.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { SolanaAgentKit } from "../agent"; -import * as multisig from "@sqds/multisig"; -const { Multisig } = multisig.accounts; - -/** - * Approves a proposal in a Solana multisig wallet. - * - * @param {SolanaAgentKit} agent - The Solana agent kit instance. - * @param {number | bigint} [transactionIndex] - The index of the transaction to approve. If not provided, the current transaction index will be used. - * @returns {Promise} - A promise that resolves to the transaction ID of the approved proposal. - * @throws {Error} - Throws an error if the approval process fails. - */ -export async function multisig_approve_proposal( - agent: SolanaAgentKit, - transactionIndex?: number | bigint, -): Promise { - try { - const createKey = agent.wallet; - const [multisigPda] = multisig.getMultisigPda({ - createKey: createKey.publicKey, - }); - const multisigInfo = await Multisig.fromAccountAddress( - agent.connection, - multisigPda, - ); - const currentTransactionIndex = Number(multisigInfo.transactionIndex); - if (!transactionIndex) { - transactionIndex = BigInt(currentTransactionIndex); - } else if (typeof transactionIndex !== "bigint") { - transactionIndex = BigInt(transactionIndex); - } - // const [proposalPda, proposalBump] = multisig.getProposalPda({ - // multisigPda, - // transactionIndex, - // }); - const multisigTx = multisig.transactions.proposalApprove({ - blockhash: (await agent.connection.getLatestBlockhash()).blockhash, - feePayer: agent.wallet.publicKey, - multisigPda, - transactionIndex: transactionIndex, - member: agent.wallet.publicKey, - }); - - multisigTx.sign([agent.wallet]); - const tx = await agent.connection.sendRawTransaction( - multisigTx.serialize(), - ); - return tx; - } catch (error: any) { - throw new Error(`Transfer failed: ${error}`); - } -} diff --git a/src/tools/multisig_create_proposal.ts b/src/tools/multisig_create_proposal.ts deleted file mode 100644 index fff39c3..0000000 --- a/src/tools/multisig_create_proposal.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { SolanaAgentKit } from "../agent"; -import * as multisig from "@sqds/multisig"; -const { Multisig } = multisig.accounts; - -/** - * Creates a proposal for a multisig transaction. - * - * @param {SolanaAgentKit} agent - The Solana agent kit instance. - * @param {number | bigint} [transactionIndex] - Optional transaction index. If not provided, the current transaction index will be used. - * @returns {Promise} - The transaction ID of the created proposal. - * @throws {Error} - Throws an error if the proposal creation fails. - */ -export async function multisig_create_proposal( - agent: SolanaAgentKit, - transactionIndex?: number | bigint, -): Promise { - try { - const createKey = agent.wallet; - const [multisigPda] = multisig.getMultisigPda({ - createKey: createKey.publicKey, - }); - const multisigInfo = await Multisig.fromAccountAddress( - agent.connection, - multisigPda, - ); - const currentTransactionIndex = Number(multisigInfo.transactionIndex); - if (!transactionIndex) { - transactionIndex = BigInt(currentTransactionIndex); - } else if (typeof transactionIndex !== "bigint") { - transactionIndex = BigInt(transactionIndex); - } - const multisigTx = multisig.transactions.proposalCreate({ - blockhash: (await agent.connection.getLatestBlockhash()).blockhash, - feePayer: agent.wallet_address, - multisigPda, - transactionIndex, - creator: agent.wallet_address, - }); - - multisigTx.sign([agent.wallet]); - const tx = await agent.connection.sendRawTransaction( - multisigTx.serialize(), - ); - return tx; - } catch (error: any) { - throw new Error(`Transfer failed: ${error}`); - } -} diff --git a/src/tools/multisig_deposit_to_treasury.ts b/src/tools/multisig_deposit_to_treasury.ts deleted file mode 100644 index f9daf97..0000000 --- a/src/tools/multisig_deposit_to_treasury.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { SolanaAgentKit } from "../agent"; -import { PublicKey, SystemProgram, Transaction } from "@solana/web3.js"; -import { LAMPORTS_PER_SOL } from "@solana/web3.js"; -import { - getAssociatedTokenAddress, - createTransferInstruction, - getMint, - createAssociatedTokenAccountInstruction, -} from "@solana/spl-token"; -import * as multisig from "@sqds/multisig"; - -/** - * Transfer SOL or SPL tokens to a multisig treasury vault. - * @param agent SolanaAgentKit instance - * @param amount Amount to transfer - * @param vaultIndex Optional vault index, default is 0 - * @param mint Optional mint address for SPL tokens - * @returns Transaction signature - */ -export async function multisig_deposit_to_treasury( - agent: SolanaAgentKit, - amount: number, - vaultIndex?: number, - mint?: PublicKey, -): Promise { - try { - let tx: string; - if (!vaultIndex) { - vaultIndex = 0; - } - const createKey = agent.wallet; - const [multisigPda] = multisig.getMultisigPda({ - createKey: createKey.publicKey, - }); - const [vaultPda] = multisig.getVaultPda({ - multisigPda, - index: vaultIndex, - }); - const to = vaultPda; - if (!mint) { - // Transfer native SOL - const transaction = new Transaction().add( - SystemProgram.transfer({ - fromPubkey: agent.wallet_address, - toPubkey: to, - lamports: amount * LAMPORTS_PER_SOL, - }), - ); - - tx = await agent.connection.sendTransaction(transaction, [agent.wallet]); - } else { - // Transfer SPL token - const fromAta = await getAssociatedTokenAddress( - mint, - agent.wallet_address, - ); - const transaction = new Transaction(); - const toAta = await getAssociatedTokenAddress(mint, to, true); - const toTokenAccountInfo = await agent.connection.getAccountInfo(toAta); - // Create associated token account if it doesn't exist - if (!toTokenAccountInfo) { - transaction.add( - createAssociatedTokenAccountInstruction( - agent.wallet_address, - toAta, - to, - mint, - ), - ); - } - // Get mint info to determine decimals - const mintInfo = await getMint(agent.connection, mint); - const adjustedAmount = amount * Math.pow(10, mintInfo.decimals); - - transaction.add( - createTransferInstruction( - fromAta, - toAta, - agent.wallet_address, - adjustedAmount, - ), - ); - - tx = await agent.connection.sendTransaction(transaction, [agent.wallet]); - } - - return tx; - } catch (error: any) { - throw new Error(`Transfer failed: ${error}`); - } -} diff --git a/src/tools/multisig_execute_proposal.ts b/src/tools/multisig_execute_proposal.ts deleted file mode 100644 index 69efde9..0000000 --- a/src/tools/multisig_execute_proposal.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { SolanaAgentKit } from "../agent"; -import * as multisig from "@sqds/multisig"; -const { Multisig } = multisig.accounts; - -/** - * Executes a transaction on the Solana blockchain using the provided agent. - * - * @param {SolanaAgentKit} agent - The Solana agent kit instance containing the wallet and connection. - * @param {number | bigint} [transactionIndex] - Optional transaction index to execute. If not provided, the current transaction index from the multisig account will be used. - * @returns {Promise} - A promise that resolves to the transaction signature string. - * @throws {Error} - Throws an error if the transaction execution fails. - */ -export async function multisig_execute_proposal( - agent: SolanaAgentKit, - transactionIndex?: number | bigint, -): Promise { - try { - const createKey = agent.wallet; - const [multisigPda] = multisig.getMultisigPda({ - createKey: createKey.publicKey, - }); - const multisigInfo = await Multisig.fromAccountAddress( - agent.connection, - multisigPda, - ); - const currentTransactionIndex = Number(multisigInfo.transactionIndex); - if (!transactionIndex) { - transactionIndex = BigInt(currentTransactionIndex); - } else if (typeof transactionIndex !== "bigint") { - transactionIndex = BigInt(transactionIndex); - } - const multisigTx = await multisig.transactions.vaultTransactionExecute({ - connection: agent.connection, - blockhash: (await agent.connection.getLatestBlockhash()).blockhash, - feePayer: agent.wallet.publicKey, - multisigPda, - transactionIndex, - member: agent.wallet.publicKey, - }); - - multisigTx.sign([agent.wallet]); - const tx = await agent.connection.sendRawTransaction( - multisigTx.serialize(), - ); - return tx; - } catch (error: any) { - throw new Error(`Transfer failed: ${error}`); - } -} diff --git a/src/tools/multisig_reject_proposal.ts b/src/tools/multisig_reject_proposal.ts deleted file mode 100644 index 78b8246..0000000 --- a/src/tools/multisig_reject_proposal.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { SolanaAgentKit } from "../agent"; -import * as multisig from "@sqds/multisig"; -const { Multisig } = multisig.accounts; - -/** - * Rejects a proposal in a Solana multisig setup. - * - * @param agent - The SolanaAgentKit instance containing the wallet and connection. - * @param transactionIndex - Optional. The index of the transaction to reject. If not provided, the current transaction index will be used. - * @returns A promise that resolves to the transaction ID of the rejection transaction. - * @throws Will throw an error if the transaction fails. - */ -export async function multisig_reject_proposal( - agent: SolanaAgentKit, - transactionIndex?: number | bigint, -): Promise { - try { - const createKey = agent.wallet; - const [multisigPda] = multisig.getMultisigPda({ - createKey: createKey.publicKey, - }); - const multisigInfo = await Multisig.fromAccountAddress( - agent.connection, - multisigPda, - ); - const currentTransactionIndex = Number(multisigInfo.transactionIndex); - if (!transactionIndex) { - transactionIndex = BigInt(currentTransactionIndex); - } else if (typeof transactionIndex !== "bigint") { - transactionIndex = BigInt(transactionIndex); - } - // const [proposalPda, proposalBump] = multisig.getProposalPda({ - // multisigPda, - // transactionIndex, - // }); - const multisigTx = multisig.transactions.proposalReject({ - blockhash: (await agent.connection.getLatestBlockhash()).blockhash, - feePayer: agent.wallet.publicKey, - multisigPda, - transactionIndex: transactionIndex, - member: agent.wallet.publicKey, - }); - - multisigTx.sign([agent.wallet]); - const tx = await agent.connection.sendRawTransaction( - multisigTx.serialize(), - ); - return tx; - } catch (error: any) { - throw new Error(`Transfer failed: ${error}`); - } -} diff --git a/src/tools/multisig_tools.ts b/src/tools/multisig_tools.ts new file mode 100644 index 0000000..1639d45 --- /dev/null +++ b/src/tools/multisig_tools.ts @@ -0,0 +1,428 @@ +import { + LAMPORTS_PER_SOL, + PublicKey, + SystemProgram, + Transaction, + TransactionInstruction, + TransactionMessage, +} from "@solana/web3.js"; +import * as multisig from "@sqds/multisig"; +import { + getAssociatedTokenAddress, + createAssociatedTokenAccountInstruction, + getMint, + createTransferInstruction, +} from "@solana/spl-token"; +import { SolanaAgentKit } from "../agent"; + +const { Multisig } = multisig.accounts; + +/** + * Creates a new Squads multisig account. + * + * @param agent - The SolanaAgentKit instance containing the connection and wallet information. + * @param creator - The public key of the creator who will be a member of the multisig. + * @returns A promise that resolves to the transaction ID of the multisig creation transaction. + * + * @throws Will throw an error if the transaction fails. + */ +export async function create_squads_multisig( + agent: SolanaAgentKit, + creator: PublicKey, +): Promise { + const connection = agent.connection; + const createKey = agent.wallet; // can be any keypair, using the agent wallet as only one multisig is required + + 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; + const tx = multisig.transactions.multisigCreateV2({ + blockhash: (await connection.getLatestBlockhash()).blockhash, + treasury: configTreasury, + createKey: createKey.publicKey, + creator: agent.wallet.publicKey, + multisigPda, + configAuthority: null, + timeLock: 0, + threshold: 2, + rentCollector: null, + members: [ + { + key: agent.wallet.publicKey, + permissions: multisig.types.Permissions.all(), + }, + { + key: creator, + permissions: multisig.types.Permissions.all(), + }, + ], + }); + + tx.sign([agent.wallet, createKey]); + + const txId = connection.sendRawTransaction(tx.serialize()); + + return txId; +} + +/** + * Creates a proposal for a multisig transaction. + * + * @param {SolanaAgentKit} agent - The Solana agent kit instance. + * @param {number | bigint} [transactionIndex] - Optional transaction index. If not provided, the current transaction index will be used. + * @returns {Promise} - The transaction ID of the created proposal. + * @throws {Error} - Throws an error if the proposal creation fails. + */ +export async function multisig_create_proposal( + agent: SolanaAgentKit, + transactionIndex?: number | bigint, +): Promise { + try { + const createKey = agent.wallet; + const [multisigPda] = multisig.getMultisigPda({ + createKey: createKey.publicKey, + }); + const multisigInfo = await Multisig.fromAccountAddress( + agent.connection, + multisigPda, + ); + const currentTransactionIndex = Number(multisigInfo.transactionIndex); + if (!transactionIndex) { + transactionIndex = BigInt(currentTransactionIndex); + } else if (typeof transactionIndex !== "bigint") { + transactionIndex = BigInt(transactionIndex); + } + const multisigTx = multisig.transactions.proposalCreate({ + blockhash: (await agent.connection.getLatestBlockhash()).blockhash, + feePayer: agent.wallet_address, + multisigPda, + transactionIndex, + creator: agent.wallet_address, + }); + + multisigTx.sign([agent.wallet]); + const tx = await agent.connection.sendRawTransaction( + multisigTx.serialize(), + ); + return tx; + } catch (error: any) { + throw new Error(`Create proposal failed: ${error}`); + } +} + +/** + * Transfer SOL or SPL tokens to a multisig treasury vault. + * @param agent SolanaAgentKit instance + * @param amount Amount to transfer + * @param vaultIndex Optional vault index, default is 0 + * @param mint Optional mint address for SPL tokens + * @returns Transaction signature + */ +export async function multisig_deposit_to_treasury( + agent: SolanaAgentKit, + amount: number, + vaultIndex?: number, + mint?: PublicKey, +): Promise { + try { + let tx: string; + if (!vaultIndex) { + vaultIndex = 0; + } + const createKey = agent.wallet; + const [multisigPda] = multisig.getMultisigPda({ + createKey: createKey.publicKey, + }); + const [vaultPda] = multisig.getVaultPda({ + multisigPda, + index: vaultIndex, + }); + const to = vaultPda; + if (!mint) { + // Transfer native SOL + const transaction = new Transaction().add( + SystemProgram.transfer({ + fromPubkey: agent.wallet_address, + toPubkey: to, + lamports: amount * LAMPORTS_PER_SOL, + }), + ); + + tx = await agent.connection.sendTransaction(transaction, [agent.wallet]); + } else { + // Transfer SPL token + const fromAta = await getAssociatedTokenAddress( + mint, + agent.wallet_address, + ); + const transaction = new Transaction(); + const toAta = await getAssociatedTokenAddress(mint, to, true); + const toTokenAccountInfo = await agent.connection.getAccountInfo(toAta); + // Create associated token account if it doesn't exist + if (!toTokenAccountInfo) { + transaction.add( + createAssociatedTokenAccountInstruction( + agent.wallet_address, + toAta, + to, + mint, + ), + ); + } + // Get mint info to determine decimals + const mintInfo = await getMint(agent.connection, mint); + const adjustedAmount = amount * Math.pow(10, mintInfo.decimals); + + transaction.add( + createTransferInstruction( + fromAta, + toAta, + agent.wallet_address, + adjustedAmount, + ), + ); + + tx = await agent.connection.sendTransaction(transaction, [agent.wallet]); + } + + return tx; + } catch (error: any) { + throw new Error(`Transfer failed: ${error}`); + } +} + +/** + * Approves a proposal in a Solana multisig wallet. + * + * @param {SolanaAgentKit} agent - The Solana agent kit instance. + * @param {number | bigint} [transactionIndex] - The index of the transaction to approve. If not provided, the current transaction index will be used. + * @returns {Promise} - A promise that resolves to the transaction ID of the approved proposal. + * @throws {Error} - Throws an error if the approval process fails. + */ +export async function multisig_approve_proposal( + agent: SolanaAgentKit, + transactionIndex?: number | bigint, +): Promise { + try { + const createKey = agent.wallet; + const [multisigPda] = multisig.getMultisigPda({ + createKey: createKey.publicKey, + }); + const multisigInfo = await Multisig.fromAccountAddress( + agent.connection, + multisigPda, + ); + const currentTransactionIndex = Number(multisigInfo.transactionIndex); + if (!transactionIndex) { + transactionIndex = BigInt(currentTransactionIndex); + } else if (typeof transactionIndex !== "bigint") { + transactionIndex = BigInt(transactionIndex); + } + + // const [proposalPda, proposalBump] = multisig.getProposalPda({ + // multisigPda, + // transactionIndex, + // }); + + const multisigTx = multisig.transactions.proposalApprove({ + blockhash: (await agent.connection.getLatestBlockhash()).blockhash, + feePayer: agent.wallet.publicKey, + multisigPda, + transactionIndex: transactionIndex, + member: agent.wallet.publicKey, + }); + + multisigTx.sign([agent.wallet]); + const tx = await agent.connection.sendRawTransaction( + multisigTx.serialize(), + ); + return tx; + } catch (error: any) { + throw new Error(`Approve proposal failed: ${error}`); + } +} + +/** + * Executes a transaction on the Solana blockchain using the provided agent. + * + * @param {SolanaAgentKit} agent - The Solana agent kit instance containing the wallet and connection. + * @param {number | bigint} [transactionIndex] - Optional transaction index to execute. If not provided, the current transaction index from the multisig account will be used. + * @returns {Promise} - A promise that resolves to the transaction signature string. + * @throws {Error} - Throws an error if the transaction execution fails. + */ +export async function multisig_execute_proposal( + agent: SolanaAgentKit, + transactionIndex?: number | bigint, +): Promise { + try { + const createKey = agent.wallet; + const [multisigPda] = multisig.getMultisigPda({ + createKey: createKey.publicKey, + }); + const multisigInfo = await Multisig.fromAccountAddress( + agent.connection, + multisigPda, + ); + const currentTransactionIndex = Number(multisigInfo.transactionIndex); + if (!transactionIndex) { + transactionIndex = BigInt(currentTransactionIndex); + } else if (typeof transactionIndex !== "bigint") { + transactionIndex = BigInt(transactionIndex); + } + const multisigTx = await multisig.transactions.vaultTransactionExecute({ + connection: agent.connection, + blockhash: (await agent.connection.getLatestBlockhash()).blockhash, + feePayer: agent.wallet.publicKey, + multisigPda, + transactionIndex, + member: agent.wallet.publicKey, + }); + + multisigTx.sign([agent.wallet]); + const tx = await agent.connection.sendRawTransaction( + multisigTx.serialize(), + ); + return tx; + } catch (error: any) { + throw new Error(`Execute proposal failed: ${error}`); + } +} + +/** + * Rejects a proposal in a Solana multisig setup. + * + * @param agent - The SolanaAgentKit instance containing the wallet and connection. + * @param transactionIndex - Optional. The index of the transaction to reject. If not provided, the current transaction index will be used. + * @returns A promise that resolves to the transaction ID of the rejection transaction. + * @throws Will throw an error if the transaction fails. + */ +export async function multisig_reject_proposal( + agent: SolanaAgentKit, + transactionIndex?: number | bigint, +): Promise { + try { + const createKey = agent.wallet; + const [multisigPda] = multisig.getMultisigPda({ + createKey: createKey.publicKey, + }); + const multisigInfo = await Multisig.fromAccountAddress( + agent.connection, + multisigPda, + ); + const currentTransactionIndex = Number(multisigInfo.transactionIndex); + if (!transactionIndex) { + transactionIndex = BigInt(currentTransactionIndex); + } else if (typeof transactionIndex !== "bigint") { + transactionIndex = BigInt(transactionIndex); + } + + const multisigTx = multisig.transactions.proposalReject({ + blockhash: (await agent.connection.getLatestBlockhash()).blockhash, + feePayer: agent.wallet.publicKey, + multisigPda, + transactionIndex: transactionIndex, + member: agent.wallet.publicKey, + }); + + multisigTx.sign([agent.wallet]); + const tx = await agent.connection.sendRawTransaction( + multisigTx.serialize(), + ); + return tx; + } catch (error: any) { + throw new Error(`Reject proposal failed: ${error}`); + } +} + +/** + * Transfer SOL or SPL tokens from a multisig treasury vault to a recipient. + * @param agent - SolanaAgentKit instance. + * @param amount - Amount to transfer. + * @param to - Recipient's public key. + * @param vaultIndex - Optional vault index, default is 0. + * @param mint - Optional mint address for SPL tokens. + * @returns Transaction signature. + */ +export async function multisig_transfer_from_treasury( + agent: SolanaAgentKit, + amount: number, + to: PublicKey, + vaultIndex: number = 0, + mint?: PublicKey, +): Promise { + try { + let transferInstruction: TransactionInstruction; + + const createKey = agent.wallet; + const [multisigPda] = multisig.getMultisigPda({ + createKey: createKey.publicKey, + }); + const multisigInfo = await Multisig.fromAccountAddress( + agent.connection, + multisigPda, + ); + const currentTransactionIndex = Number(multisigInfo.transactionIndex); + const transactionIndex = BigInt(currentTransactionIndex + 1); + const [vaultPda] = multisig.getVaultPda({ + multisigPda, + index: vaultIndex, + }); + + if (!mint) { + // Transfer native SOL + transferInstruction = SystemProgram.transfer({ + fromPubkey: agent.wallet_address, + toPubkey: to, + lamports: amount * LAMPORTS_PER_SOL, + }); + } else { + // Transfer SPL token + const fromAta = await getAssociatedTokenAddress(mint, vaultPda, true); + const toAta = await getAssociatedTokenAddress(mint, to, true); + const mintInfo = await getMint(agent.connection, mint); + const adjustedAmount = amount * Math.pow(10, mintInfo.decimals); + + transferInstruction = createTransferInstruction( + fromAta, + toAta, + agent.wallet_address, + adjustedAmount, + ); + } + + const transferMessage = new TransactionMessage({ + payerKey: vaultPda, + recentBlockhash: (await agent.connection.getLatestBlockhash()).blockhash, + instructions: [transferInstruction], + }); + + const multisigTx = multisig.transactions.vaultTransactionCreate({ + blockhash: (await agent.connection.getLatestBlockhash()).blockhash, + feePayer: agent.wallet_address, + multisigPda, + transactionIndex, + creator: agent.wallet_address, + vaultIndex: 0, + ephemeralSigners: 0, + transactionMessage: transferMessage, + }); + + multisigTx.sign([agent.wallet]); + const tx = await agent.connection.sendRawTransaction( + multisigTx.serialize(), + ); + return tx; + } catch (error: any) { + throw new Error(`Transfer failed: ${error}`); + } +} diff --git a/src/tools/multisig_transfer_from_treasury.ts b/src/tools/multisig_transfer_from_treasury.ts deleted file mode 100644 index fae9bf2..0000000 --- a/src/tools/multisig_transfer_from_treasury.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { SolanaAgentKit } from "../agent"; -import { - PublicKey, - SystemProgram, - TransactionInstruction, - TransactionMessage, -} from "@solana/web3.js"; -import { LAMPORTS_PER_SOL } from "@solana/web3.js"; -import { - getAssociatedTokenAddress, - createTransferInstruction, - getMint, -} from "@solana/spl-token"; -import * as multisig from "@sqds/multisig"; -const { Multisig } = multisig.accounts; - -/** - * Transfer SOL or SPL tokens to a recipient from a multisig treasury vault. - * @param agent - SolanaAgentKit instance. - * @param amount - Amount to transfer. - * @param to - Recipient's public key. - * @param vaultIndex - Optional vault index, default is 0. - * @param mint - Optional mint address for SPL tokens. - * @returns Transaction signature. - */ -export async function multisig_transfer_from_treasury( - agent: SolanaAgentKit, - amount: number, - to: PublicKey, - vaultIndex: number = 0, - mint?: PublicKey, -): Promise { - try { - let transferInstruction: TransactionInstruction; - - const createKey = agent.wallet; - const [multisigPda] = multisig.getMultisigPda({ - createKey: createKey.publicKey, - }); - const multisigInfo = await Multisig.fromAccountAddress( - agent.connection, - multisigPda, - ); - const currentTransactionIndex = Number(multisigInfo.transactionIndex); - const transactionIndex = BigInt(currentTransactionIndex + 1); - const [vaultPda] = multisig.getVaultPda({ - multisigPda, - index: vaultIndex, - }); - - if (!mint) { - // Transfer native SOL - transferInstruction = SystemProgram.transfer({ - fromPubkey: agent.wallet_address, - toPubkey: to, - lamports: amount * LAMPORTS_PER_SOL, - }); - } else { - // Transfer SPL token - const fromAta = await getAssociatedTokenAddress(mint, vaultPda, true); - const toAta = await getAssociatedTokenAddress(mint, to, true); - const mintInfo = await getMint(agent.connection, mint); - const adjustedAmount = amount * Math.pow(10, mintInfo.decimals); - - transferInstruction = createTransferInstruction( - fromAta, - toAta, - agent.wallet_address, - adjustedAmount, - ); - } - - const transferMessage = new TransactionMessage({ - payerKey: vaultPda, - recentBlockhash: (await agent.connection.getLatestBlockhash()).blockhash, - instructions: [transferInstruction], - }); - - const multisigTx = multisig.transactions.vaultTransactionCreate({ - blockhash: (await agent.connection.getLatestBlockhash()).blockhash, - feePayer: agent.wallet_address, - multisigPda, - transactionIndex, - creator: agent.wallet_address, - vaultIndex: 0, - ephemeralSigners: 0, - transactionMessage: transferMessage, - }); - - multisigTx.sign([agent.wallet]); - const tx = await agent.connection.sendRawTransaction( - multisigTx.serialize(), - ); - return tx; - } catch (error: any) { - throw new Error(`Transfer failed: ${error}`); - } -}