diff --git a/sdk/package.json b/sdk/package.json index e0890e9..346fefd 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@metadaoproject/futarchy", - "version": "0.4.0-alpha.23", + "version": "0.4.0-alpha.33", "type": "module", "main": "dist/index.js", "module": "dist/index.js", diff --git a/sdk/src/v0.3/AmmClient.ts b/sdk/src/v0.3/AmmClient.ts index 0c574a8..a581bbf 100644 --- a/sdk/src/v0.3/AmmClient.ts +++ b/sdk/src/v0.3/AmmClient.ts @@ -309,7 +309,9 @@ export class AmmClient { quoteMint: PublicKey, swapType: SwapType, inputAmount: BN, - outputAmountMin: BN + outputAmountMin: BN, + user: PublicKey = this.provider.publicKey, + payer: PublicKey = this.provider.publicKey ) { const receivingToken = swapType.buy ? baseMint : quoteMint; @@ -320,30 +322,19 @@ export class AmmClient { outputAmountMin, }) .accounts({ - user: this.provider.publicKey, + user, amm, - userBaseAccount: getAssociatedTokenAddressSync( - baseMint, - this.provider.publicKey, - true - ), - userQuoteAccount: getAssociatedTokenAddressSync( - quoteMint, - this.provider.publicKey, - true - ), + userBaseAccount: getAssociatedTokenAddressSync(baseMint, user, true), + userQuoteAccount: getAssociatedTokenAddressSync(quoteMint, user, true), vaultAtaBase: getAssociatedTokenAddressSync(baseMint, amm, true), vaultAtaQuote: getAssociatedTokenAddressSync(quoteMint, amm, true), }) .preInstructions([ // create the receiving token account if it doesn't exist createAssociatedTokenAccountIdempotentInstruction( - this.provider.publicKey, - getAssociatedTokenAddressSync( - receivingToken, - this.provider.publicKey - ), - this.provider.publicKey, + payer, + getAssociatedTokenAddressSync(receivingToken, user), + user, receivingToken ), ]); diff --git a/sdk/src/v0.3/AutocratClient.ts b/sdk/src/v0.3/AutocratClient.ts index 46890f3..eaeecbf 100644 --- a/sdk/src/v0.3/AutocratClient.ts +++ b/sdk/src/v0.3/AutocratClient.ts @@ -13,10 +13,6 @@ import { PriceMath } from "./utils/ammMath.js"; import { ProposalInstruction, InitializeDaoParams } from "./types/index.js"; import { Autocrat, IDL as AutocratIDL } from "./types/autocrat.js"; -import { - ConditionalVault, - IDL as ConditionalVaultIDL, -} from "./types/conditional_vault.js"; import BN from "bn.js"; import { diff --git a/sdk/src/v0.3/ConditionalVaultClient.ts b/sdk/src/v0.3/ConditionalVaultClient.ts index 2ef1dd6..8828d7a 100644 --- a/sdk/src/v0.3/ConditionalVaultClient.ts +++ b/sdk/src/v0.3/ConditionalVaultClient.ts @@ -68,7 +68,7 @@ export class ConditionalVaultClient { async mintConditionalTokens( vault: PublicKey, uiAmount: number, - user?: PublicKey | Keypair + user: PublicKey = this.provider.publicKey ) { const storedVault = await this.getVault(vault); @@ -90,17 +90,9 @@ export class ConditionalVaultClient { vault: PublicKey, underlyingTokenMint: PublicKey, amount: BN, - user?: PublicKey | Keypair + user: PublicKey = this.provider.publicKey, + payer: PublicKey = this.provider.publicKey ) { - let userPubkey; - if (!user) { - userPubkey = this.provider.publicKey; - } else if (user instanceof Keypair) { - userPubkey = user.publicKey; - } else { - userPubkey = user; - } - const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( this.vaultProgram.programId, vault @@ -112,18 +104,18 @@ export class ConditionalVaultClient { let userConditionalOnFinalizeTokenAccount = getAssociatedTokenAddressSync( conditionalOnFinalizeTokenMint, - userPubkey + user ); let userConditionalOnRevertTokenAccount = getAssociatedTokenAddressSync( conditionalOnRevertTokenMint, - userPubkey + user ); let ix = this.vaultProgram.methods .mintConditionalTokens(amount) .accounts({ - authority: userPubkey, + authority: user, vault, vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, @@ -132,7 +124,7 @@ export class ConditionalVaultClient { ), userUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, - userPubkey, + user, true ), conditionalOnFinalizeTokenMint, @@ -142,28 +134,26 @@ export class ConditionalVaultClient { }) .preInstructions([ createAssociatedTokenAccountIdempotentInstruction( - userPubkey, + payer, userConditionalOnFinalizeTokenAccount, - userPubkey, + user, conditionalOnFinalizeTokenMint ), createAssociatedTokenAccountIdempotentInstruction( - userPubkey, + payer, userConditionalOnRevertTokenAccount, - userPubkey, + user, conditionalOnRevertTokenMint ), ]); - if (user instanceof Keypair) { - ix = ix.signers([user]); - } return ix; } initializeVaultIx( settlementAuthority: PublicKey, - underlyingTokenMint: PublicKey + underlyingTokenMint: PublicKey, + payer: PublicKey = this.provider.publicKey ) { const [vault] = getVaultAddr( this.vaultProgram.programId, @@ -197,7 +187,7 @@ export class ConditionalVaultClient { }) .preInstructions([ createAssociatedTokenAccountIdempotentInstruction( - this.provider.publicKey, + payer, vaultUnderlyingTokenAccount, vault, underlyingTokenMint @@ -210,7 +200,8 @@ export class ConditionalVaultClient { underlyingTokenMint: PublicKey, proposalNumber: number, onFinalizeUri: string, - onRevertUri: string + onRevertUri: string, + payer: PublicKey = this.provider.publicKey ) { const [underlyingTokenMetadata] = getMetadataAddr(underlyingTokenMint); @@ -238,7 +229,7 @@ export class ConditionalVaultClient { onRevertUri, }) .accounts({ - payer: this.provider.publicKey, + payer, vault, underlyingTokenMint, underlyingTokenMetadata, @@ -250,7 +241,11 @@ export class ConditionalVaultClient { }); } - redeemConditionalTokensIx(vault: PublicKey, underlyingTokenMint: PublicKey) { + redeemConditionalTokensIx( + vault: PublicKey, + underlyingTokenMint: PublicKey, + user: PublicKey = this.provider.publicKey + ) { const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( this.vaultProgram.programId, vault @@ -263,7 +258,7 @@ export class ConditionalVaultClient { return this.vaultProgram.methods .redeemConditionalTokensForUnderlyingTokens() .accounts({ - authority: this.provider.publicKey, + authority: user, vault, vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, @@ -272,18 +267,18 @@ export class ConditionalVaultClient { ), userUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, - this.provider.publicKey, + user, true ), conditionalOnFinalizeTokenMint, userConditionalOnFinalizeTokenAccount: getAssociatedTokenAddressSync( conditionalOnFinalizeTokenMint, - this.provider.publicKey + user ), conditionalOnRevertTokenMint, userConditionalOnRevertTokenAccount: getAssociatedTokenAddressSync( conditionalOnRevertTokenMint, - this.provider.publicKey + user ), }); } @@ -291,7 +286,8 @@ export class ConditionalVaultClient { mergeConditionalTokensIx( vault: PublicKey, underlyingTokenMint: PublicKey, - amount: BN + amount: BN, + user: PublicKey = this.provider.publicKey ) { const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( this.vaultProgram.programId, @@ -305,7 +301,7 @@ export class ConditionalVaultClient { return this.vaultProgram.methods .mergeConditionalTokensForUnderlyingTokens(amount) .accounts({ - authority: this.provider.publicKey, + authority: user, vault, vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, @@ -314,25 +310,26 @@ export class ConditionalVaultClient { ), userUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, - this.provider.publicKey, + user, true ), conditionalOnFinalizeTokenMint, userConditionalOnFinalizeTokenAccount: getAssociatedTokenAddressSync( conditionalOnFinalizeTokenMint, - this.provider.publicKey + user ), conditionalOnRevertTokenMint, userConditionalOnRevertTokenAccount: getAssociatedTokenAddressSync( conditionalOnRevertTokenMint, - this.provider.publicKey + user ), }); } async initializeVault( settlementAuthority: PublicKey, - underlyingTokenMint: PublicKey + underlyingTokenMint: PublicKey, + payer: PublicKey = this.provider.publicKey ): Promise { const [vault] = getVaultAddr( this.vaultProgram.programId, @@ -342,7 +339,8 @@ export class ConditionalVaultClient { await this.initializeVaultIx( settlementAuthority, - underlyingTokenMint + underlyingTokenMint, + payer ).rpc(); return vault; diff --git a/sdk/src/v0.3/FutarchyClient.ts b/sdk/src/v0.3/FutarchyClient.ts new file mode 100644 index 0000000..6427d4e --- /dev/null +++ b/sdk/src/v0.3/FutarchyClient.ts @@ -0,0 +1,130 @@ +import { AnchorProvider } from "@coral-xyz/anchor"; +import { PublicKey, Transaction } from "@solana/web3.js"; +import BN from "bn.js"; +import { AmmClient, SwapType } from "./AmmClient.js"; +import { AutocratClient } from "./AutocratClient.js"; +import { ConditionalVaultClient } from "./ConditionalVaultClient.js"; +import { + getAmmAddr, + getVaultAddr, + getVaultFinalizeMintAddr, + getVaultRevertMintAddr, +} from "./utils/pda.js"; + +export class FutarchyClient { + public readonly provider: AnchorProvider; + public readonly ammClient: AmmClient; + public readonly autocratClient: AutocratClient; + public readonly conditionalVaultClient: ConditionalVaultClient; + + constructor(provider: AnchorProvider) { + this.provider = provider; + this.ammClient = AmmClient.createClient({ provider }); + this.autocratClient = AutocratClient.createClient({ provider }); + this.conditionalVaultClient = ConditionalVaultClient.createClient({ + provider, + }); + } + + public static createClient({ + provider, + }: { + provider: AnchorProvider; + }): FutarchyClient { + return new FutarchyClient(provider); + } + + public async createMintAndSwapTx({ + proposal, + inputAmount, + quoteMint, + baseMint, + user, + payer, + swapType, + outcome, + }: { + proposal: PublicKey; + inputAmount: BN; + quoteMint: PublicKey; + baseMint: PublicKey; + user: PublicKey; + payer: PublicKey; + swapType: SwapType; + outcome: "pass" | "fail"; + }): Promise { + const [baseVault] = getVaultAddr( + this.conditionalVaultClient.vaultProgram.programId, + proposal, + baseMint + ); + const [quoteVault] = getVaultAddr( + this.conditionalVaultClient.vaultProgram.programId, + proposal, + quoteMint + ); + + const [underlyingVault, underlyingTokenMint] = swapType.buy + ? [quoteVault, quoteMint] + : [baseVault, baseMint]; + + const mintTx = await this.conditionalVaultClient + .mintConditionalTokensIx( + underlyingVault, + underlyingTokenMint, + inputAmount, + user, + payer + ) + .transaction(); + + const [pUSDC] = getVaultFinalizeMintAddr( + this.conditionalVaultClient.vaultProgram.programId, + quoteVault + ); + const [pTOKE] = getVaultFinalizeMintAddr( + this.conditionalVaultClient.vaultProgram.programId, + baseVault + ); + + const [fUSDC] = getVaultRevertMintAddr( + this.conditionalVaultClient.vaultProgram.programId, + quoteVault + ); + const [fTOKE] = getVaultRevertMintAddr( + this.conditionalVaultClient.vaultProgram.programId, + baseVault + ); + + const [passMarket] = getAmmAddr( + this.ammClient.program.programId, + pTOKE, + pUSDC + ); + const [failMarket] = getAmmAddr( + this.ammClient.program.programId, + fTOKE, + fUSDC + ); + + const [market, ammBaseMint, ammQuoteMint] = + outcome === "pass" + ? [passMarket, pTOKE, pUSDC] + : [failMarket, fTOKE, fUSDC]; + + const swapTx = await this.ammClient + .swapIx( + market, + ammBaseMint, + ammQuoteMint, + swapType, + inputAmount, + new BN(0), + user, + payer + ) + .transaction(); + + return new Transaction().add(mintTx, swapTx); + } +} diff --git a/sdk/src/v0.3/index.ts b/sdk/src/v0.3/index.ts index 1aa39fe..0eb16f6 100644 --- a/sdk/src/v0.3/index.ts +++ b/sdk/src/v0.3/index.ts @@ -4,3 +4,4 @@ export * from "./constants.js"; export * from "./AmmClient.js"; export * from "./AutocratClient.js"; export * from "./ConditionalVaultClient.js"; +export * from "./FutarchyClient.js"; diff --git a/sdk/src/v0.3/utils/ammMath.ts b/sdk/src/v0.3/utils/ammMath.ts index ab44499..151c3fc 100644 --- a/sdk/src/v0.3/utils/ammMath.ts +++ b/sdk/src/v0.3/utils/ammMath.ts @@ -29,6 +29,19 @@ export type RemoveLiquiditySimulation = { }; export class AmmMath { + public static getHumanPriceFromReserves( + baseReserves: BN, + quoteReserves: BN, + baseDecimals: number, + quoteDecimals: number + ): number { + return this.getHumanPrice( + this.getAmmPriceFromReserves(baseReserves, quoteReserves), + baseDecimals, + quoteDecimals + ); + } + public static getAmmPriceFromReserves( baseReserves: BN, quoteReserves: BN diff --git a/sdk/src/v0.4/ConditionalVaultClient.ts b/sdk/src/v0.4/ConditionalVaultClient.ts index faa81c9..5714203 100644 --- a/sdk/src/v0.4/ConditionalVaultClient.ts +++ b/sdk/src/v0.4/ConditionalVaultClient.ts @@ -128,7 +128,8 @@ export class ConditionalVaultClient { initializeVaultIx( question: PublicKey, underlyingTokenMint: PublicKey, - numOutcomes: number + numOutcomes: number, + payer: PublicKey = this.provider.publicKey ) { const [vault] = getVaultAddr( this.vaultProgram.programId, @@ -162,7 +163,7 @@ export class ConditionalVaultClient { }) .preInstructions([ createAssociatedTokenAccountIdempotentInstruction( - this.provider.publicKey, + payer, vaultUnderlyingTokenAccount, vault, underlyingTokenMint @@ -247,7 +248,8 @@ export class ConditionalVaultClient { getConditionalTokenAccountsAndInstructions( vault: PublicKey, numOutcomes: number, - user: PublicKey + user: PublicKey, + payer: PublicKey = this.provider.publicKey ) { const conditionalTokenMintAddrs = this.getConditionalTokenMints( vault, @@ -259,7 +261,7 @@ export class ConditionalVaultClient { const preInstructions = conditionalTokenMintAddrs.map((mint) => createAssociatedTokenAccountIdempotentInstruction( - this.provider.publicKey, + payer, getAssociatedTokenAddressSync(mint, user), user, mint @@ -312,7 +314,8 @@ export class ConditionalVaultClient { underlyingTokenMint: PublicKey, amount: BN, numOutcomes: number, - user: PublicKey = this.provider.publicKey + user: PublicKey = this.provider.publicKey, + payer: PublicKey = this.provider.publicKey ) { let conditionalTokenMintAddrs = this.getConditionalTokenMints( vault, @@ -346,7 +349,7 @@ export class ConditionalVaultClient { .preInstructions( conditionalTokenMintAddrs.map((conditionalTokenMint) => { return createAssociatedTokenAccountIdempotentInstruction( - this.provider.publicKey, + payer, getAssociatedTokenAddressSync(conditionalTokenMint, user), user, conditionalTokenMint @@ -373,7 +376,8 @@ export class ConditionalVaultClient { vault: PublicKey, underlyingTokenMint: PublicKey, numOutcomes: number, - user: PublicKey = this.provider.publicKey + user: PublicKey = this.provider.publicKey, + payer: PublicKey = this.provider.publicKey ) { let conditionalTokenMintAddrs = []; for (let i = 0; i < numOutcomes; i++) { @@ -412,7 +416,7 @@ export class ConditionalVaultClient { .preInstructions( conditionalTokenMintAddrs.map((conditionalTokenMint) => { return createAssociatedTokenAccountIdempotentInstruction( - this.provider.publicKey, + payer, getAssociatedTokenAddressSync(conditionalTokenMint, user), user, conditionalTokenMint @@ -439,35 +443,17 @@ export class ConditionalVaultClient { index: number, name: string, symbol: string, - uri: string - // underlyingTokenMint: PublicKey, - // proposalNumber: number, - // onFinalizeUri: string, - // onRevertUri: string + uri: string, + payer: PublicKey = this.provider.publicKey ) { - // const [underlyingTokenMetadata] = getMetadataAddr(underlyingTokenMint); - const [conditionalTokenMint] = getConditionalTokenMintAddr( this.vaultProgram.programId, vault, index ); - // const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( - // this.vaultProgram.programId, - // vault - // ); - // const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( - // this.vaultProgram.programId, - // vault - // ); - const [conditionalTokenMetadata] = getMetadataAddr(conditionalTokenMint); - // const [conditionalOnRevertTokenMetadata] = getMetadataAddr( - // conditionalOnRevertTokenMint - // ); - return this.vaultProgram.methods .addMetadataToConditionalTokens({ name, @@ -475,7 +461,7 @@ export class ConditionalVaultClient { uri, }) .accounts({ - payer: this.provider.publicKey, + payer, vault, conditionalTokenMint, conditionalTokenMetadata, diff --git a/sdk/src/v0.4/utils/priceMath.ts b/sdk/src/v0.4/utils/priceMath.ts index 4744a04..cebf00c 100644 --- a/sdk/src/v0.4/utils/priceMath.ts +++ b/sdk/src/v0.4/utils/priceMath.ts @@ -31,6 +31,7 @@ export type RemoveLiquiditySimulation = { export class AmmMath { // Re-export common methods from v0.3 + public static getHumanPriceFromReserves = V3AmmMath.getHumanPriceFromReserves; public static getAmmPriceFromReserves = V3AmmMath.getAmmPriceFromReserves; public static getChainAmount = V3AmmMath.getChainAmount; public static getHumanAmount = V3AmmMath.getHumanAmount;