diff --git a/lib/client/rpc/proposals/proposals.ts b/lib/client/rpc/proposals/proposals.ts index e19b029..4c0d282 100644 --- a/lib/client/rpc/proposals/proposals.ts +++ b/lib/client/rpc/proposals/proposals.ts @@ -1,4 +1,4 @@ -import { PublicKey, Transaction } from "@solana/web3.js"; +import { PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js"; import { AnchorProvider, BN, Program } from "@coral-xyz/anchor"; import { TOKEN_PROGRAM_ID, @@ -25,7 +25,8 @@ import { AMM_PROGRAM_ID, AUTOCRAT_PROGRAM_ID, AutocratClient, - CONDITIONAL_VAULT_PROGRAM_ID + CONDITIONAL_VAULT_PROGRAM_ID, + InstructionUtils } from "@metadaoproject/futarchy"; import { SendTransactionResponse } from "@/types/transactions"; @@ -37,6 +38,7 @@ import { import { FinalizeProposalClient } from "./finalizeProposal"; import { CreateProposalClient } from "./createProposal"; import { autocratVersionToConditionalVaultMap } from "@/constants"; +import { throwError } from "rxjs"; export class FutarchyRPCProposalsClient implements FutarchyProposalsClient { private rpcProvider: AnchorProvider; @@ -236,25 +238,25 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient { return this.finalizeProposalClient.finalizeProposal(proposal); } - async getVaultAccounts( + async getUserVaultAccounts( vaultAccount: VaultAccountWithProtocol, - proposal: PublicKey + user: PublicKey ) { const userConditionalOnFinalizeTokenAccount = getAssociatedTokenAddressSync( vaultAccount.conditionalOnFinalizeTokenMint, - this.rpcProvider.publicKey, + user, true ); const userConditionalOnRevertTokenAccount = getAssociatedTokenAddressSync( vaultAccount.conditionalOnRevertTokenMint, - this.rpcProvider.publicKey, + user, true ); const userUnderlyingTokenAccount = getAssociatedTokenAddressSync( vaultAccount.underlyingTokenMint, - this.rpcProvider.publicKey, + user, true ); @@ -262,8 +264,7 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient { userConditionalOnFinalizeTokenAccount, userConditionalOnRevertTokenAccount, userUnderlyingTokenAccount, - conditionalOnFinalizeTokenMint: - vaultAccount.conditionalOnFinalizeTokenMint, + conditionalOnFinalizeTokenMint: vaultAccount.conditionalOnFinalizeTokenMint, conditionalOnRevertTokenMint: vaultAccount.conditionalOnRevertTokenMint, vaultUnderlyingTokenAccount: vaultAccount.underlyingTokenAccount, tokenProgram: TOKEN_PROGRAM_ID @@ -279,7 +280,7 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient { if (programVersion == "V0.3" || programVersion == "V0.2") { const vaultForVersion = autocratVersionToConditionalVaultMap[ - proposal.protocol.deploymentVersion + proposal.protocol.deploymentVersion ]; const vaultProgram = new Program( vaultForVersion.idl, @@ -296,9 +297,9 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient { ? proposal.account.baseVault : proposal.account.quoteVault; - const accounts = await this.getVaultAccounts( + const accounts = await this.getUserVaultAccounts( vaultAccount, - proposal.publicKey + this.rpcProvider.publicKey ); const mergeTx = await vaultProgram.methods .mergeConditionalTokensForUnderlyingTokens(amount) @@ -322,7 +323,46 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient { } else throw Error("Version not compatible"); } + private async createRedeemIx( + vaultProgram: any, + accounts: Record, + vault: PublicKey, + user: PublicKey + ) { + const passVaultAccountRedeemCreateIx = + createAssociatedTokenAccountIdempotentInstruction( + user, + accounts.userConditionalOnFinalizeTokenAccount, // TODO: Review if we want to instead use getAssociatedTokenAddressSync() + user, + accounts.conditionalOnFinalizeTokenMint + ); + + const failVaultAccountRedeemCreateIx = + createAssociatedTokenAccountIdempotentInstruction( + user, + accounts.userConditionalOnRevertTokenAccount, // TODO: Review if we want to instead use getAssociatedTokenAddressSync() + user, + accounts.conditionalOnRevertTokenMint + ); + + let redeem = ( + vaultProgram.methods + .redeemConditionalTokensForUnderlyingTokens() + .accounts({ + ...accounts, + authority: user, + vault + }) + .preInstructions( + [passVaultAccountRedeemCreateIx, failVaultAccountRedeemCreateIx] + ) + ); + + return redeem && (await redeem.transaction()).instructions; + } + public async withdraw(proposal: Proposal) { + const user = this.rpcProvider.publicKey const vaultForVersion = autocratVersionToConditionalVaultMap[proposal.protocol.deploymentVersion]; const vaultProgram = new Program( @@ -331,39 +371,19 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient { this.rpcProvider ); - const baseAccounts = await this.getVaultAccounts( - proposal.baseVaultAccount, - proposal.publicKey - ); - const quoteAccounts = await this.getVaultAccounts( - proposal.quoteVaultAccount, - proposal.publicKey - ); + const baseAccounts = await this.getUserVaultAccounts(proposal.baseVaultAccount, user); + const quoteAccounts = await this.getUserVaultAccounts(proposal.quoteVaultAccount, user); - const redeeemBaseIx = ( - await vaultProgram.methods - .redeemConditionalTokensForUnderlyingTokens() - .accounts({ - ...baseAccounts, - authority: this.rpcProvider.publicKey, - vault: proposal.account.baseVault - }) - .transaction() - ).instructions; + const redeemBaseIx = await this.createRedeemIx(vaultProgram, baseAccounts, proposal.account.baseVault, user); + const redeemQuoteIx = await this.createRedeemIx(vaultProgram, quoteAccounts, proposal.account.quoteVault, user); - const redeeemQuoteIx = ( - await vaultProgram.methods - .redeemConditionalTokensForUnderlyingTokens() - .accounts({ - ...quoteAccounts, - authority: this.rpcProvider.publicKey, - vault: proposal.account.quoteVault - }) - .transaction() - ).instructions; + const tx = new Transaction(); + if (redeemBaseIx) tx.add(...redeemBaseIx); + if (redeemQuoteIx) tx.add(...redeemQuoteIx); + + if(!redeemBaseIx && !redeemQuoteIx) throw new Error("No account") - const tx = new Transaction().add(...redeeemBaseIx).add(...redeeemQuoteIx); - const resp = await this.transactionSender?.send( + const resp = this.transactionSender?.send( [tx], this.rpcProvider.connection, { @@ -376,6 +396,6 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient { } // TO DO INDEXER - public async saveProposalDetails(proposalDetails: ProposalDetails) {} - public async updateProposalAccounts(accounts: ProposalAccounts) {} + public async saveProposalDetails(proposalDetails: ProposalDetails) { } + public async updateProposalAccounts(accounts: ProposalAccounts) { } } diff --git a/package.json b/package.json index 3140573..31bc016 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@metadaoproject/futarchy-sdk", - "version": "3.0.0-alpha.1", + "version": "3.0.0-alpha.2", "main": "dist", "scripts": { "preinstall": "npx only-allow pnpm",