Skip to content

Commit

Permalink
add create indempotent when an account is not initialized (#136)
Browse files Browse the repository at this point in the history
* add create indempotent when an account is not initialized

* feat: adds instruction regardless, update package

* fix: rename function to relay details

---------

Co-authored-by: Kollan House <[email protected]>
  • Loading branch information
swaggymarie and R-K-H authored Jun 3, 2024
1 parent 3affa53 commit 9fb7da6
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 45 deletions.
108 changes: 64 additions & 44 deletions lib/client/rpc/proposals/proposals.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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";

Expand All @@ -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;
Expand Down Expand Up @@ -236,34 +238,33 @@ 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
);

return {
userConditionalOnFinalizeTokenAccount,
userConditionalOnRevertTokenAccount,
userUnderlyingTokenAccount,
conditionalOnFinalizeTokenMint:
vaultAccount.conditionalOnFinalizeTokenMint,
conditionalOnFinalizeTokenMint: vaultAccount.conditionalOnFinalizeTokenMint,
conditionalOnRevertTokenMint: vaultAccount.conditionalOnRevertTokenMint,
vaultUnderlyingTokenAccount: vaultAccount.underlyingTokenAccount,
tokenProgram: TOKEN_PROGRAM_ID
Expand All @@ -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,
Expand All @@ -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)
Expand All @@ -322,7 +323,46 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient {
} else throw Error("Version not compatible");
}

private async createRedeemIx(
vaultProgram: any,
accounts: Record<string, PublicKey>,
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(
Expand All @@ -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,
{
Expand All @@ -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) { }
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down

0 comments on commit 9fb7da6

Please sign in to comment.