diff --git a/.github/workflows/test-anchor.yml b/.github/workflows/test-anchor.yml new file mode 100644 index 0000000..ac6d1bd --- /dev/null +++ b/.github/workflows/test-anchor.yml @@ -0,0 +1,16 @@ +name: anchor-test +on: + push: + branches: + - master + pull_request: + +jobs: + run-anchor-test: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - uses: metadaoproject/anchor-test@v2.1 + with: + anchor-version: '0.29.0' + solana-cli-version: '1.17.23' \ No newline at end of file diff --git a/app/src/AmmClient.ts b/app/src/AmmClient.ts index accbc15..f18b490 100644 --- a/app/src/AmmClient.ts +++ b/app/src/AmmClient.ts @@ -1,10 +1,7 @@ import { AnchorProvider, Program } from "@coral-xyz/anchor"; -import { - AddressLookupTableAccount, - PublicKey, -} from "@solana/web3.js"; +import { AddressLookupTableAccount, PublicKey } from "@solana/web3.js"; -import { Amm as AmmIDLType, IDL as AmmIDL } from './types/amm'; +import { Amm as AmmIDLType, IDL as AmmIDL } from "./types/amm"; import * as ixs from "./instructions/amm"; import BN from "bn.js"; @@ -13,238 +10,230 @@ import { Amm, AmmPositionWrapper, AmmWrapper } from "./types"; import { filterPositionsByUser, filterPositionsByAmm } from "./utils"; export type CreateAmmClientParams = { - provider: AnchorProvider, - programId?: PublicKey, -} + provider: AnchorProvider; + programId?: PublicKey; +}; export class AmmClient { - public readonly provider: AnchorProvider; - public readonly program: Program; - public readonly luts: AddressLookupTableAccount[]; - - constructor( - provider: AnchorProvider, - ammProgramId: PublicKey, - luts: AddressLookupTableAccount[], - ) { - this.provider = provider - this.program = new Program(AmmIDL, ammProgramId, provider) - this.luts = luts - } - - public static async createClient(createAutocratClientParams: CreateAmmClientParams): Promise { - let { provider, programId } = createAutocratClientParams; - - const luts: AddressLookupTableAccount[] = [] - - return new AmmClient( - provider, - programId || AMM_PROGRAM_ID, - luts, - ) - } - - async createAmm( - baseMint: PublicKey, - quoteMint: PublicKey, - swapFeeBps: number, - permissionedCaller: PublicKey = PublicKey.default, - ltwapDecimals = 9 - ) { - return ixs.createAmmHandler( - this, - baseMint, - quoteMint, - swapFeeBps, - permissionedCaller, - ltwapDecimals - ) - } - - async createAmmPosition( - amm: PublicKey - ) { - return ixs.createAmmPositionHandler( - this, - amm - ) - } - - async addLiquidity( - ammAddr: PublicKey, - ammPositionAddr: PublicKey, - maxBaseAmount: BN, - maxQuoteAmount: BN, - minBaseAmount: BN, - minQuoteAmount: BN, - ) { - return ixs.addLiquidityHandler( - this, - ammAddr, - ammPositionAddr, - maxBaseAmount, - maxQuoteAmount, - minBaseAmount, - minQuoteAmount, - ) - } - - async removeLiquidity( - ammAddr: PublicKey, - ammPositionAddr: PublicKey, - removeBps: BN, - ) { - return ixs.removeLiquidityHandler( - this, - ammAddr, - ammPositionAddr, - removeBps - ) - } + public readonly provider: AnchorProvider; + public readonly program: Program; + public readonly luts: AddressLookupTableAccount[]; - async swap( - ammAddr: PublicKey, - isQuoteToBase: boolean, - inputAmount: BN, - minOutputAmount: BN, - ) { - return ixs.swapHandler( - this, - ammAddr, - isQuoteToBase, - inputAmount, - minOutputAmount, - ) - } - - async updateLTWAP( - ammAddr: PublicKey, - ) { - return ixs.updateLtwapHandler( - this, - ammAddr, - ) - } - - // getter functions - - async getLTWAP( - ammAddr: PublicKey, - ): Promise { - const amm = await this.program.account.amm.fetch(ammAddr); - return amm.ltwapLatest.toNumber() / 10 ** amm.ltwapDecimals - } - - async getAmm( - ammAddr: PublicKey, - ): Promise { - return await this.program.account.amm.fetch(ammAddr); - } - - async getAllAmms(): Promise { - return await this.program.account.amm.all(); + constructor( + provider: AnchorProvider, + ammProgramId: PublicKey, + luts: AddressLookupTableAccount[] + ) { + this.provider = provider; + this.program = new Program(AmmIDL, ammProgramId, provider); + this.luts = luts; + } + + public static async createClient( + createAutocratClientParams: CreateAmmClientParams + ): Promise { + let { provider, programId } = createAutocratClientParams; + + const luts: AddressLookupTableAccount[] = []; + + return new AmmClient(provider, programId || AMM_PROGRAM_ID, luts); + } + + async createAmm( + baseMint: PublicKey, + quoteMint: PublicKey, + swapFeeBps: number, + permissionedCaller: PublicKey = PublicKey.default, + ltwapDecimals = 9 + ) { + return ixs.createAmmHandler( + this, + baseMint, + quoteMint, + swapFeeBps, + permissionedCaller, + ltwapDecimals + ); + } + + async createAmmPosition(amm: PublicKey) { + return ixs.createAmmPositionHandler(this, amm); + } + + async addLiquidity( + ammAddr: PublicKey, + ammPositionAddr: PublicKey, + maxBaseAmount: BN, + maxQuoteAmount: BN, + minBaseAmount: BN, + minQuoteAmount: BN + ) { + return ixs.addLiquidityHandler( + this, + ammAddr, + ammPositionAddr, + maxBaseAmount, + maxQuoteAmount, + minBaseAmount, + minQuoteAmount + ); + } + + async removeLiquidity( + ammAddr: PublicKey, + ammPositionAddr: PublicKey, + removeBps: BN + ) { + return ixs.removeLiquidityHandler( + this, + ammAddr, + ammPositionAddr, + removeBps + ); + } + + async swap( + ammAddr: PublicKey, + isQuoteToBase: boolean, + inputAmount: BN, + minOutputAmount: BN + ) { + return ixs.swapHandler( + this, + ammAddr, + isQuoteToBase, + inputAmount, + minOutputAmount + ); + } + + async updateLTWAP(ammAddr: PublicKey) { + return ixs.updateLtwapHandler(this, ammAddr); + } + + // getter functions + + async getLTWAP(ammAddr: PublicKey): Promise { + const amm = await this.program.account.amm.fetch(ammAddr); + return amm.ltwapLatest.toNumber() / 10 ** amm.ltwapDecimals; + } + + async getAmm(ammAddr: PublicKey): Promise { + return await this.program.account.amm.fetch(ammAddr); + } + + async getAllAmms(): Promise { + return await this.program.account.amm.all(); + } + + async getAllUserPositions(): Promise { + try { + return await this.program.account.ammPosition.all([ + filterPositionsByUser(this.provider.wallet.publicKey), + ]); + } catch (e) { + return []; } - - async getAllUserPositions(): Promise { - try { - return await this.program.account.ammPosition.all([ - filterPositionsByUser(this.provider.wallet.publicKey) - ]) - } catch (e) { - return [] - } + } + + async getUserPositionForAmm( + ammAddr: PublicKey + ): Promise { + try { + return ( + await this.program.account.ammPosition.all([ + filterPositionsByUser(this.provider.wallet.publicKey), + filterPositionsByAmm(ammAddr), + ]) + )[0]; + } catch (e) { + return undefined; } - - async getUserPositionForAmm(ammAddr: PublicKey): Promise { - try { - return (await this.program.account.ammPosition.all([ - filterPositionsByUser(this.provider.wallet.publicKey), - filterPositionsByAmm(ammAddr) - ]))[0] - } catch (e) { - return undefined - } - } - - getSwapPreview( - amm: Amm, - inputAmount: BN, - isBuyBase: boolean - ): SwapPreview { - let quoteAmount = amm.quoteAmount - let baseAmount = amm.baseAmount - - let startPrice = (quoteAmount.toNumber() / 10 ** amm.quoteMintDecimals) / - (baseAmount.toNumber() / 10 ** amm.baseMintDecimals) - - let k = quoteAmount.mul(baseAmount) - - let inputMinusFee = inputAmount - .mul(new BN(10_000).sub(amm.swapFeeBps)) - .div(new BN(10_000)) - - if (isBuyBase) { - let tempQuoteAmount = quoteAmount.add(inputMinusFee) - let tempBaseAmount = k.div(tempQuoteAmount) - - let finalPrice = (tempQuoteAmount.toNumber() / 10 ** amm.quoteMintDecimals) / - (tempBaseAmount.toNumber() / 10 ** amm.baseMintDecimals) - - let outputAmountBase = baseAmount.sub(tempBaseAmount) - - let inputUnits = inputAmount.toNumber() / 10 ** amm.quoteMintDecimals - let outputUnits = outputAmountBase.toNumber() / 10 ** amm.baseMintDecimals - - let priceImpact = Math.abs(finalPrice - startPrice) / startPrice - - return { - isBuyBase, - inputAmount, - outputAmount: outputAmountBase, - inputUnits, - outputUnits, - startPrice, - finalPrice, - avgSwapPrice: inputUnits / outputUnits, - priceImpact - } - } else { - let tempBaseAmount = baseAmount.add(inputMinusFee) - let tempQuoteAmount = k.div(tempBaseAmount) - - let finalPrice = (tempQuoteAmount.toNumber() / 10 ** amm.quoteMintDecimals) / - (tempBaseAmount.toNumber() / 10 ** amm.baseMintDecimals) - - let outputAmountQuote = quoteAmount.sub(tempQuoteAmount) - - let inputUnits = inputAmount.toNumber() / 10 ** amm.baseMintDecimals - let outputUnits = outputAmountQuote.toNumber() / 10 ** amm.quoteMintDecimals - - let priceImpact = Math.abs(finalPrice - startPrice) / startPrice - - return { - isBuyBase, - inputAmount, - outputAmount: outputAmountQuote, - inputUnits, - outputUnits, - startPrice, - finalPrice, - avgSwapPrice: outputUnits / inputUnits, - priceImpact - } - } + } + + getSwapPreview(amm: Amm, inputAmount: BN, isBuyBase: boolean): SwapPreview { + let quoteAmount = amm.quoteAmount; + let baseAmount = amm.baseAmount; + + let startPrice = + quoteAmount.toNumber() / + 10 ** amm.quoteMintDecimals / + (baseAmount.toNumber() / 10 ** amm.baseMintDecimals); + + let k = quoteAmount.mul(baseAmount); + + let inputMinusFee = inputAmount + .mul(new BN(10_000).sub(amm.swapFeeBps)) + .div(new BN(10_000)); + + if (isBuyBase) { + let tempQuoteAmount = quoteAmount.add(inputMinusFee); + let tempBaseAmount = k.div(tempQuoteAmount); + + let finalPrice = + tempQuoteAmount.toNumber() / + 10 ** amm.quoteMintDecimals / + (tempBaseAmount.toNumber() / 10 ** amm.baseMintDecimals); + + let outputAmountBase = baseAmount.sub(tempBaseAmount); + + let inputUnits = inputAmount.toNumber() / 10 ** amm.quoteMintDecimals; + let outputUnits = + outputAmountBase.toNumber() / 10 ** amm.baseMintDecimals; + + let priceImpact = Math.abs(finalPrice - startPrice) / startPrice; + + return { + isBuyBase, + inputAmount, + outputAmount: outputAmountBase, + inputUnits, + outputUnits, + startPrice, + finalPrice, + avgSwapPrice: inputUnits / outputUnits, + priceImpact, + }; + } else { + let tempBaseAmount = baseAmount.add(inputMinusFee); + let tempQuoteAmount = k.div(tempBaseAmount); + + let finalPrice = + tempQuoteAmount.toNumber() / + 10 ** amm.quoteMintDecimals / + (tempBaseAmount.toNumber() / 10 ** amm.baseMintDecimals); + + let outputAmountQuote = quoteAmount.sub(tempQuoteAmount); + + let inputUnits = inputAmount.toNumber() / 10 ** amm.baseMintDecimals; + let outputUnits = + outputAmountQuote.toNumber() / 10 ** amm.quoteMintDecimals; + + let priceImpact = Math.abs(finalPrice - startPrice) / startPrice; + + return { + isBuyBase, + inputAmount, + outputAmount: outputAmountQuote, + inputUnits, + outputUnits, + startPrice, + finalPrice, + avgSwapPrice: outputUnits / inputUnits, + priceImpact, + }; } + } } export type SwapPreview = { - isBuyBase: boolean, - inputAmount: BN, - outputAmount: BN, - inputUnits: number, - outputUnits: number, - startPrice: number, - finalPrice: number, - avgSwapPrice: number, - priceImpact: number -} \ No newline at end of file + isBuyBase: boolean; + inputAmount: BN; + outputAmount: BN; + inputUnits: number; + outputUnits: number; + startPrice: number; + finalPrice: number; + avgSwapPrice: number; + priceImpact: number; +}; diff --git a/app/src/AutocratClient.ts b/app/src/AutocratClient.ts index 3af3b45..7726d01 100644 --- a/app/src/AutocratClient.ts +++ b/app/src/AutocratClient.ts @@ -1,283 +1,311 @@ import { AnchorProvider, Program } from "@coral-xyz/anchor"; import { - AccountMeta, - AddressLookupTableAccount, - Keypair, - PublicKey, + AccountMeta, + AddressLookupTableAccount, + Keypair, + PublicKey, } from "@solana/web3.js"; -import { Autocrat as AutocratIDLType, IDL as AutocratIDL } from './types/autocrat'; +import { + Autocrat as AutocratIDLType, + IDL as AutocratIDL, +} from "./types/autocrat"; import * as ixs from "./instructions/autocrat"; import BN from "bn.js"; -import { AMM_PROGRAM_ID, AUTOCRAT_LUTS, AUTOCRAT_PROGRAM_ID } from "./constants"; -import { ProposalInstruction, UpdateDaoParams, Dao, DaoTreasury, Proposal, ProposalWrapper, ProposalVault, ProposalInstructions } from "./types"; -import { getDaoAddr, getDaoTreasuryAddr, getProposalAddr, getProposalVaultAddr } from "./utils"; +import { + AMM_PROGRAM_ID, + AUTOCRAT_LUTS, + AUTOCRAT_PROGRAM_ID, +} from "./constants"; +import { + ProposalInstruction, + UpdateDaoParams, + Dao, + DaoTreasury, + Proposal, + ProposalWrapper, + ProposalVault, + ProposalInstructions, +} from "./types"; +import { getDaoAddr, getDaoTreasuryAddr, getProposalAddr } from "./utils"; export type CreateAutocratClientParams = { - provider: AnchorProvider, - programId?: PublicKey, -} + provider: AnchorProvider; + programId?: PublicKey; +}; export class AutocratClient { - public readonly provider: AnchorProvider; - public readonly program: Program; - public readonly luts: AddressLookupTableAccount[]; - - constructor( - provider: AnchorProvider, - programId: PublicKey, - luts: AddressLookupTableAccount[], - ) { - this.provider = provider - this.program = new Program(AutocratIDL, programId, provider) - this.luts = luts - } - - public static async createClient(createAutocratClientParams: CreateAutocratClientParams): Promise { - let { provider, programId } = createAutocratClientParams; - - const getLuts = () => Promise.all( - AUTOCRAT_LUTS.map(lut => { - return provider.connection - .getAddressLookupTable(lut) - .then((res) => res.value as AddressLookupTableAccount) - }) - ) - - const luts = await getLuts() - - return new AutocratClient( - provider, - programId || AUTOCRAT_PROGRAM_ID, - luts as AddressLookupTableAccount[], - ) - } - - async initializeDao( - metaMint: PublicKey, - usdcMint: PublicKey - ) { - return ixs.initializeDaoHandler( - this, - metaMint, - usdcMint - ) - } - - // this won't ever be called directly (must be called via a proposal), but is here anyway for completeness / testing - async updateDao( - updateDaoParams: UpdateDaoParams - ) { - return ixs.updateDaoHandler( - this, - updateDaoParams - ) - } - - async createProposalInstructions( - proposalNumber: number, - instructions: ProposalInstruction[], - ) { - return ixs.createProposalInstructionsHandler( - this, - proposalNumber, - instructions, - ) - } - - async addProposalInstructions( - proposalNumber: number, - instructions: ProposalInstruction[], - ) { - return ixs.addProposalInstructionsHandler( - this, - proposalNumber, - instructions, - ) - } - - async createProposal( - proposalNumber: number, - descriptionUrl: string, - condMetaToMint: BN, - condUsdcToMint: BN, - ) { - return ixs.createProposalHandler( - this, - proposalNumber, - descriptionUrl, - condMetaToMint, - condUsdcToMint, - ) - } - - async createProposalMarketSide( - proposalNumber: number, - isPassMarket: boolean, - ammBaseAmountDeposit: BN, - ammQuoteAmountDeposit: BN, - ammProgram = AMM_PROGRAM_ID, - ) { - return ixs.createProposalMarketSideHandler( - this, - proposalNumber, - isPassMarket, - ammBaseAmountDeposit, - ammQuoteAmountDeposit, - ammProgram - ) - } - - async submitProposal( - proposalNumber: number, - ammProgram = AMM_PROGRAM_ID, - ) { - return ixs.submitProposalHandler( - this, - proposalNumber, - ammProgram - ) - } - - async finalizeProposal( - proposalNumber: number, - accounts: AccountMeta[] - ) { - return ixs.finalizeProposalHandler( - this, - proposalNumber, - accounts - ) - } - - async mintConditionalTokens( - proposalAddr: PublicKey, - metaAmount: BN, - usdcAmount: BN, - ) { - return ixs.mintConditionalTokensHandler( - this, - proposalAddr, - metaAmount, - usdcAmount, - ) - } - - async mergeConditionalTokens( - proposalAddr: PublicKey, - metaAmount: BN, - usdcAmount: BN, - ) { - return ixs.mergeConditionalTokensHandler( - this, - proposalAddr, - metaAmount, - usdcAmount, - ) - } - - async redeemConditionalTokens( - proposalAddr: PublicKey - ) { - return ixs.redeemConditionalTokensHandler( - this, - proposalAddr, - ) - } - - // amm cpi functions - - async createAmmPositionCpi( - proposalAddr: PublicKey, - amm: PublicKey, - ammProgram = AMM_PROGRAM_ID, - ) { - return ixs.createAmmPositionCpiHandler( - this, - proposalAddr, - amm, - ammProgram - ) - } - - async addLiquidityCpi( - proposalAddr: PublicKey, - ammAddr: PublicKey, - maxBaseAmount: BN, - maxQuoteAmount: BN, - minBaseAmount: BN, - minQuoteAmount: BN, - ammProgram = AMM_PROGRAM_ID, - ) { - return ixs.addLiquidityCpiHandler( - this, - proposalAddr, - ammAddr, - maxBaseAmount, - maxQuoteAmount, - minBaseAmount, - minQuoteAmount, - ammProgram - ) - } - - async removeLiquidityCpi( - proposalAddr: PublicKey, - ammAddr: PublicKey, - removeBps: BN, - ammProgram = AMM_PROGRAM_ID, - ) { - return ixs.removeLiquidityCpiHandler( - this, - proposalAddr, - ammAddr, - removeBps, - ammProgram - ) - } - - async swapCpi( - proposalAddr: PublicKey, - ammAddr: PublicKey, - isQuoteToBase: boolean, - inputAmount: BN, - minOutputAmount: BN, - ammProgram = AMM_PROGRAM_ID, - ) { - return ixs.swapCpiHandler( - this, - proposalAddr, - ammAddr, - isQuoteToBase, - inputAmount, - minOutputAmount, - ammProgram - ) - } - - // getter functions - - async getDao(): Promise { - return await this.program.account.dao.fetch(getDaoAddr(this.program.programId)[0]); - } - - async getDaoTreasury(): Promise { - return await this.program.account.daoTreasury.fetch(getDaoTreasuryAddr(this.program.programId)[0]); - } - - async getAllProposals(): Promise { - return await this.program.account.proposal.all(); - } - - async getProposalByNumber(proposalNumber: number): Promise { - return await this.program.account.proposal.fetch(getProposalAddr(this.program.programId, proposalNumber)[0]); - } - - async getProposalInstructionsByNumber(proposalNumber: number): Promise { - const proposal = await this.getProposalByNumber(proposalNumber) - return await this.program.account.proposalInstructions.fetch(proposal.instructions); - } + public readonly provider: AnchorProvider; + public readonly program: Program; + public readonly luts: AddressLookupTableAccount[]; + constructor( + provider: AnchorProvider, + programId: PublicKey, + luts: AddressLookupTableAccount[] + ) { + this.provider = provider; + this.program = new Program( + AutocratIDL, + programId, + provider + ); + this.luts = luts; + } + + public static async createClient( + createAutocratClientParams: CreateAutocratClientParams + ): Promise { + let { provider, programId } = createAutocratClientParams; + + const getLuts = () => + Promise.all( + AUTOCRAT_LUTS.map((lut) => { + return provider.connection + .getAddressLookupTable(lut) + .then((res) => res.value as AddressLookupTableAccount); + }) + ); + + const luts = await getLuts(); + + return new AutocratClient( + provider, + programId || AUTOCRAT_PROGRAM_ID, + luts as AddressLookupTableAccount[] + ); + } + + async initializeDao( + daoId: PublicKey, + metaMint: PublicKey, + usdcMint: PublicKey + ) { + return ixs.initializeDaoHandler(this, daoId, metaMint, usdcMint); + } + + // this won't ever be called directly (must be called via a proposal), but is here anyway for completeness / testing + async updateDao(daoId: PublicKey, updateDaoParams: UpdateDaoParams) { + return ixs.updateDaoHandler(this, daoId, updateDaoParams); + } + + async createProposalInstructions( + daoId: PublicKey, + proposalNumber: number, + instructions: ProposalInstruction[] + ) { + return ixs.createProposalInstructionsHandler( + this, + daoId, + proposalNumber, + instructions + ); + } + + async addProposalInstructions( + daoId: PublicKey, + proposalNumber: number, + instructions: ProposalInstruction[] + ) { + return ixs.addProposalInstructionsHandler( + this, + daoId, + proposalNumber, + instructions + ); + } + + async createProposal( + daoId: PublicKey, + proposalNumber: number, + descriptionUrl: string, + condMetaToMint: BN, + condUsdcToMint: BN + ) { + return ixs.createProposalHandler( + this, + daoId, + proposalNumber, + descriptionUrl, + condMetaToMint, + condUsdcToMint + ); + } + + async createProposalMarketSide( + daoId: PublicKey, + proposalNumber: number, + isPassMarket: boolean, + ammBaseAmountDeposit: BN, + ammQuoteAmountDeposit: BN, + ammProgram = AMM_PROGRAM_ID + ) { + return ixs.createProposalMarketSideHandler( + this, + daoId, + proposalNumber, + isPassMarket, + ammBaseAmountDeposit, + ammQuoteAmountDeposit, + ammProgram + ); + } + + async submitProposal( + daoId: PublicKey, + proposalNumber: number, + ammProgram = AMM_PROGRAM_ID + ) { + return ixs.submitProposalHandler(this, daoId, proposalNumber, ammProgram); + } + + async finalizeProposal( + daoId: PublicKey, + proposalNumber: number, + accounts: AccountMeta[] + ) { + return ixs.finalizeProposalHandler(this, daoId, proposalNumber, accounts); + } + + async mintConditionalTokens( + proposalAddr: PublicKey, + metaAmount: BN, + usdcAmount: BN + ) { + return ixs.mintConditionalTokensHandler( + this, + proposalAddr, + metaAmount, + usdcAmount + ); + } + + async mergeConditionalTokens( + proposalAddr: PublicKey, + metaAmount: BN, + usdcAmount: BN + ) { + return ixs.mergeConditionalTokensHandler( + this, + proposalAddr, + metaAmount, + usdcAmount + ); + } + + async redeemConditionalTokens(daoId: PublicKey, proposalAddr: PublicKey) { + return ixs.redeemConditionalTokensHandler(this, proposalAddr); + } + + // amm cpi functions + + async createAmmPositionCpi( + proposalAddr: PublicKey, + amm: PublicKey, + ammProgram = AMM_PROGRAM_ID + ) { + return ixs.createAmmPositionCpiHandler(this, proposalAddr, amm, ammProgram); + } + + async addLiquidityCpi( + proposalAddr: PublicKey, + ammAddr: PublicKey, + maxBaseAmount: BN, + maxQuoteAmount: BN, + minBaseAmount: BN, + minQuoteAmount: BN, + ammProgram = AMM_PROGRAM_ID + ) { + return ixs.addLiquidityCpiHandler( + this, + proposalAddr, + ammAddr, + maxBaseAmount, + maxQuoteAmount, + minBaseAmount, + minQuoteAmount, + ammProgram + ); + } + + async removeLiquidityCpi( + proposalAddr: PublicKey, + ammAddr: PublicKey, + removeBps: BN, + ammProgram = AMM_PROGRAM_ID + ) { + return ixs.removeLiquidityCpiHandler( + this, + proposalAddr, + ammAddr, + removeBps, + ammProgram + ); + } + + async swapCpi( + proposalAddr: PublicKey, + ammAddr: PublicKey, + isQuoteToBase: boolean, + inputAmount: BN, + minOutputAmount: BN, + ammProgram = AMM_PROGRAM_ID + ) { + return ixs.swapCpiHandler( + this, + proposalAddr, + ammAddr, + isQuoteToBase, + inputAmount, + minOutputAmount, + ammProgram + ); + } + + // getter functions + + async getDao(daoId: PublicKey): Promise { + return await this.program.account.dao.fetch( + getDaoAddr(this.program.programId, daoId)[0] + ); + } + + async getDaoTreasury(daoId: PublicKey): Promise { + return await this.program.account.daoTreasury.fetch( + getDaoTreasuryAddr(this.program.programId, daoId)[0] + ); + } + + async getAllProposals(daoId: PublicKey): Promise { + return await this.program.account.proposal.all([ + { + memcmp: { + offset: 8, + bytes: getDaoAddr(this.program.programId, daoId)[0].toBase58(), + }, + }, + ]); + } + + async getProposalByNumber( + daoId: PublicKey, + proposalNumber: number + ): Promise { + const daoAddr = getDaoAddr(this.program.programId, daoId)[0]; + return await this.program.account.proposal.fetch( + getProposalAddr(this.program.programId, daoAddr, proposalNumber)[0] + ); + } + + async getProposalInstructionsByNumber( + daoId: PublicKey, + proposalNumber: number + ): Promise { + const daoAddr = getDaoAddr(this.program.programId, daoId)[0]; + const proposal = await this.getProposalByNumber(daoAddr, proposalNumber); + return await this.program.account.proposalInstructions.fetch( + proposal.instructions + ); + } } - diff --git a/app/src/InstructionHandler.ts b/app/src/InstructionHandler.ts index 1724bd8..629c680 100644 --- a/app/src/InstructionHandler.ts +++ b/app/src/InstructionHandler.ts @@ -1,13 +1,13 @@ import { - AddressLookupTableAccount, - Blockhash, - ConfirmOptions, - Keypair, - Signer, - Transaction, - TransactionInstruction, - TransactionMessage, - VersionedTransaction, + AddressLookupTableAccount, + Blockhash, + ConfirmOptions, + Keypair, + Signer, + Transaction, + TransactionInstruction, + TransactionMessage, + VersionedTransaction, } from "@solana/web3.js"; import { BanksClient } from "solana-bankrun"; import { AutocratClient } from "./AutocratClient"; @@ -15,126 +15,136 @@ import { addComputeUnits, addPriorityFee } from "./utils"; import { AmmClient } from "./AmmClient"; import { AnchorProvider, Program } from "@coral-xyz/anchor"; -export type SignerOrKeypair = Signer | Keypair +export type SignerOrKeypair = Signer | Keypair; interface Client { - provider: AnchorProvider; - program: ProgramType; - luts: AddressLookupTableAccount[]; + provider: AnchorProvider; + program: ProgramType; + luts: AddressLookupTableAccount[]; } -export class InstructionHandler = Client> { - public instructions: TransactionInstruction[]; - public signers: Set; - public client: Type; - - public computeUnits = 200_000; - public microLamportsPerComputeUnit = 0; - - public preInstructions: TransactionInstruction[]; - public postInstructions: TransactionInstruction[]; - - constructor( - instructions: TransactionInstruction[], - signers: SignerOrKeypair[], - client: Type, - ) { - this.instructions = instructions - - this.signers = new Set() - signers.forEach(s => this.signers.add(s)) - - this.client = client - - this.preInstructions = [] - this.postInstructions = [] +export class InstructionHandler< + ProgramType, + Type extends Client = Client +> { + public instructions: TransactionInstruction[]; + public signers: Set; + public client: Type; + + public computeUnits = 200_000; + public microLamportsPerComputeUnit = 0; + + public preInstructions: TransactionInstruction[]; + public postInstructions: TransactionInstruction[]; + + constructor( + instructions: TransactionInstruction[], + signers: SignerOrKeypair[], + client: Type + ) { + this.instructions = instructions; + + this.signers = new Set(); + signers.forEach((s) => this.signers.add(s)); + + this.client = client; + + this.preInstructions = []; + this.postInstructions = []; + } + + addPreInstructions( + instructions: TransactionInstruction[], + signers: SignerOrKeypair[] = [] + ): InstructionHandler { + this.preInstructions = [...instructions, ...this.preInstructions]; + signers.forEach((s) => this.signers.add(s)); + return this; + } + + addPostInstructions( + instructions: TransactionInstruction[], + signers: SignerOrKeypair[] = [] + ): InstructionHandler { + this.postInstructions = [...instructions, ...this.postInstructions]; + signers.forEach((s) => this.signers.add(s)); + return this; + } + + async getVersionedTransaction(blockhash: Blockhash) { + this.instructions = [ + ...this.preInstructions, + ...this.instructions, + ...this.postInstructions, + ]; + + if (this.microLamportsPerComputeUnit != 0) { + this.instructions = [ + addPriorityFee(this.microLamportsPerComputeUnit), + ...this.instructions, + ]; } - addPreInstructions(instructions: TransactionInstruction[], signers: SignerOrKeypair[] = []): InstructionHandler { - this.preInstructions = [ - ...instructions, - ...this.preInstructions - ] - signers.forEach(s => this.signers.add(s)) - return this + if (this.computeUnits != 200_000) { + this.instructions = [ + addComputeUnits(this.computeUnits), + ...this.instructions, + ]; } - addPostInstructions(instructions: TransactionInstruction[], signers: SignerOrKeypair[] = []): InstructionHandler { - this.postInstructions = [ - ...instructions, - ...this.postInstructions - ] - signers.forEach(s => this.signers.add(s)) - return this - } - - async getVersionedTransaction(blockhash: Blockhash) { - this.instructions = [ - ...this.preInstructions, - ...this.instructions, - ...this.postInstructions, - ] - - if (this.microLamportsPerComputeUnit != 0) { - this.instructions = [ - addPriorityFee(this.microLamportsPerComputeUnit), - ...this.instructions - ] - } - - if (this.computeUnits != 200_000) { - this.instructions = [ - addComputeUnits(this.computeUnits), - ...this.instructions - ] - } - - const message = new TransactionMessage({ - payerKey: this.client.provider.wallet.publicKey, - recentBlockhash: blockhash, - instructions: this.instructions, - }).compileToV0Message(this.client.luts); - - let tx = new VersionedTransaction(message) - - let signersArray = Array.from(this.signers) - if (this.signers.size) { - tx.sign(signersArray) - } - - return tx - } + const message = new TransactionMessage({ + payerKey: this.client.provider.wallet.publicKey, + recentBlockhash: blockhash, + instructions: this.instructions, + }).compileToV0Message(this.client.luts); - setComputeUnits(computeUnits: number): InstructionHandler { - this.computeUnits = computeUnits - return this - } + let tx = new VersionedTransaction(message); - setPriorityFee(microLamportsPerComputeUnit: number): InstructionHandler { - this.microLamportsPerComputeUnit = microLamportsPerComputeUnit - return this + let signersArray = Array.from(this.signers); + if (this.signers.size) { + tx.sign(signersArray); } - async bankrun(banksClient: BanksClient) { - try { - let [blockhash] = (await banksClient.getLatestBlockhash())!; - const tx = await this.getVersionedTransaction(blockhash); - return await banksClient.processTransaction(tx); - } catch (e) { - console.log(e) - throw e - } + return tx; + } + + setComputeUnits(computeUnits: number): InstructionHandler { + this.computeUnits = computeUnits; + return this; + } + + setPriorityFee( + microLamportsPerComputeUnit: number + ): InstructionHandler { + this.microLamportsPerComputeUnit = microLamportsPerComputeUnit; + return this; + } + + async bankrun(banksClient: BanksClient) { + try { + let [blockhash] = (await banksClient.getLatestBlockhash())!; + const tx = await this.getVersionedTransaction(blockhash); + return await banksClient.processTransaction(tx); + } catch (e) { + console.log(e); + throw e; } - - async rpc(opts: ConfirmOptions = { skipPreflight: true }) { - try { - let blockhash = (await this.client.provider.connection.getLatestBlockhash()).blockhash - let tx = await this.getVersionedTransaction(blockhash); - tx = await this.client.provider.wallet.signTransaction(tx) - return await this.client.provider.connection.sendRawTransaction(tx.serialize(), opts) - } catch (e) { - console.log(e) - throw e - } + } + + async rpc(opts: ConfirmOptions = { skipPreflight: true }) { + try { + let blockhash = ( + await this.client.provider.connection.getLatestBlockhash() + ).blockhash; + let tx = await this.getVersionedTransaction(blockhash); + tx = await this.client.provider.wallet.signTransaction(tx); + return await this.client.provider.connection.sendRawTransaction( + tx.serialize(), + opts + ); + } catch (e) { + console.log(e); + throw e; } -} \ No newline at end of file + } +} diff --git a/app/src/constants.ts b/app/src/constants.ts index c1e4589..c4b490e 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -1,9 +1,17 @@ import { PublicKey } from "@solana/web3.js"; -export const AUTOCRAT_PROGRAM_ID = new PublicKey("66629qDqH5vJuz4ZgaL1HVpeAC9kJXnzamMpvMJfr3kE") -export const AMM_PROGRAM_ID = new PublicKey("Ens7Gx99whnA8zZm6ZiFnWgGq3x76nXbSmh5gaaJqpAz") +export const AUTOCRAT_PROGRAM_ID = new PublicKey( + "66629qDqH5vJuz4ZgaL1HVpeAC9kJXnzamMpvMJfr3kE" +); +export const AMM_PROGRAM_ID = new PublicKey( + "Ens7Gx99whnA8zZm6ZiFnWgGq3x76nXbSmh5gaaJqpAz" +); -export const META_MINT = new PublicKey("3gN1WVEJwSHNWjo7hr87DgZp6zkf8kWgAJD29DmfE2Gr") -export const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") +export const META_MINT = new PublicKey( + "3gN1WVEJwSHNWjo7hr87DgZp6zkf8kWgAJD29DmfE2Gr" +); +export const USDC_MINT = new PublicKey( + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" +); -export const AUTOCRAT_LUTS: PublicKey[] = [] +export const AUTOCRAT_LUTS: PublicKey[] = []; diff --git a/app/src/index.ts b/app/src/index.ts index 75a502a..19772c2 100644 --- a/app/src/index.ts +++ b/app/src/index.ts @@ -1,5 +1,5 @@ -export * from './types' -export * from './utils' -export * from './constants' -export * from './AmmClient' -export * from './AutocratClient' +export * from "./types"; +export * from "./utils"; +export * from "./constants"; +export * from "./AmmClient"; +export * from "./AutocratClient"; diff --git a/app/src/instructions/amm/addLiquidity.ts b/app/src/instructions/amm/addLiquidity.ts index 828cd50..85d575c 100644 --- a/app/src/instructions/amm/addLiquidity.ts +++ b/app/src/instructions/amm/addLiquidity.ts @@ -1,40 +1,35 @@ import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA } from '../../utils'; +import { getATA } from "../../utils"; import BN from "bn.js"; import { AmmClient } from "../../AmmClient"; export const addLiquidityHandler = async ( - client: AmmClient, - ammAddr: PublicKey, - ammPositionAddr: PublicKey, - maxBaseAmount: BN, - maxQuoteAmount: BN, - minBaseAmount: BN, - minQuoteAmount: BN, + client: AmmClient, + ammAddr: PublicKey, + ammPositionAddr: PublicKey, + maxBaseAmount: BN, + maxQuoteAmount: BN, + minBaseAmount: BN, + minQuoteAmount: BN ): Promise> => { - const amm = await client.program.account.amm.fetch(ammAddr); + const amm = await client.program.account.amm.fetch(ammAddr); - let ix = await client.program.methods - .addLiquidity( - maxBaseAmount, - maxQuoteAmount, - minBaseAmount, - minQuoteAmount - ) - .accounts({ - user: client.provider.publicKey, - amm: ammAddr, - ammPosition: ammPositionAddr, - baseMint: amm.baseMint, - quoteMint: amm.quoteMint, - userAtaBase: getATA(amm.baseMint, client.provider.publicKey)[0], - userAtaQuote: getATA(amm.quoteMint, client.provider.publicKey)[0], - vaultAtaBase: getATA(amm.baseMint, ammAddr)[0], - vaultAtaQuote: getATA(amm.quoteMint, ammAddr)[0], - authPda: null, - }) - .instruction() + let ix = await client.program.methods + .addLiquidity(maxBaseAmount, maxQuoteAmount, minBaseAmount, minQuoteAmount) + .accounts({ + user: client.provider.publicKey, + amm: ammAddr, + ammPosition: ammPositionAddr, + baseMint: amm.baseMint, + quoteMint: amm.quoteMint, + userAtaBase: getATA(amm.baseMint, client.provider.publicKey)[0], + userAtaQuote: getATA(amm.quoteMint, client.provider.publicKey)[0], + vaultAtaBase: getATA(amm.baseMint, ammAddr)[0], + vaultAtaQuote: getATA(amm.quoteMint, ammAddr)[0], + authPda: null, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/amm/createAmm.ts b/app/src/instructions/amm/createAmm.ts index fbf1fc0..46590c2 100644 --- a/app/src/instructions/amm/createAmm.ts +++ b/app/src/instructions/amm/createAmm.ts @@ -1,44 +1,44 @@ import { PublicKey } from "@solana/web3.js"; import { AmmClient } from "../../AmmClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA, getAmmAddr } from '../../utils'; +import { getATA, getAmmAddr } from "../../utils"; import BN from "bn.js"; export const createAmmHandler = async ( - client: AmmClient, - baseMint: PublicKey, - quoteMint: PublicKey, - swapFeeBps: number, - permissionedCaller: PublicKey, - ltwapDecimals: number, + client: AmmClient, + baseMint: PublicKey, + quoteMint: PublicKey, + swapFeeBps: number, + permissionedCaller: PublicKey, + ltwapDecimals: number ): Promise> => { - let [ammAddr] = getAmmAddr( - client.program.programId, - baseMint, - quoteMint, - swapFeeBps, - permissionedCaller - ) + let [ammAddr] = getAmmAddr( + client.program.programId, + baseMint, + quoteMint, + swapFeeBps, + permissionedCaller + ); - let [vaultAtaBase] = getATA(baseMint, ammAddr) - let [vaultAtaQuote] = getATA(quoteMint, ammAddr) + let [vaultAtaBase] = getATA(baseMint, ammAddr); + let [vaultAtaQuote] = getATA(quoteMint, ammAddr); - let ix = await client.program.methods - .createAmm({ - permissionedCaller, - swapFeeBps: new BN(swapFeeBps), - ltwapDecimals - }) - .accounts({ - user: client.provider.publicKey, - amm: ammAddr, - baseMint, - quoteMint, - vaultAtaBase, - vaultAtaQuote, - authPda: null - }) - .instruction() + let ix = await client.program.methods + .createAmm({ + permissionedCaller, + swapFeeBps: new BN(swapFeeBps), + ltwapDecimals, + }) + .accounts({ + user: client.provider.publicKey, + amm: ammAddr, + baseMint, + quoteMint, + vaultAtaBase, + vaultAtaQuote, + authPda: null, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/amm/createAmmPosition.ts b/app/src/instructions/amm/createAmmPosition.ts index 9be1d92..d78242c 100644 --- a/app/src/instructions/amm/createAmmPosition.ts +++ b/app/src/instructions/amm/createAmmPosition.ts @@ -1,21 +1,25 @@ import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; import { AmmClient } from "../../AmmClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getAmmPositionAddr } from '../../utils'; +import { getAmmPositionAddr } from "../../utils"; export const createAmmPositionHandler = async ( - client: AmmClient, - amm: PublicKey, + client: AmmClient, + amm: PublicKey ): Promise> => { - let ix = await client.program.methods - .createPosition() - .accounts({ - user: client.provider.publicKey, - amm, - ammPosition: getAmmPositionAddr(client.program.programId, amm, client.provider.publicKey)[0], - authPda: null, - }) - .instruction() + let ix = await client.program.methods + .createPosition() + .accounts({ + user: client.provider.publicKey, + amm, + ammPosition: getAmmPositionAddr( + client.program.programId, + amm, + client.provider.publicKey + )[0], + authPda: null, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/amm/index.ts b/app/src/instructions/amm/index.ts index 49d2623..b52fed5 100644 --- a/app/src/instructions/amm/index.ts +++ b/app/src/instructions/amm/index.ts @@ -1,6 +1,6 @@ -export * from './addLiquidity' -export * from './createAmm' -export * from './createAmmPosition' -export * from './removeLiquidity' -export * from './swap' -export * from './updateLtwap' +export * from "./addLiquidity"; +export * from "./createAmm"; +export * from "./createAmmPosition"; +export * from "./removeLiquidity"; +export * from "./swap"; +export * from "./updateLtwap"; diff --git a/app/src/instructions/amm/removeLiquidity.ts b/app/src/instructions/amm/removeLiquidity.ts index c86d0d4..8744f77 100644 --- a/app/src/instructions/amm/removeLiquidity.ts +++ b/app/src/instructions/amm/removeLiquidity.ts @@ -1,34 +1,32 @@ import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA } from '../../utils'; +import { getATA } from "../../utils"; import BN from "bn.js"; import { AmmClient } from "../../AmmClient"; export const removeLiquidityHandler = async ( - client: AmmClient, - ammAddr: PublicKey, - ammPositionAddr: PublicKey, - removeBps: BN, + client: AmmClient, + ammAddr: PublicKey, + ammPositionAddr: PublicKey, + removeBps: BN ): Promise> => { - const amm = await client.program.account.amm.fetch(ammAddr); + const amm = await client.program.account.amm.fetch(ammAddr); - let ix = await client.program.methods - .removeLiquidity( - removeBps, - ) - .accounts({ - user: client.provider.publicKey, - amm: ammAddr, - ammPosition: ammPositionAddr, - baseMint: amm.baseMint, - quoteMint: amm.quoteMint, - userAtaBase: getATA(amm.baseMint, client.provider.publicKey)[0], - userAtaQuote: getATA(amm.quoteMint, client.provider.publicKey)[0], - vaultAtaBase: getATA(amm.baseMint, ammAddr)[0], - vaultAtaQuote: getATA(amm.quoteMint, ammAddr)[0], - authPda: null, - }) - .instruction() + let ix = await client.program.methods + .removeLiquidity(removeBps) + .accounts({ + user: client.provider.publicKey, + amm: ammAddr, + ammPosition: ammPositionAddr, + baseMint: amm.baseMint, + quoteMint: amm.quoteMint, + userAtaBase: getATA(amm.baseMint, client.provider.publicKey)[0], + userAtaQuote: getATA(amm.quoteMint, client.provider.publicKey)[0], + vaultAtaBase: getATA(amm.baseMint, ammAddr)[0], + vaultAtaQuote: getATA(amm.quoteMint, ammAddr)[0], + authPda: null, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/amm/swap.ts b/app/src/instructions/amm/swap.ts index 8d6eea7..5e1e661 100644 --- a/app/src/instructions/amm/swap.ts +++ b/app/src/instructions/amm/swap.ts @@ -1,36 +1,32 @@ import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA } from '../../utils'; +import { getATA } from "../../utils"; import BN from "bn.js"; import { AmmClient } from "../../AmmClient"; export const swapHandler = async ( - client: AmmClient, - ammAddr: PublicKey, - isQuoteToBase: boolean, - inputAmount: BN, - minOutputAmount: BN, + client: AmmClient, + ammAddr: PublicKey, + isQuoteToBase: boolean, + inputAmount: BN, + minOutputAmount: BN ): Promise> => { - const amm = await client.program.account.amm.fetch(ammAddr); + const amm = await client.program.account.amm.fetch(ammAddr); - let ix = await client.program.methods - .swap( - isQuoteToBase, - inputAmount, - minOutputAmount, - ) - .accounts({ - user: client.provider.publicKey, - amm: ammAddr, - baseMint: amm.baseMint, - quoteMint: amm.quoteMint, - userAtaBase: getATA(amm.baseMint, client.provider.publicKey)[0], - userAtaQuote: getATA(amm.quoteMint, client.provider.publicKey)[0], - vaultAtaBase: getATA(amm.baseMint, ammAddr)[0], - vaultAtaQuote: getATA(amm.quoteMint, ammAddr)[0], - authPda: null, - }) - .instruction() + let ix = await client.program.methods + .swap(isQuoteToBase, inputAmount, minOutputAmount) + .accounts({ + user: client.provider.publicKey, + amm: ammAddr, + baseMint: amm.baseMint, + quoteMint: amm.quoteMint, + userAtaBase: getATA(amm.baseMint, client.provider.publicKey)[0], + userAtaQuote: getATA(amm.quoteMint, client.provider.publicKey)[0], + vaultAtaBase: getATA(amm.baseMint, ammAddr)[0], + vaultAtaQuote: getATA(amm.quoteMint, ammAddr)[0], + authPda: null, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/amm/updateLtwap.ts b/app/src/instructions/amm/updateLtwap.ts index f45783a..ffb31d2 100644 --- a/app/src/instructions/amm/updateLtwap.ts +++ b/app/src/instructions/amm/updateLtwap.ts @@ -3,17 +3,17 @@ import { InstructionHandler } from "../../InstructionHandler"; import { AmmClient } from "../../AmmClient"; export const updateLtwapHandler = async ( - client: AmmClient, - ammAddr: PublicKey, + client: AmmClient, + ammAddr: PublicKey ): Promise> => { - let ix = await client.program.methods - .updateLtwap(null) - .accounts({ - user: client.provider.publicKey, - amm: ammAddr, - authPda: null, - }) - .instruction() + let ix = await client.program.methods + .updateLtwap(null) + .accounts({ + user: client.provider.publicKey, + amm: ammAddr, + authPda: null, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/addProposalInstructions.ts b/app/src/instructions/autocrat/addProposalInstructions.ts index da7f686..3004c38 100644 --- a/app/src/instructions/autocrat/addProposalInstructions.ts +++ b/app/src/instructions/autocrat/addProposalInstructions.ts @@ -1,27 +1,40 @@ import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { ProposalInstruction } from '../../types'; +import { ProposalInstruction } from "../../types"; import { PublicKey } from "@solana/web3.js"; -import { getProposalAddr, getProposalInstructionsAddr } from "../../utils"; +import { + getDaoAddr, + getProposalAddr, + getProposalInstructionsAddr, +} from "../../utils"; export const addProposalInstructionsHandler = async ( - client: AutocratClient, - proposalNumber: number, - instructions: ProposalInstruction[], + client: AutocratClient, + daoId: PublicKey, + proposalNumber: number, + instructions: ProposalInstruction[] ): Promise> => { + let daoAddr = getDaoAddr(client.program.programId, daoId)[0]; - let proposalAddr = getProposalAddr(client.program.programId, proposalNumber)[0] + let proposalAddr = getProposalAddr( + client.program.programId, + daoAddr, + proposalNumber + )[0]; - let proposalInstructionsAddr = getProposalInstructionsAddr(client.program.programId, proposalAddr)[0] + let proposalInstructionsAddr = getProposalInstructionsAddr( + client.program.programId, + proposalAddr + )[0]; - let ix = await client.program.methods - .addProposalInstructions(instructions) - .accounts({ - proposer: client.provider.publicKey, - proposal: proposalAddr, - proposalInstructions: proposalInstructionsAddr, - }) - .instruction() + let ix = await client.program.methods + .addProposalInstructions(instructions) + .accounts({ + proposer: client.provider.publicKey, + proposal: proposalAddr, + proposalInstructions: proposalInstructionsAddr, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/ammCpi/addLiquidityCpi.ts b/app/src/instructions/autocrat/ammCpi/addLiquidityCpi.ts index eff6b37..56a4263 100644 --- a/app/src/instructions/autocrat/ammCpi/addLiquidityCpi.ts +++ b/app/src/instructions/autocrat/ammCpi/addLiquidityCpi.ts @@ -1,66 +1,83 @@ -import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; import { AutocratClient } from "../../../AutocratClient"; import { InstructionHandler } from "../../../InstructionHandler"; -import { getATA, getAmmAuthAddr, getAmmPositionAddr, getProposalVaultAddr } from '../../../utils'; +import { + getATA, + getAmmAuthAddr, + getAmmPositionAddr, + getProposalVaultAddr, +} from "../../../utils"; import BN from "bn.js"; export const addLiquidityCpiHandler = async ( - client: AutocratClient, - proposalAddr: PublicKey, - ammAddr: PublicKey, - maxBaseAmount: BN, - maxQuoteAmount: BN, - minBaseAmount: BN, - minQuoteAmount: BN, - ammProgram: PublicKey, + client: AutocratClient, + proposalAddr: PublicKey, + ammAddr: PublicKey, + maxBaseAmount: BN, + maxQuoteAmount: BN, + minBaseAmount: BN, + minQuoteAmount: BN, + ammProgram: PublicKey ): Promise> => { + const proposal = await client.program.account.proposal.fetch(proposalAddr); + let proposalVaultAddr = getProposalVaultAddr( + client.program.programId, + proposalAddr + )[0]; - const proposal = await client.program.account.proposal.fetch(proposalAddr); - let proposalVaultAddr = getProposalVaultAddr(client.program.programId, proposalAddr)[0] + if ( + proposal.passMarketAmm.toBase58() !== ammAddr.toBase58() && + proposal.failMarketAmm.toBase58() !== ammAddr.toBase58() + ) { + throw new Error( + "the amm address passed in addLiquidityCpiHandler does not correspond to either the pass or fail market" + ); + } - if (proposal.passMarketAmm.toBase58() !== ammAddr.toBase58() && proposal.failMarketAmm.toBase58() !== ammAddr.toBase58()) { - throw new Error("the amm address passed in addLiquidityCpiHandler does not correspond to either the pass or fail market"); - } + let conditionalMetaMint: PublicKey; + let conditionalUsdcMint: PublicKey; - let conditionalMetaMint: PublicKey; - let conditionalUsdcMint: PublicKey; + if (proposal.passMarketAmm.toBase58() === ammAddr.toBase58()) { + conditionalMetaMint = proposal.conditionalOnPassMetaMint; + conditionalUsdcMint = proposal.conditionalOnPassUsdcMint; + } else { + conditionalMetaMint = proposal.conditionalOnFailMetaMint; + conditionalUsdcMint = proposal.conditionalOnFailUsdcMint; + } - if (proposal.passMarketAmm.toBase58() === ammAddr.toBase58()) { - conditionalMetaMint = proposal.conditionalOnPassMetaMint - conditionalUsdcMint = proposal.conditionalOnPassUsdcMint - } else { - conditionalMetaMint = proposal.conditionalOnFailMetaMint - conditionalUsdcMint = proposal.conditionalOnFailUsdcMint - } + let ammPositionAddr = getAmmPositionAddr( + ammProgram, + ammAddr, + client.provider.publicKey + )[0]; + let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0]; - let ammPositionAddr = getAmmPositionAddr(ammProgram, ammAddr, client.provider.publicKey)[0] - let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0] + let ix = await client.program.methods + .addLiquidity(maxBaseAmount, maxQuoteAmount, minBaseAmount, minQuoteAmount) + .accounts({ + user: client.provider.publicKey, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + amm: ammAddr, + ammPosition: ammPositionAddr, + ammAuthPda: ammAuthAddr, + metaMint: proposal.metaMint, + usdcMint: proposal.usdcMint, + conditionalMetaMint, + conditionalUsdcMint, + conditionalMetaUserAta: getATA( + conditionalMetaMint, + client.provider.publicKey + )[0], + conditionalUsdcUserAta: getATA( + conditionalUsdcMint, + client.provider.publicKey + )[0], + conditionalMetaVaultAta: getATA(conditionalMetaMint, ammAddr)[0], + conditionalUsdcVaultAta: getATA(conditionalUsdcMint, ammAddr)[0], + ammProgram, + }) + .instruction(); - let ix = await client.program.methods - .addLiquidity( - maxBaseAmount, - maxQuoteAmount, - minBaseAmount, - minQuoteAmount - ) - .accounts({ - user: client.provider.publicKey, - proposal: proposalAddr, - proposalVault: proposalVaultAddr, - amm: ammAddr, - ammPosition: ammPositionAddr, - ammAuthPda: ammAuthAddr, - metaMint: proposal.metaMint, - usdcMint: proposal.usdcMint, - conditionalMetaMint, - conditionalUsdcMint, - conditionalMetaUserAta: getATA(conditionalMetaMint, client.provider.publicKey)[0], - conditionalUsdcUserAta: getATA(conditionalUsdcMint, client.provider.publicKey)[0], - conditionalMetaVaultAta: getATA(conditionalMetaMint, ammAddr)[0], - conditionalUsdcVaultAta: getATA(conditionalUsdcMint, ammAddr)[0], - ammProgram, - }) - .instruction() - - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/ammCpi/createAmmPositionCpi.ts b/app/src/instructions/autocrat/ammCpi/createAmmPositionCpi.ts index 5b76018..28da010 100644 --- a/app/src/instructions/autocrat/ammCpi/createAmmPositionCpi.ts +++ b/app/src/instructions/autocrat/ammCpi/createAmmPositionCpi.ts @@ -1,27 +1,31 @@ import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; import { AutocratClient } from "../../../AutocratClient"; import { InstructionHandler } from "../../../InstructionHandler"; -import { getAmmAuthAddr, getAmmPositionAddr } from '../../../utils'; +import { getAmmAuthAddr, getAmmPositionAddr } from "../../../utils"; export const createAmmPositionCpiHandler = async ( - client: AutocratClient, - proposalAddr: PublicKey, - amm: PublicKey, - ammProgram: PublicKey, + client: AutocratClient, + proposalAddr: PublicKey, + amm: PublicKey, + ammProgram: PublicKey ): Promise> => { - let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0] + let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0]; - let ix = await client.program.methods - .createPosition() - .accounts({ - user: client.provider.publicKey, - proposal: proposalAddr, - amm, - ammPosition: getAmmPositionAddr(ammProgram, amm, client.provider.publicKey)[0], - ammAuthPda: ammAuthAddr, - ammProgram, - }) - .instruction() + let ix = await client.program.methods + .createPosition() + .accounts({ + user: client.provider.publicKey, + proposal: proposalAddr, + amm, + ammPosition: getAmmPositionAddr( + ammProgram, + amm, + client.provider.publicKey + )[0], + ammAuthPda: ammAuthAddr, + ammProgram, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/ammCpi/removeLiquidityCpi.ts b/app/src/instructions/autocrat/ammCpi/removeLiquidityCpi.ts index 91e639b..34761b3 100644 --- a/app/src/instructions/autocrat/ammCpi/removeLiquidityCpi.ts +++ b/app/src/instructions/autocrat/ammCpi/removeLiquidityCpi.ts @@ -1,59 +1,80 @@ -import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; import { AutocratClient } from "../../../AutocratClient"; import { InstructionHandler } from "../../../InstructionHandler"; -import { getATA, getAmmAuthAddr, getAmmPositionAddr, getProposalVaultAddr } from '../../../utils'; +import { + getATA, + getAmmAuthAddr, + getAmmPositionAddr, + getProposalVaultAddr, +} from "../../../utils"; import BN from "bn.js"; export const removeLiquidityCpiHandler = async ( - client: AutocratClient, - proposalAddr: PublicKey, - ammAddr: PublicKey, - removeBps: BN, - ammProgram: PublicKey, + client: AutocratClient, + proposalAddr: PublicKey, + ammAddr: PublicKey, + removeBps: BN, + ammProgram: PublicKey ): Promise> => { - const proposal = await client.program.account.proposal.fetch(proposalAddr); - let proposalVaultAddr = getProposalVaultAddr(client.program.programId, proposalAddr)[0] + const proposal = await client.program.account.proposal.fetch(proposalAddr); + let proposalVaultAddr = getProposalVaultAddr( + client.program.programId, + proposalAddr + )[0]; - if (proposal.passMarketAmm.toBase58() !== ammAddr.toBase58() && proposal.failMarketAmm.toBase58() !== ammAddr.toBase58()) { - throw new Error("the amm address passed in removeLiquidityCpiHandler does not correspond to either the pass or fail market"); - } + if ( + proposal.passMarketAmm.toBase58() !== ammAddr.toBase58() && + proposal.failMarketAmm.toBase58() !== ammAddr.toBase58() + ) { + throw new Error( + "the amm address passed in removeLiquidityCpiHandler does not correspond to either the pass or fail market" + ); + } - let conditionalMetaMint: PublicKey; - let conditionalUsdcMint: PublicKey; + let conditionalMetaMint: PublicKey; + let conditionalUsdcMint: PublicKey; - if (proposal.passMarketAmm.toBase58() === ammAddr.toBase58()) { - conditionalMetaMint = proposal.conditionalOnPassMetaMint - conditionalUsdcMint = proposal.conditionalOnPassUsdcMint - } else { - conditionalMetaMint = proposal.conditionalOnFailMetaMint - conditionalUsdcMint = proposal.conditionalOnFailUsdcMint - } + if (proposal.passMarketAmm.toBase58() === ammAddr.toBase58()) { + conditionalMetaMint = proposal.conditionalOnPassMetaMint; + conditionalUsdcMint = proposal.conditionalOnPassUsdcMint; + } else { + conditionalMetaMint = proposal.conditionalOnFailMetaMint; + conditionalUsdcMint = proposal.conditionalOnFailUsdcMint; + } - let ammPositionAddr = getAmmPositionAddr(ammProgram, ammAddr, client.provider.publicKey)[0] - let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0] + let ammPositionAddr = getAmmPositionAddr( + ammProgram, + ammAddr, + client.provider.publicKey + )[0]; + let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0]; - let ix = await client.program.methods - .removeLiquidity( - removeBps, - ) - .accounts({ - user: client.provider.publicKey, - proposal: proposalAddr, - proposalVault: proposalVaultAddr, - amm: ammAddr, - ammPosition: ammPositionAddr, - ammAuthPda: ammAuthAddr, - metaMint: proposal.metaMint, - usdcMint: proposal.usdcMint, - conditionalMetaMint, - conditionalUsdcMint, - conditionalMetaUserAta: getATA(conditionalMetaMint, client.provider.publicKey)[0], - conditionalUsdcUserAta: getATA(conditionalUsdcMint, client.provider.publicKey)[0], - conditionalMetaVaultAta: getATA(conditionalMetaMint, ammAddr)[0], - conditionalUsdcVaultAta: getATA(conditionalUsdcMint, ammAddr)[0], - ammProgram, - }) - .instruction() + let ix = await client.program.methods + .removeLiquidity(removeBps) + .accounts({ + user: client.provider.publicKey, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + amm: ammAddr, + ammPosition: ammPositionAddr, + ammAuthPda: ammAuthAddr, + metaMint: proposal.metaMint, + usdcMint: proposal.usdcMint, + conditionalMetaMint, + conditionalUsdcMint, + conditionalMetaUserAta: getATA( + conditionalMetaMint, + client.provider.publicKey + )[0], + conditionalUsdcUserAta: getATA( + conditionalUsdcMint, + client.provider.publicKey + )[0], + conditionalMetaVaultAta: getATA(conditionalMetaMint, ammAddr)[0], + conditionalUsdcVaultAta: getATA(conditionalUsdcMint, ammAddr)[0], + ammProgram, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/ammCpi/swapCpi.ts b/app/src/instructions/autocrat/ammCpi/swapCpi.ts index 00ec704..5356db0 100644 --- a/app/src/instructions/autocrat/ammCpi/swapCpi.ts +++ b/app/src/instructions/autocrat/ammCpi/swapCpi.ts @@ -1,67 +1,75 @@ -import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; import { AutocratClient } from "../../../AutocratClient"; import { InstructionHandler } from "../../../InstructionHandler"; -import { getATA, getAmmAuthAddr, getProposalVaultAddr } from '../../../utils'; +import { + getATA, + getAmmAuthAddr, + getProposalVaultAddr, +} from "../../../utils"; import BN from "bn.js"; export const swapCpiHandler = async ( - client: AutocratClient, - proposalAddr: PublicKey, - ammAddr: PublicKey, - isQuoteToBase: boolean, - inputAmount: BN, - minOutputAmount: BN, - ammProgram: PublicKey, + client: AutocratClient, + proposalAddr: PublicKey, + ammAddr: PublicKey, + isQuoteToBase: boolean, + inputAmount: BN, + minOutputAmount: BN, + ammProgram: PublicKey ): Promise> => { - const proposal = await client.program.account.proposal.fetch(proposalAddr); - let proposalVaultAddr = getProposalVaultAddr(client.program.programId, proposalAddr)[0] + const proposal = await client.program.account.proposal.fetch(proposalAddr); + let proposalVaultAddr = getProposalVaultAddr( + client.program.programId, + proposalAddr + )[0]; - if (proposal.passMarketAmm.toBase58() !== ammAddr.toBase58() && proposal.failMarketAmm.toBase58() !== ammAddr.toBase58()) { - throw new Error("the amm address passed in swapCpiHandler does not correspond to either the pass or fail market"); - } + if ( + proposal.passMarketAmm.toBase58() !== ammAddr.toBase58() && + proposal.failMarketAmm.toBase58() !== ammAddr.toBase58() + ) { + throw new Error( + "the amm address passed in swapCpiHandler does not correspond to either the pass or fail market" + ); + } - let baseMint: PublicKey; - let quoteMint: PublicKey; + let baseMint: PublicKey; + let quoteMint: PublicKey; - if (proposal.passMarketAmm.toBase58() === ammAddr.toBase58()) { - baseMint = proposal.conditionalOnPassMetaMint - quoteMint = proposal.conditionalOnPassUsdcMint - } else { - baseMint = proposal.conditionalOnFailMetaMint - quoteMint = proposal.conditionalOnFailUsdcMint - } + if (proposal.passMarketAmm.toBase58() === ammAddr.toBase58()) { + baseMint = proposal.conditionalOnPassMetaMint; + quoteMint = proposal.conditionalOnPassUsdcMint; + } else { + baseMint = proposal.conditionalOnFailMetaMint; + quoteMint = proposal.conditionalOnFailUsdcMint; + } - let userAtaBase = getATA(baseMint, client.provider.publicKey)[0] - let userAtaQuote = getATA(quoteMint, client.provider.publicKey)[0] + let userAtaBase = getATA(baseMint, client.provider.publicKey)[0]; + let userAtaQuote = getATA(quoteMint, client.provider.publicKey)[0]; - let vaultAtaBase = getATA(baseMint, ammAddr)[0] - let vaultAtaQuote = getATA(quoteMint, ammAddr)[0] + let vaultAtaBase = getATA(baseMint, ammAddr)[0]; + let vaultAtaQuote = getATA(quoteMint, ammAddr)[0]; - let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0] + let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0]; - let ix = await client.program.methods - .swap( - isQuoteToBase, - inputAmount, - minOutputAmount, - ) - .accounts({ - user: client.provider.publicKey, - proposal: proposalAddr, - proposalVault: proposalVaultAddr, - amm: ammAddr, - ammAuthPda: ammAuthAddr, - metaMint: proposal.metaMint, - usdcMint: proposal.usdcMint, - conditionalMetaMint: baseMint, - conditionalUsdcMint: quoteMint, - conditionalMetaUserAta: userAtaBase, - conditionalUsdcUserAta: userAtaQuote, - conditionalMetaVaultAta: vaultAtaBase, - conditionalUsdcVaultAta: vaultAtaQuote, - ammProgram, - }) - .instruction() + let ix = await client.program.methods + .swap(isQuoteToBase, inputAmount, minOutputAmount) + .accounts({ + user: client.provider.publicKey, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + amm: ammAddr, + ammAuthPda: ammAuthAddr, + metaMint: proposal.metaMint, + usdcMint: proposal.usdcMint, + conditionalMetaMint: baseMint, + conditionalUsdcMint: quoteMint, + conditionalMetaUserAta: userAtaBase, + conditionalUsdcUserAta: userAtaQuote, + conditionalMetaVaultAta: vaultAtaBase, + conditionalUsdcVaultAta: vaultAtaQuote, + ammProgram, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/createProposal.ts b/app/src/instructions/autocrat/createProposal.ts index ef7452a..ca3f8d2 100644 --- a/app/src/instructions/autocrat/createProposal.ts +++ b/app/src/instructions/autocrat/createProposal.ts @@ -1,38 +1,55 @@ import { createAssociatedTokenAccountInstruction } from "@solana/spl-token"; import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA, getDaoAddr, getProposalAddr, getProposalVaultAddr } from '../../utils'; -import { Keypair, PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { + getATA, + getDaoAddr, + getProposalAddr, + getProposalVaultAddr, +} from "../../utils"; +import { + Keypair, + PublicKey, + SYSVAR_INSTRUCTIONS_PUBKEY, +} from "@solana/web3.js"; import BN from "bn.js"; export const createProposalHandler = async ( - client: AutocratClient, - proposalNumber: number, - descriptionUrl: string, - condMetaToMint: BN, - condUsdcToMint: BN, + client: AutocratClient, + daoId: PublicKey, + proposalNumber: number, + descriptionUrl: string, + condMetaToMint: BN, + condUsdcToMint: BN ): Promise> => { - let daoAddr = getDaoAddr(client.program.programId)[0] - let dao = await client.program.account.dao.fetch(daoAddr) + let daoAddr = getDaoAddr(client.program.programId, daoId)[0]; + let dao = await client.program.account.dao.fetch(daoAddr); - let proposalAddr = getProposalAddr(client.program.programId, proposalNumber)[0] - let proposalVaultAddr = getProposalVaultAddr(client.program.programId, proposalAddr)[0] + let proposalAddr = getProposalAddr( + client.program.programId, + daoAddr, + proposalNumber + )[0]; + let proposalVaultAddr = getProposalVaultAddr( + client.program.programId, + proposalAddr + )[0]; - let ix = await client.program.methods - .createProposal(descriptionUrl, condMetaToMint, condUsdcToMint) - .accounts({ - proposer: client.provider.publicKey, - dao: daoAddr, - proposal: proposalAddr, - proposalVault: proposalVaultAddr, - metaMint: dao.metaMint, - usdcMint: dao.usdcMint, - metaProposerAta: getATA(dao.metaMint, client.provider.publicKey)[0], - usdcProposerAta: getATA(dao.usdcMint, client.provider.publicKey)[0], - metaVaultAta: getATA(dao.metaMint, proposalVaultAddr)[0], - usdcVaultAta: getATA(dao.usdcMint, proposalVaultAddr)[0], - }) - .instruction() + let ix = await client.program.methods + .createProposal(descriptionUrl, condMetaToMint, condUsdcToMint) + .accounts({ + proposer: client.provider.publicKey, + dao: daoAddr, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + metaMint: dao.metaMint, + usdcMint: dao.usdcMint, + metaProposerAta: getATA(dao.metaMint, client.provider.publicKey)[0], + usdcProposerAta: getATA(dao.usdcMint, client.provider.publicKey)[0], + metaVaultAta: getATA(dao.metaMint, proposalVaultAddr)[0], + usdcVaultAta: getATA(dao.usdcMint, proposalVaultAddr)[0], + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/createProposalInstructions.ts b/app/src/instructions/autocrat/createProposalInstructions.ts index 296566d..10765bc 100644 --- a/app/src/instructions/autocrat/createProposalInstructions.ts +++ b/app/src/instructions/autocrat/createProposalInstructions.ts @@ -1,27 +1,40 @@ import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { ProposalInstruction } from '../../types'; -import { Keypair } from "@solana/web3.js"; -import { getProposalAddr, getProposalInstructionsAddr } from "../../utils"; +import { ProposalInstruction } from "../../types"; +import { PublicKey } from "@solana/web3.js"; +import { + getDaoAddr, + getProposalAddr, + getProposalInstructionsAddr, +} from "../../utils"; export const createProposalInstructionsHandler = async ( - client: AutocratClient, - proposalNumber: number, - instructions: ProposalInstruction[], + client: AutocratClient, + daoId: PublicKey, + proposalNumber: number, + instructions: ProposalInstruction[] ): Promise> => { + let daoAddr = getDaoAddr(client.program.programId, daoId)[0]; - let proposalAddr = getProposalAddr(client.program.programId, proposalNumber)[0] + let proposalAddr = getProposalAddr( + client.program.programId, + daoAddr, + proposalNumber + )[0]; - let proposalInstructionsAddr = getProposalInstructionsAddr(client.program.programId, proposalAddr)[0] + let proposalInstructionsAddr = getProposalInstructionsAddr( + client.program.programId, + proposalAddr + )[0]; - let ix = await client.program.methods - .createProposalInstructions(instructions) - .accounts({ - proposer: client.provider.publicKey, - proposal: proposalAddr, - proposalInstructions: proposalInstructionsAddr, - }) - .instruction() + let ix = await client.program.methods + .createProposalInstructions(instructions) + .accounts({ + proposer: client.provider.publicKey, + proposal: proposalAddr, + proposalInstructions: proposalInstructionsAddr, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/createProposalMarketSide.ts b/app/src/instructions/autocrat/createProposalMarketSide.ts index 8d557cc..53726fd 100644 --- a/app/src/instructions/autocrat/createProposalMarketSide.ts +++ b/app/src/instructions/autocrat/createProposalMarketSide.ts @@ -1,57 +1,126 @@ import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA, getAmmAddr, getAmmAuthAddr, getAmmPositionAddr, getDaoAddr, getProposalAddr, getProposalVaultAddr } from '../../utils'; +import { + getATA, + getAmmAddr, + getAmmAuthAddr, + getAmmPositionAddr, + getDaoAddr, + getProposalAddr, + getProposalVaultAddr, +} from "../../utils"; import BN from "bn.js"; -import { Keypair, PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { Keypair, PublicKey } from "@solana/web3.js"; export const createProposalMarketSideHandler = async ( - client: AutocratClient, - proposalNumber: number, - isPassMarket: boolean, - ammBaseAmountDeposit: BN, - ammQuoteAmountDeposit: BN, - ammProgram: PublicKey, + client: AutocratClient, + daoId: PublicKey, + proposalNumber: number, + isPassMarket: boolean, + ammBaseAmountDeposit: BN, + ammQuoteAmountDeposit: BN, + ammProgram: PublicKey ): Promise> => { - let daoAddr = getDaoAddr(client.program.programId)[0] - let dao = await client.program.account.dao.fetch(daoAddr) + let daoAddr = getDaoAddr(client.program.programId, daoId)[0]; + let dao = await client.program.account.dao.fetch(daoAddr); - let proposalAddr = getProposalAddr(client.program.programId, proposalNumber)[0] - let proposalVaultAddr = getProposalVaultAddr(client.program.programId, proposalAddr)[0] + let proposalAddr = getProposalAddr( + client.program.programId, + daoAddr, + proposalNumber + )[0]; + let proposalVaultAddr = getProposalVaultAddr( + client.program.programId, + proposalAddr + )[0]; - let conditionalMetaMintKeypair = Keypair.generate() - let conditionalMetaMintAddr = conditionalMetaMintKeypair.publicKey + let conditionalMetaMintKeypair = Keypair.generate(); + let conditionalMetaMintAddr = conditionalMetaMintKeypair.publicKey; - let conditionalUsdcMintKeypair = Keypair.generate() - let conditionalUsdcMintAddr = conditionalUsdcMintKeypair.publicKey + let conditionalUsdcMintKeypair = Keypair.generate(); + let conditionalUsdcMintAddr = conditionalUsdcMintKeypair.publicKey; - let ammAddr = getAmmAddr(ammProgram, conditionalMetaMintAddr, conditionalUsdcMintAddr, dao.ammSwapFeeBps.toNumber(), client.program.programId)[0] - let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0] + let ammAddr = getAmmAddr( + ammProgram, + conditionalMetaMintAddr, + conditionalUsdcMintAddr, + dao.ammSwapFeeBps.toNumber(), + client.program.programId + )[0]; + let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0]; - let ix = await client.program.methods - .createProposalMarketSide( - isPassMarket, - ammBaseAmountDeposit, - ammQuoteAmountDeposit, - ) - .accounts({ - proposer: client.provider.publicKey, - proposal: proposalAddr, - proposalVault: proposalVaultAddr, - dao: daoAddr, - amm: ammAddr, - ammPosition: getAmmPositionAddr(ammProgram, ammAddr, client.provider.publicKey)[0], - ammAuthPda: ammAuthAddr, - metaMint: dao.metaMint, - usdcMint: dao.usdcMint, - conditionalMetaMint: conditionalMetaMintAddr, - conditionalUsdcMint: conditionalUsdcMintAddr, - conditionalMetaProposerAta: getATA(conditionalMetaMintAddr, client.provider.publicKey)[0], - conditionalUsdcProposerAta: getATA(conditionalUsdcMintAddr, client.provider.publicKey)[0], - conditionalMetaAmmVaultAta: getATA(conditionalMetaMintAddr, ammAddr)[0], - conditionalUsdcAmmVaultAta: getATA(conditionalUsdcMintAddr, ammAddr)[0], - ammProgram, - }) - .instruction() + console.log( + { + proposer: client.provider.publicKey, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + dao: daoAddr, + amm: ammAddr, + ammPosition: getAmmPositionAddr( + ammProgram, + ammAddr, + client.provider.publicKey + )[0], + ammAuthPda: ammAuthAddr, + metaMint: dao.metaMint, + usdcMint: dao.usdcMint, + conditionalMetaMint: conditionalMetaMintAddr, + conditionalUsdcMint: conditionalUsdcMintAddr, + conditionalMetaProposerAta: getATA( + conditionalMetaMintAddr, + client.provider.publicKey + )[0], + conditionalUsdcProposerAta: getATA( + conditionalUsdcMintAddr, + client.provider.publicKey + )[0], + conditionalMetaAmmVaultAta: getATA(conditionalMetaMintAddr, ammAddr)[0], + conditionalUsdcAmmVaultAta: getATA(conditionalUsdcMintAddr, ammAddr)[0], + ammProgram, + }, + isPassMarket, + ammBaseAmountDeposit, + ammQuoteAmountDeposit + ); + let ix = await client.program.methods + .createProposalMarketSide( + isPassMarket, + ammBaseAmountDeposit, + ammQuoteAmountDeposit + ) + .accounts({ + proposer: client.provider.publicKey, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + dao: daoAddr, + amm: ammAddr, + ammPosition: getAmmPositionAddr( + ammProgram, + ammAddr, + client.provider.publicKey + )[0], + ammAuthPda: ammAuthAddr, + metaMint: dao.metaMint, + usdcMint: dao.usdcMint, + conditionalMetaMint: conditionalMetaMintAddr, + conditionalUsdcMint: conditionalUsdcMintAddr, + conditionalMetaProposerAta: getATA( + conditionalMetaMintAddr, + client.provider.publicKey + )[0], + conditionalUsdcProposerAta: getATA( + conditionalUsdcMintAddr, + client.provider.publicKey + )[0], + conditionalMetaAmmVaultAta: getATA(conditionalMetaMintAddr, ammAddr)[0], + conditionalUsdcAmmVaultAta: getATA(conditionalUsdcMintAddr, ammAddr)[0], + ammProgram, + }) + .instruction(); - return new InstructionHandler([ix], [conditionalMetaMintKeypair, conditionalUsdcMintKeypair], client) + return new InstructionHandler( + [ix], + [conditionalMetaMintKeypair, conditionalUsdcMintKeypair], + client + ); }; diff --git a/app/src/instructions/autocrat/finalizeProposal.ts b/app/src/instructions/autocrat/finalizeProposal.ts index 7d5da27..278e008 100644 --- a/app/src/instructions/autocrat/finalizeProposal.ts +++ b/app/src/instructions/autocrat/finalizeProposal.ts @@ -1,29 +1,38 @@ -import { AccountMeta, PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { + AccountMeta, + PublicKey, + SYSVAR_INSTRUCTIONS_PUBKEY, +} from "@solana/web3.js"; import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getDaoAddr, getDaoTreasuryAddr, getProposalAddr } from '../../utils'; +import { getDaoAddr, getDaoTreasuryAddr, getProposalAddr } from "../../utils"; export const finalizeProposalHandler = async ( - client: AutocratClient, - proposalNumber: number, - accounts: AccountMeta[], + client: AutocratClient, + daoId: PublicKey, + proposalNumber: number, + accounts: AccountMeta[] ): Promise> => { + let daoAddr = getDaoAddr(client.program.programId, daoId)[0]; + let proposalAddr = getProposalAddr( + client.program.programId, + daoAddr, + proposalNumber + )[0]; + const proposalAcc = await client.program.account.proposal.fetch(proposalAddr); - let proposalAddr = getProposalAddr(client.program.programId, proposalNumber)[0] - const proposalAcc = await client.program.account.proposal.fetch(proposalAddr); + let ix = await client.program.methods + .finalizeProposal() + .accounts({ + proposal: proposalAddr, + proposalInstructions: proposalAcc.instructions, + dao: daoAddr, + daoTreasury: getDaoTreasuryAddr(client.program.programId, daoId)[0], + passMarketAmm: proposalAcc.passMarketAmm, + failMarketAmm: proposalAcc.failMarketAmm, + }) + .remainingAccounts(accounts) + .instruction(); - let ix = await client.program.methods - .finalizeProposal() - .accounts({ - proposal: proposalAddr, - proposalInstructions: proposalAcc.instructions, - dao: getDaoAddr(client.program.programId)[0], - daoTreasury: getDaoTreasuryAddr(client.program.programId)[0], - passMarketAmm: proposalAcc.passMarketAmm, - failMarketAmm: proposalAcc.failMarketAmm, - }) - .remainingAccounts(accounts) - .instruction() - - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/index.ts b/app/src/instructions/autocrat/index.ts index 7db5c9f..673e72e 100644 --- a/app/src/instructions/autocrat/index.ts +++ b/app/src/instructions/autocrat/index.ts @@ -1,16 +1,15 @@ -export * from './ammCpi/addLiquidityCpi' -export * from './addProposalInstructions' -export * from './ammCpi/createAmmPositionCpi' -export * from './createProposal' -export * from './createProposalMarketSide' -export * from './submitProposal' -export * from './createProposalInstructions' -export * from './initializeDao' -export * from './finalizeProposal' -export * from './mergeConditionalTokens' -export * from './mintConditionalTokens' -export * from './redeemConditionalTokens' -export * from './ammCpi/removeLiquidityCpi' -export * from './ammCpi/swapCpi' -export * from './updateDao' - +export * from "./ammCpi/addLiquidityCpi"; +export * from "./addProposalInstructions"; +export * from "./ammCpi/createAmmPositionCpi"; +export * from "./createProposal"; +export * from "./createProposalMarketSide"; +export * from "./submitProposal"; +export * from "./createProposalInstructions"; +export * from "./initializeDao"; +export * from "./finalizeProposal"; +export * from "./mergeConditionalTokens"; +export * from "./mintConditionalTokens"; +export * from "./redeemConditionalTokens"; +export * from "./ammCpi/removeLiquidityCpi"; +export * from "./ammCpi/swapCpi"; +export * from "./updateDao"; diff --git a/app/src/instructions/autocrat/initializeDao.ts b/app/src/instructions/autocrat/initializeDao.ts index 33978c3..a63019d 100644 --- a/app/src/instructions/autocrat/initializeDao.ts +++ b/app/src/instructions/autocrat/initializeDao.ts @@ -1,26 +1,26 @@ -import { PublicKey } from '@solana/web3.js'; +import { PublicKey } from "@solana/web3.js"; import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA, getDaoAddr, getDaoTreasuryAddr } from '../../utils'; -import { META_MINT, USDC_MINT } from '../../constants'; +import { getDaoAddr, getDaoTreasuryAddr } from "../../utils"; export const initializeDaoHandler = async ( - client: AutocratClient, - metaMint: PublicKey, - usdcMint: PublicKey + client: AutocratClient, + daoId: PublicKey, + metaMint: PublicKey, + usdcMint: PublicKey ): Promise> => { - let daoTreasuryAddr = getDaoTreasuryAddr(client.program.programId)[0] + let daoTreasuryAddr = getDaoTreasuryAddr(client.program.programId, daoId)[0]; - let ix = await client.program.methods - .initializeDao() - .accounts({ - payer: client.provider.wallet.publicKey, - dao: getDaoAddr(client.program.programId)[0], - daoTreasury: daoTreasuryAddr, - metaMint: metaMint, - usdcMint: usdcMint, - }) - .instruction() + let ix = await client.program.methods + .initializeDao(daoId) + .accounts({ + payer: client.provider.wallet.publicKey, + dao: getDaoAddr(client.program.programId, daoId)[0], + daoTreasury: daoTreasuryAddr, + metaMint: metaMint, + usdcMint: usdcMint, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/mergeConditionalTokens.ts b/app/src/instructions/autocrat/mergeConditionalTokens.ts index 558b576..aeab972 100644 --- a/app/src/instructions/autocrat/mergeConditionalTokens.ts +++ b/app/src/instructions/autocrat/mergeConditionalTokens.ts @@ -1,41 +1,56 @@ -import { PublicKey } from '@solana/web3.js'; +import { PublicKey } from "@solana/web3.js"; import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA, getProposalVaultAddr } from '../../utils'; -import BN from 'bn.js'; +import { getATA, getProposalVaultAddr } from "../../utils"; +import BN from "bn.js"; export const mergeConditionalTokensHandler = async ( - client: AutocratClient, - proposalAddr: PublicKey, - metaAmount: BN, - usdcAmount: BN, + client: AutocratClient, + proposalAddr: PublicKey, + metaAmount: BN, + usdcAmount: BN ): Promise> => { - const proposal = await client.program.account.proposal.fetch(proposalAddr); + const proposal = await client.program.account.proposal.fetch(proposalAddr); - let proposalVaultAddr = getProposalVaultAddr(client.program.programId, proposalAddr)[0] + let proposalVaultAddr = getProposalVaultAddr( + client.program.programId, + proposalAddr + )[0]; - let ix = await client.program.methods - .mergeConditionalTokens(metaAmount, usdcAmount) - .accounts({ - user: client.provider.publicKey, - proposal: proposalAddr, - proposalVault: proposalVaultAddr, - metaMint: proposal.metaMint, - usdcMint: proposal.usdcMint, - conditionalOnPassMetaMint: proposal.conditionalOnPassMetaMint, - conditionalOnPassUsdcMint: proposal.conditionalOnPassUsdcMint, - conditionalOnFailMetaMint: proposal.conditionalOnFailMetaMint, - conditionalOnFailUsdcMint: proposal.conditionalOnFailUsdcMint, - metaUserAta: getATA(proposal.metaMint, client.provider.publicKey)[0], - usdcUserAta: getATA(proposal.usdcMint, client.provider.publicKey)[0], - conditionalOnPassMetaUserAta: getATA(proposal.conditionalOnPassMetaMint, client.provider.publicKey)[0], - conditionalOnPassUsdcUserAta: getATA(proposal.conditionalOnPassUsdcMint, client.provider.publicKey)[0], - conditionalOnFailMetaUserAta: getATA(proposal.conditionalOnFailMetaMint, client.provider.publicKey)[0], - conditionalOnFailUsdcUserAta: getATA(proposal.conditionalOnFailUsdcMint, client.provider.publicKey)[0], - metaVaultAta: getATA(proposal.metaMint, proposalVaultAddr)[0], - usdcVaultAta: getATA(proposal.usdcMint, proposalVaultAddr)[0], - }) - .instruction() + let ix = await client.program.methods + .mergeConditionalTokens(metaAmount, usdcAmount) + .accounts({ + user: client.provider.publicKey, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + metaMint: proposal.metaMint, + usdcMint: proposal.usdcMint, + conditionalOnPassMetaMint: proposal.conditionalOnPassMetaMint, + conditionalOnPassUsdcMint: proposal.conditionalOnPassUsdcMint, + conditionalOnFailMetaMint: proposal.conditionalOnFailMetaMint, + conditionalOnFailUsdcMint: proposal.conditionalOnFailUsdcMint, + metaUserAta: getATA(proposal.metaMint, client.provider.publicKey)[0], + usdcUserAta: getATA(proposal.usdcMint, client.provider.publicKey)[0], + conditionalOnPassMetaUserAta: getATA( + proposal.conditionalOnPassMetaMint, + client.provider.publicKey + )[0], + conditionalOnPassUsdcUserAta: getATA( + proposal.conditionalOnPassUsdcMint, + client.provider.publicKey + )[0], + conditionalOnFailMetaUserAta: getATA( + proposal.conditionalOnFailMetaMint, + client.provider.publicKey + )[0], + conditionalOnFailUsdcUserAta: getATA( + proposal.conditionalOnFailUsdcMint, + client.provider.publicKey + )[0], + metaVaultAta: getATA(proposal.metaMint, proposalVaultAddr)[0], + usdcVaultAta: getATA(proposal.usdcMint, proposalVaultAddr)[0], + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/mintConditionalTokens.ts b/app/src/instructions/autocrat/mintConditionalTokens.ts index ea48394..c801ee6 100644 --- a/app/src/instructions/autocrat/mintConditionalTokens.ts +++ b/app/src/instructions/autocrat/mintConditionalTokens.ts @@ -1,41 +1,56 @@ -import { PublicKey } from '@solana/web3.js'; +import { PublicKey } from "@solana/web3.js"; import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA, getDaoAddr, getProposalVaultAddr } from '../../utils'; -import BN from 'bn.js'; +import { getATA, getProposalVaultAddr } from "../../utils"; +import BN from "bn.js"; export const mintConditionalTokensHandler = async ( - client: AutocratClient, - proposalAddr: PublicKey, - metaAmount: BN, - usdcAmount: BN, + client: AutocratClient, + proposalAddr: PublicKey, + metaAmount: BN, + usdcAmount: BN ): Promise> => { - const proposal = await client.program.account.proposal.fetch(proposalAddr); + const proposal = await client.program.account.proposal.fetch(proposalAddr); - let proposalVaultAddr = getProposalVaultAddr(client.program.programId, proposalAddr)[0] + let proposalVaultAddr = getProposalVaultAddr( + client.program.programId, + proposalAddr + )[0]; - let ix = await client.program.methods - .mintConditionalTokens(metaAmount, usdcAmount) - .accounts({ - user: client.provider.publicKey, - proposal: proposalAddr, - proposalVault: proposalVaultAddr, - metaMint: proposal.metaMint, - usdcMint: proposal.usdcMint, - conditionalOnPassMetaMint: proposal.conditionalOnPassMetaMint, - conditionalOnPassUsdcMint: proposal.conditionalOnPassUsdcMint, - conditionalOnFailMetaMint: proposal.conditionalOnFailMetaMint, - conditionalOnFailUsdcMint: proposal.conditionalOnFailUsdcMint, - metaUserAta: getATA(proposal.metaMint, client.provider.publicKey)[0], - usdcUserAta: getATA(proposal.usdcMint, client.provider.publicKey)[0], - conditionalOnPassMetaUserAta: getATA(proposal.conditionalOnPassMetaMint, client.provider.publicKey)[0], - conditionalOnPassUsdcUserAta: getATA(proposal.conditionalOnPassUsdcMint, client.provider.publicKey)[0], - conditionalOnFailMetaUserAta: getATA(proposal.conditionalOnFailMetaMint, client.provider.publicKey)[0], - conditionalOnFailUsdcUserAta: getATA(proposal.conditionalOnFailUsdcMint, client.provider.publicKey)[0], - metaVaultAta: getATA(proposal.metaMint, proposalVaultAddr)[0], - usdcVaultAta: getATA(proposal.usdcMint, proposalVaultAddr)[0], - }) - .instruction() + let ix = await client.program.methods + .mintConditionalTokens(metaAmount, usdcAmount) + .accounts({ + user: client.provider.publicKey, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + metaMint: proposal.metaMint, + usdcMint: proposal.usdcMint, + conditionalOnPassMetaMint: proposal.conditionalOnPassMetaMint, + conditionalOnPassUsdcMint: proposal.conditionalOnPassUsdcMint, + conditionalOnFailMetaMint: proposal.conditionalOnFailMetaMint, + conditionalOnFailUsdcMint: proposal.conditionalOnFailUsdcMint, + metaUserAta: getATA(proposal.metaMint, client.provider.publicKey)[0], + usdcUserAta: getATA(proposal.usdcMint, client.provider.publicKey)[0], + conditionalOnPassMetaUserAta: getATA( + proposal.conditionalOnPassMetaMint, + client.provider.publicKey + )[0], + conditionalOnPassUsdcUserAta: getATA( + proposal.conditionalOnPassUsdcMint, + client.provider.publicKey + )[0], + conditionalOnFailMetaUserAta: getATA( + proposal.conditionalOnFailMetaMint, + client.provider.publicKey + )[0], + conditionalOnFailUsdcUserAta: getATA( + proposal.conditionalOnFailUsdcMint, + client.provider.publicKey + )[0], + metaVaultAta: getATA(proposal.metaMint, proposalVaultAddr)[0], + usdcVaultAta: getATA(proposal.usdcMint, proposalVaultAddr)[0], + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/redeemConditionalTokens.ts b/app/src/instructions/autocrat/redeemConditionalTokens.ts index 4c7f207..0796fc2 100644 --- a/app/src/instructions/autocrat/redeemConditionalTokens.ts +++ b/app/src/instructions/autocrat/redeemConditionalTokens.ts @@ -1,38 +1,53 @@ -import { PublicKey } from '@solana/web3.js'; +import { PublicKey } from "@solana/web3.js"; import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA, getProposalVaultAddr } from '../../utils'; +import { getATA, getProposalVaultAddr } from "../../utils"; export const redeemConditionalTokensHandler = async ( - client: AutocratClient, - proposalAddr: PublicKey, + client: AutocratClient, + proposalAddr: PublicKey ): Promise> => { - const proposal = await client.program.account.proposal.fetch(proposalAddr); + const proposal = await client.program.account.proposal.fetch(proposalAddr); - let proposalVaultAddr = getProposalVaultAddr(client.program.programId, proposalAddr)[0] + let proposalVaultAddr = getProposalVaultAddr( + client.program.programId, + proposalAddr + )[0]; - let ix = await client.program.methods - .redeemConditionalTokens() - .accounts({ - user: client.provider.publicKey, - proposal: proposalAddr, - proposalVault: proposalVaultAddr, - metaMint: proposal.metaMint, - usdcMint: proposal.usdcMint, - conditionalOnPassMetaMint: proposal.conditionalOnPassMetaMint, - conditionalOnPassUsdcMint: proposal.conditionalOnPassUsdcMint, - conditionalOnFailMetaMint: proposal.conditionalOnFailMetaMint, - conditionalOnFailUsdcMint: proposal.conditionalOnFailUsdcMint, - metaUserAta: getATA(proposal.metaMint, client.provider.publicKey)[0], - usdcUserAta: getATA(proposal.usdcMint, client.provider.publicKey)[0], - conditionalOnPassMetaUserAta: getATA(proposal.conditionalOnPassMetaMint, client.provider.publicKey)[0], - conditionalOnPassUsdcUserAta: getATA(proposal.conditionalOnPassUsdcMint, client.provider.publicKey)[0], - conditionalOnFailMetaUserAta: getATA(proposal.conditionalOnFailMetaMint, client.provider.publicKey)[0], - conditionalOnFailUsdcUserAta: getATA(proposal.conditionalOnFailUsdcMint, client.provider.publicKey)[0], - metaVaultAta: getATA(proposal.metaMint, proposalVaultAddr)[0], - usdcVaultAta: getATA(proposal.usdcMint, proposalVaultAddr)[0], - }) - .instruction() + let ix = await client.program.methods + .redeemConditionalTokens() + .accounts({ + user: client.provider.publicKey, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + metaMint: proposal.metaMint, + usdcMint: proposal.usdcMint, + conditionalOnPassMetaMint: proposal.conditionalOnPassMetaMint, + conditionalOnPassUsdcMint: proposal.conditionalOnPassUsdcMint, + conditionalOnFailMetaMint: proposal.conditionalOnFailMetaMint, + conditionalOnFailUsdcMint: proposal.conditionalOnFailUsdcMint, + metaUserAta: getATA(proposal.metaMint, client.provider.publicKey)[0], + usdcUserAta: getATA(proposal.usdcMint, client.provider.publicKey)[0], + conditionalOnPassMetaUserAta: getATA( + proposal.conditionalOnPassMetaMint, + client.provider.publicKey + )[0], + conditionalOnPassUsdcUserAta: getATA( + proposal.conditionalOnPassUsdcMint, + client.provider.publicKey + )[0], + conditionalOnFailMetaUserAta: getATA( + proposal.conditionalOnFailMetaMint, + client.provider.publicKey + )[0], + conditionalOnFailUsdcUserAta: getATA( + proposal.conditionalOnFailUsdcMint, + client.provider.publicKey + )[0], + metaVaultAta: getATA(proposal.metaMint, proposalVaultAddr)[0], + usdcVaultAta: getATA(proposal.usdcMint, proposalVaultAddr)[0], + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/submitProposal.ts b/app/src/instructions/autocrat/submitProposal.ts index 88eabb4..1b4da97 100644 --- a/app/src/instructions/autocrat/submitProposal.ts +++ b/app/src/instructions/autocrat/submitProposal.ts @@ -1,44 +1,62 @@ -import { createAssociatedTokenAccountInstruction } from "@solana/spl-token"; import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getATA, getAmmAuthAddr, getDaoAddr, getDaoTreasuryAddr, getProposalAddr, getProposalInstructionsAddr, getProposalVaultAddr } from '../../utils'; -import { Keypair, PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { + getATA, + getAmmAuthAddr, + getDaoAddr, + getDaoTreasuryAddr, + getProposalAddr, + getProposalInstructionsAddr, + getProposalVaultAddr, +} from "../../utils"; +import { PublicKey } from "@solana/web3.js"; export const submitProposalHandler = async ( - client: AutocratClient, - proposalNumber: number, - ammProgram: PublicKey, + client: AutocratClient, + daoId: PublicKey, + proposalNumber: number, + ammProgram: PublicKey ): Promise> => { - let daoAddr = getDaoAddr(client.program.programId)[0] - let daoTreasuryAddr = getDaoTreasuryAddr(client.program.programId)[0] + let daoAddr = getDaoAddr(client.program.programId, daoId)[0]; + let daoTreasuryAddr = getDaoTreasuryAddr(client.program.programId, daoId)[0]; - let proposalAddr = getProposalAddr(client.program.programId, proposalNumber)[0] - let proposal = await client.program.account.proposal.fetch(proposalAddr) + let proposalAddr = getProposalAddr( + client.program.programId, + daoAddr, + proposalNumber + )[0]; + let proposal = await client.program.account.proposal.fetch(proposalAddr); - let proposalInstructionsAddr = getProposalInstructionsAddr(client.program.programId, proposalAddr)[0] + let proposalInstructionsAddr = getProposalInstructionsAddr( + client.program.programId, + proposalAddr + )[0]; - let proposalVaultAddr = getProposalVaultAddr(client.program.programId, proposalAddr)[0] + let proposalVaultAddr = getProposalVaultAddr( + client.program.programId, + proposalAddr + )[0]; - let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0] + let ammAuthAddr = getAmmAuthAddr(client.program.programId)[0]; - let ix = await client.program.methods - .submitProposal() - .accounts({ - proposer: client.provider.publicKey, - dao: daoAddr, - daoTreasury: daoTreasuryAddr, - proposal: proposalAddr, - proposalVault: proposalVaultAddr, - proposalInstructions: proposalInstructionsAddr, - usdcMint: proposal.usdcMint, - usdcProposerAta: getATA(proposal.usdcMint, client.provider.publicKey)[0], - usdcTreasuryVaultAta: getATA(proposal.usdcMint, daoTreasuryAddr)[0], - passMarketAmm: proposal.passMarketAmm, - failMarketAmm: proposal.failMarketAmm, - ammAuthPda: ammAuthAddr, - ammProgram, - }) - .instruction() + let ix = await client.program.methods + .submitProposal() + .accounts({ + proposer: client.provider.publicKey, + dao: daoAddr, + daoTreasury: daoTreasuryAddr, + proposal: proposalAddr, + proposalVault: proposalVaultAddr, + proposalInstructions: proposalInstructionsAddr, + usdcMint: proposal.usdcMint, + usdcProposerAta: getATA(proposal.usdcMint, client.provider.publicKey)[0], + usdcTreasuryVaultAta: getATA(proposal.usdcMint, daoTreasuryAddr)[0], + passMarketAmm: proposal.passMarketAmm, + failMarketAmm: proposal.failMarketAmm, + ammAuthPda: ammAuthAddr, + ammProgram, + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/autocrat/updateDao.ts b/app/src/instructions/autocrat/updateDao.ts index 8af7079..bd40554 100644 --- a/app/src/instructions/autocrat/updateDao.ts +++ b/app/src/instructions/autocrat/updateDao.ts @@ -1,19 +1,21 @@ +import { PublicKey } from "@solana/web3.js"; import { AutocratClient } from "../../AutocratClient"; import { InstructionHandler } from "../../InstructionHandler"; -import { getDaoAddr, getDaoTreasuryAddr } from '../../utils'; -import { UpdateDaoParams } from '../../types'; +import { getDaoAddr, getDaoTreasuryAddr } from "../../utils"; +import { UpdateDaoParams } from "../../types"; export const updateDaoHandler = async ( - client: AutocratClient, - updateDaoParams: UpdateDaoParams + client: AutocratClient, + daoId: PublicKey, + updateDaoParams: UpdateDaoParams ): Promise> => { - let ix = await client.program.methods - .updateDao(updateDaoParams) - .accounts({ - dao: getDaoAddr(client.program.programId)[0], - daoTreasury: getDaoTreasuryAddr(client.program.programId)[0], - }) - .instruction() + let ix = await client.program.methods + .updateDao(updateDaoParams) + .accounts({ + dao: getDaoAddr(client.program.programId, daoId)[0], + daoTreasury: getDaoTreasuryAddr(client.program.programId, daoId)[0], + }) + .instruction(); - return new InstructionHandler([ix], [], client) + return new InstructionHandler([ix], [], client); }; diff --git a/app/src/instructions/index.ts b/app/src/instructions/index.ts index e10275a..95cc0b9 100644 --- a/app/src/instructions/index.ts +++ b/app/src/instructions/index.ts @@ -1,2 +1,2 @@ -export * from './amm' -export * from './autocrat' +export * from "./amm"; +export * from "./autocrat"; diff --git a/app/src/types/autocrat.ts b/app/src/types/autocrat.ts index 5f4f35b..6b3f6fe 100644 --- a/app/src/types/autocrat.ts +++ b/app/src/types/autocrat.ts @@ -46,7 +46,12 @@ export type Autocrat = { "isSigner": false } ], - "args": [] + "args": [ + { + "name": "id", + "type": "publicKey" + } + ] }, { "name": "updateDao", @@ -255,7 +260,7 @@ export type Autocrat = { }, { "name": "dao", - "isMut": true, + "isMut": false, "isSigner": false }, { @@ -1226,6 +1231,10 @@ export type Autocrat = { "type": { "kind": "struct", "fields": [ + { + "name": "id", + "type": "publicKey" + }, { "name": "treasuryPdaBump", "type": "u8" @@ -1310,6 +1319,10 @@ export type Autocrat = { "type": { "kind": "struct", "fields": [ + { + "name": "dao", + "type": "publicKey" + }, { "name": "number", "type": "u64" @@ -1702,7 +1715,12 @@ export const IDL: Autocrat = { "isSigner": false } ], - "args": [] + "args": [ + { + "name": "id", + "type": "publicKey" + } + ] }, { "name": "updateDao", @@ -1911,7 +1929,7 @@ export const IDL: Autocrat = { }, { "name": "dao", - "isMut": true, + "isMut": false, "isSigner": false }, { @@ -2882,6 +2900,10 @@ export const IDL: Autocrat = { "type": { "kind": "struct", "fields": [ + { + "name": "id", + "type": "publicKey" + }, { "name": "treasuryPdaBump", "type": "u8" @@ -2966,6 +2988,10 @@ export const IDL: Autocrat = { "type": { "kind": "struct", "fields": [ + { + "name": "dao", + "type": "publicKey" + }, { "name": "number", "type": "u64" diff --git a/app/src/types/index.ts b/app/src/types/index.ts index 7dd8ca6..7777c0e 100644 --- a/app/src/types/index.ts +++ b/app/src/types/index.ts @@ -1,32 +1,34 @@ -import { Autocrat as AutocratIDLType } from './autocrat'; -import { Amm as AmmIDLType } from './amm'; +import { Autocrat as AutocratIDLType } from "./autocrat"; +import { Amm as AmmIDLType } from "./amm"; import type { IdlAccounts, IdlTypes } from "@coral-xyz/anchor"; -import { PublicKey } from '@solana/web3.js'; +import { PublicKey } from "@solana/web3.js"; -export type UpdateDaoParams = IdlTypes['UpdateDaoParams']; -export type ProposalInstruction = IdlTypes['ProposalInstruction']; +export type UpdateDaoParams = IdlTypes["UpdateDaoParams"]; +export type ProposalInstruction = + IdlTypes["ProposalInstruction"]; -export type Proposal = IdlAccounts['proposal']; +export type Proposal = IdlAccounts["proposal"]; export type ProposalWrapper = { - account: Proposal, - publicKey: PublicKey -} + account: Proposal; + publicKey: PublicKey; +}; -export type ProposalVault = IdlAccounts['proposalVault']; -export type ProposalInstructions = IdlAccounts['proposalInstructions']; +export type ProposalVault = IdlAccounts["proposalVault"]; +export type ProposalInstructions = + IdlAccounts["proposalInstructions"]; -export type Dao = IdlAccounts['dao']; -export type DaoTreasury = IdlAccounts['daoTreasury']; +export type Dao = IdlAccounts["dao"]; +export type DaoTreasury = IdlAccounts["daoTreasury"]; -export type Amm = IdlAccounts['amm']; +export type Amm = IdlAccounts["amm"]; export type AmmWrapper = { - account: Amm, - publicKey: PublicKey -} + account: Amm; + publicKey: PublicKey; +}; -export type AmmPosition = IdlAccounts['ammPosition']; +export type AmmPosition = IdlAccounts["ammPosition"]; export type AmmPositionWrapper = { - account: AmmPosition, - publicKey: PublicKey -} \ No newline at end of file + account: AmmPosition; + publicKey: PublicKey; +}; diff --git a/app/src/utils/filters.ts b/app/src/utils/filters.ts index 846b1ba..945f773 100644 --- a/app/src/utils/filters.ts +++ b/app/src/utils/filters.ts @@ -1,22 +1,21 @@ import { GetProgramAccountsFilter, PublicKey } from "@solana/web3.js"; export const filterPositionsByUser = ( - userAddr: PublicKey + userAddr: PublicKey ): GetProgramAccountsFilter => ({ - memcmp: { - offset: - 8, // discriminator - bytes: userAddr.toBase58() - } -}) + memcmp: { + offset: 8, // discriminator + bytes: userAddr.toBase58(), + }, +}); export const filterPositionsByAmm = ( - ammAddr: PublicKey + ammAddr: PublicKey ): GetProgramAccountsFilter => ({ - memcmp: { - offset: - 8 // discriminator - + 32, // user address - bytes: ammAddr.toBase58() - } -}) \ No newline at end of file + memcmp: { + offset: + 8 + // discriminator + 32, // user address + bytes: ammAddr.toBase58(), + }, +}); diff --git a/app/src/utils/index.ts b/app/src/utils/index.ts index 77ec3fd..8059d77 100644 --- a/app/src/utils/index.ts +++ b/app/src/utils/index.ts @@ -1,32 +1,38 @@ -export * from './filters' -export * from './pda' -export * from './numbers' -export * from './lookupTable' +export * from "./filters"; +export * from "./pda"; +export * from "./numbers"; +export * from "./lookupTable"; import { AccountMeta, ComputeBudgetProgram, PublicKey } from "@solana/web3.js"; export enum PriorityFeeTier { - NORMAL = 35, - HIGH = 3571, - TURBO = 357142, + NORMAL = 35, + HIGH = 3571, + TURBO = 357142, } -export const addComputeUnits = (num_units: number = 1_400_000) => ComputeBudgetProgram.setComputeUnitLimit({ - units: num_units -}); +export const addComputeUnits = (num_units: number = 1_400_000) => + ComputeBudgetProgram.setComputeUnitLimit({ + units: num_units, + }); -export const addPriorityFee = (pf: number) => ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: pf -}); +export const addPriorityFee = (pf: number) => + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: pf, + }); -export const pubkeyToAccountInfo = (pubkey: PublicKey, isWritable: boolean, isSigner = false): AccountMeta => { - return { - pubkey: pubkey, - isSigner: isSigner, - isWritable: isWritable, - } -} +export const pubkeyToAccountInfo = ( + pubkey: PublicKey, + isWritable: boolean, + isSigner = false +): AccountMeta => { + return { + pubkey: pubkey, + isSigner: isSigner, + isWritable: isWritable, + }; +}; export async function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/app/src/utils/lookupTable.ts b/app/src/utils/lookupTable.ts index 9769746..7b7b5b7 100644 --- a/app/src/utils/lookupTable.ts +++ b/app/src/utils/lookupTable.ts @@ -1,129 +1,146 @@ import { - AddressLookupTableProgram, - ComputeBudgetProgram, - PublicKey, - SystemProgram, - SYSVAR_RENT_PUBKEY, - TransactionMessage, - VersionedTransaction + AddressLookupTableProgram, + ComputeBudgetProgram, + PublicKey, + SystemProgram, + SYSVAR_RENT_PUBKEY, + TransactionMessage, + VersionedTransaction, } from "@solana/web3.js"; import { - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, } from "@solana/spl-token"; -import {AutocratClient} from "../AutocratClient"; -import { AUTOCRAT_LUTS, AUTOCRAT_PROGRAM_ID, META_MINT, USDC_MINT } from "../constants"; +import { AutocratClient } from "../AutocratClient"; +import { + AUTOCRAT_LUTS, + AUTOCRAT_PROGRAM_ID, + META_MINT, + USDC_MINT, +} from "../constants"; import { getDaoAddr, getDaoTreasuryAddr } from "./pda"; export async function createLookupTable( - client: AutocratClient, - print: boolean = false + client: AutocratClient, + daoId: PublicKey, + print: boolean = false ): Promise { - const slot = await client.provider.connection.getSlot(); - - let isNewLUT = false - let lookupTableAddress = AUTOCRAT_LUTS[0] - - if (isNewLUT) { - const lookupTableObj = - AddressLookupTableProgram.createLookupTable({ - authority: client.provider.wallet.publicKey, - payer: client.provider.wallet.publicKey, - recentSlot: slot, - }); - - const lookupTableInst = lookupTableObj[0] - lookupTableAddress = lookupTableObj[1] - - let latestBlockhash = await client.provider.connection.getLatestBlockhash() - - const messageLegacy = new TransactionMessage({ - payerKey: client.provider.wallet.publicKey, - recentBlockhash: latestBlockhash.blockhash, - instructions: [lookupTableInst], - }).compileToLegacyMessage(); + const slot = await client.provider.connection.getSlot(); + + let isNewLUT = false; + let lookupTableAddress = AUTOCRAT_LUTS[0]; + + if (isNewLUT) { + const lookupTableObj = AddressLookupTableProgram.createLookupTable({ + authority: client.provider.wallet.publicKey, + payer: client.provider.wallet.publicKey, + recentSlot: slot, + }); + + const lookupTableInst = lookupTableObj[0]; + lookupTableAddress = lookupTableObj[1]; + + let latestBlockhash = await client.provider.connection.getLatestBlockhash(); + + const messageLegacy = new TransactionMessage({ + payerKey: client.provider.wallet.publicKey, + recentBlockhash: latestBlockhash.blockhash, + instructions: [lookupTableInst], + }).compileToLegacyMessage(); + + let transaction = new VersionedTransaction(messageLegacy); + transaction = await client.provider.wallet.signTransaction(transaction); + + let signature = await client.provider.connection.sendTransaction( + transaction, + { skipPreflight: true } + ); + await client.provider.connection.confirmTransaction( + { signature, ...latestBlockhash }, + "confirmed" + ); + } + + if (print) { + console.log("lookup table: ", lookupTableAddress.toBase58()); + } + + let addresses = new Set([ + ComputeBudgetProgram.programId, + SystemProgram.programId, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + SYSVAR_RENT_PUBKEY, + AUTOCRAT_PROGRAM_ID, + getDaoAddr(AUTOCRAT_PROGRAM_ID, daoId)[0], + getDaoTreasuryAddr(AUTOCRAT_PROGRAM_ID, daoId)[0], + META_MINT, + USDC_MINT, + ]); + + addresses.delete(PublicKey.default); + + const lookupTableAccount = await client.provider.connection + .getAddressLookupTable(lookupTableAddress) + .then((res) => res.value); + + for (let pk of addresses) { + if (pk.toBase58() === PublicKey.default.toBase58()) { + continue; + } - let transaction = new VersionedTransaction(messageLegacy) - transaction = await client.provider.wallet.signTransaction(transaction) + let alreadyAdded = false; + for (let addr of lookupTableAccount?.state.addresses || []) { + if (addr.toBase58() === pk.toBase58()) { + if (print) { + console.log("already added", pk.toBase58()); + } + alreadyAdded = true; + break; + } + } - let signature = await client.provider.connection.sendTransaction(transaction, {skipPreflight: true}); - await client.provider.connection.confirmTransaction({signature, ...latestBlockhash}, 'confirmed'); + if (alreadyAdded) { + continue; } if (print) { - console.log("lookup table: ", lookupTableAddress.toBase58()) + console.log("adding [", pk.toBase58(), "] to lut"); } - let addresses = new Set([ - ComputeBudgetProgram.programId, - SystemProgram.programId, - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID, - SYSVAR_RENT_PUBKEY, - AUTOCRAT_PROGRAM_ID, - getDaoAddr(AUTOCRAT_PROGRAM_ID)[0], - getDaoTreasuryAddr(AUTOCRAT_PROGRAM_ID)[0], - META_MINT, - USDC_MINT - ]) - - addresses.delete(PublicKey.default) - - const lookupTableAccount = await client.provider.connection - .getAddressLookupTable(lookupTableAddress) - .then((res) => res.value); - - for (let pk of addresses){ - if (pk.toBase58() === PublicKey.default.toBase58()){ - continue - } - - let alreadyAdded = false - for (let addr of lookupTableAccount?.state.addresses || []){ - if (addr.toBase58() === pk.toBase58()){ - if (print) { - console.log("already added", pk.toBase58()) - } - alreadyAdded = true - break - } - } - - if (alreadyAdded){ - continue - } - - if (print) { - console.log("adding [", pk.toBase58(), "] to lut") - } - - try { - const extendInstruction = AddressLookupTableProgram.extendLookupTable({ - payer: client.provider.wallet.publicKey, - authority: client.provider.wallet.publicKey, - lookupTable: lookupTableAddress, - addresses: [pk], - }); - - let latestBlockhash = await client.provider.connection.getLatestBlockhash() - - const messageLegacy = new TransactionMessage({ - payerKey: client.provider.wallet.publicKey, - recentBlockhash: latestBlockhash.blockhash, - instructions: [extendInstruction], - }).compileToLegacyMessage(); - - // Create a new VersionedTransaction which supports legacy and v0 - let transaction = new VersionedTransaction(messageLegacy) - transaction = await client.provider.wallet.signTransaction(transaction) - - let signature = await client.provider.connection.sendTransaction(transaction, {skipPreflight: true}); - await client.provider.connection.confirmTransaction({signature, ...latestBlockhash}, 'confirmed'); - } catch (e){ - console.log(e) - } + try { + const extendInstruction = AddressLookupTableProgram.extendLookupTable({ + payer: client.provider.wallet.publicKey, + authority: client.provider.wallet.publicKey, + lookupTable: lookupTableAddress, + addresses: [pk], + }); + + let latestBlockhash = + await client.provider.connection.getLatestBlockhash(); + + const messageLegacy = new TransactionMessage({ + payerKey: client.provider.wallet.publicKey, + recentBlockhash: latestBlockhash.blockhash, + instructions: [extendInstruction], + }).compileToLegacyMessage(); + + // Create a new VersionedTransaction which supports legacy and v0 + let transaction = new VersionedTransaction(messageLegacy); + transaction = await client.provider.wallet.signTransaction(transaction); + + let signature = await client.provider.connection.sendTransaction( + transaction, + { skipPreflight: true } + ); + await client.provider.connection.confirmTransaction( + { signature, ...latestBlockhash }, + "confirmed" + ); + } catch (e) { + console.log(e); } + } - return lookupTableAddress + return lookupTableAddress; } - diff --git a/app/src/utils/numbers.ts b/app/src/utils/numbers.ts index 32e1340..2ec3896 100644 --- a/app/src/utils/numbers.ts +++ b/app/src/utils/numbers.ts @@ -1,14 +1,14 @@ import { BN } from "bn.js"; -import { Decimal } from 'decimal.js'; +import { Decimal } from "decimal.js"; export const numToBytes32LE = (num: number) => { - let bytesU32 = Buffer.alloc(4); - bytesU32.writeInt32LE(num) - return bytesU32 -} + let bytesU32 = Buffer.alloc(4); + bytesU32.writeInt32LE(num); + return bytesU32; +}; export const numToBytes64LE = (num: number) => { - let bytesU64 = Buffer.alloc(8); - bytesU64.writeBigUInt64LE(BigInt(num)) - return bytesU64 -} \ No newline at end of file + let bytesU64 = Buffer.alloc(8); + bytesU64.writeBigUInt64LE(BigInt(num)); + return bytesU64; +}; diff --git a/app/src/utils/pda.ts b/app/src/utils/pda.ts index b98d22e..04c6f88 100644 --- a/app/src/utils/pda.ts +++ b/app/src/utils/pda.ts @@ -1,93 +1,110 @@ -import { AccountMeta, PublicKey } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; import { utils } from "@coral-xyz/anchor"; -import { numToBytes32LE, numToBytes64LE } from "./numbers"; -import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { numToBytes64LE } from "./numbers"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; export const getDaoAddr = ( - programId: PublicKey, + programId: PublicKey, + daoId: PublicKey ): [PublicKey, number] => { - return PublicKey.findProgramAddressSync( - [utils.bytes.utf8.encode("WWCACOTMICMIBMHAFTTWYGHMB")], - programId, - ); + return PublicKey.findProgramAddressSync([daoId.toBuffer()], programId); }; export const getDaoTreasuryAddr = ( - programId: PublicKey, + programId: PublicKey, + daoId: PublicKey ): [PublicKey, number] => { - let [dao] = getDaoAddr(programId) - return PublicKey.findProgramAddressSync( - [utils.bytes.utf8.encode("dao_treasury"), dao.toBuffer()], - programId, - ); + let [dao] = getDaoAddr(programId, daoId); + return PublicKey.findProgramAddressSync( + [utils.bytes.utf8.encode("dao_treasury"), dao.toBuffer()], + programId + ); }; export const getProposalAddr = ( - programId: PublicKey, - proposalNumber: number, + programId: PublicKey, + dao: PublicKey, + proposalNumber: number ): [PublicKey, number] => { - return PublicKey.findProgramAddressSync( - [utils.bytes.utf8.encode("proposal__"), numToBytes64LE(proposalNumber)], - programId, - ); + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("proposal__"), + dao.toBuffer(), + numToBytes64LE(proposalNumber), + ], + programId + ); }; export const getProposalInstructionsAddr = ( - programId: PublicKey, - proposal: PublicKey, + programId: PublicKey, + proposal: PublicKey ): [PublicKey, number] => { - return PublicKey.findProgramAddressSync( - [utils.bytes.utf8.encode("proposal_instructions"), proposal.toBuffer()], - programId, - ); + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("proposal_instructions"), + proposal.toBuffer(), + ], + programId + ); }; export const getProposalVaultAddr = ( - programId: PublicKey, - proposal: PublicKey + programId: PublicKey, + proposal: PublicKey ): [PublicKey, number] => { - return PublicKey.findProgramAddressSync( - [utils.bytes.utf8.encode("proposal_vault"), proposal.toBuffer()], - programId, - ); + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("proposal_vault"), + proposal.toBuffer(), + ], + programId + ); }; export const getAmmAddr = ( - programId: PublicKey, - baseMint: PublicKey, - quoteMint: PublicKey, - swapFeeBps: number, - permissionedCaller: PublicKey + programId: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + swapFeeBps: number, + permissionedCaller: PublicKey ): [PublicKey, number] => { - return PublicKey.findProgramAddressSync( - [utils.bytes.utf8.encode("amm__"), baseMint.toBuffer(), quoteMint.toBuffer(), numToBytes64LE(swapFeeBps), permissionedCaller.toBuffer()], - programId, - ); + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("amm__"), + baseMint.toBuffer(), + quoteMint.toBuffer(), + numToBytes64LE(swapFeeBps), + permissionedCaller.toBuffer(), + ], + programId + ); }; export const getAmmPositionAddr = ( - programId: PublicKey, - amm: PublicKey, - user: PublicKey + programId: PublicKey, + amm: PublicKey, + user: PublicKey ): [PublicKey, number] => { - return PublicKey.findProgramAddressSync( - [utils.bytes.utf8.encode("amm_position"), amm.toBuffer(), user.toBuffer()], - programId, - ); + return PublicKey.findProgramAddressSync( + [utils.bytes.utf8.encode("amm_position"), amm.toBuffer(), user.toBuffer()], + programId + ); }; -export const getAmmAuthAddr = ( - programId: PublicKey, -): [PublicKey, number] => { - return PublicKey.findProgramAddressSync( - [utils.bytes.utf8.encode("amm_auth")], - programId, - ); +export const getAmmAuthAddr = (programId: PublicKey): [PublicKey, number] => { + return PublicKey.findProgramAddressSync( + [utils.bytes.utf8.encode("amm_auth")], + programId + ); }; export const getATA = (mint: PublicKey, owner: PublicKey) => { - return PublicKey.findProgramAddressSync( - [owner.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], - ASSOCIATED_TOKEN_PROGRAM_ID, - ); -}; \ No newline at end of file + return PublicKey.findProgramAddressSync( + [owner.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], + ASSOCIATED_TOKEN_PROGRAM_ID + ); +}; diff --git a/package.json b/package.json index c1f60f3..27c0341 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "scripts": { + "build": "anchor build && cd app && yarn build", "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" }, diff --git a/programs/amm/src/instructions/create_amm.rs b/programs/amm/src/instructions/create_amm.rs index 52b4faf..8cfd4d3 100644 --- a/programs/amm/src/instructions/create_amm.rs +++ b/programs/amm/src/instructions/create_amm.rs @@ -4,7 +4,6 @@ use anchor_spl::associated_token::AssociatedToken; use anchor_spl::token; use anchor_spl::token::*; -use crate::error::ErrorCode; use crate::state::*; use crate::BPS_SCALE; diff --git a/programs/amm/src/instructions/create_position.rs b/programs/amm/src/instructions/create_position.rs index 90375ba..b112b2b 100644 --- a/programs/amm/src/instructions/create_position.rs +++ b/programs/amm/src/instructions/create_position.rs @@ -1,6 +1,5 @@ use anchor_lang::prelude::*; -use crate::error::ErrorCode; use crate::state::*; #[derive(Accounts)] diff --git a/programs/amm/src/instructions/remove_liquidity.rs b/programs/amm/src/instructions/remove_liquidity.rs index 693791d..0e01a28 100644 --- a/programs/amm/src/instructions/remove_liquidity.rs +++ b/programs/amm/src/instructions/remove_liquidity.rs @@ -5,7 +5,6 @@ use anchor_spl::token; use anchor_spl::token::*; use num_traits::ToPrimitive; -use crate::error::ErrorCode; use crate::generate_vault_seeds; use crate::state::*; use crate::{utils::*, BPS_SCALE}; diff --git a/programs/amm/src/lib.rs b/programs/amm/src/lib.rs index a154e46..d651e22 100644 --- a/programs/amm/src/lib.rs +++ b/programs/amm/src/lib.rs @@ -20,10 +20,8 @@ pub mod instructions; pub mod state; pub mod utils; -use crate::error::*; use crate::instructions::*; use crate::state::*; -use crate::utils::*; declare_id!("Ens7Gx99whnA8zZm6ZiFnWgGq3x76nXbSmh5gaaJqpAz"); #[program] diff --git a/programs/amm/src/state/amm.rs b/programs/amm/src/state/amm.rs index a4087e1..4bca6f8 100644 --- a/programs/amm/src/state/amm.rs +++ b/programs/amm/src/state/amm.rs @@ -2,7 +2,6 @@ use anchor_lang::prelude::*; use num_traits::{FromPrimitive, ToPrimitive}; use rust_decimal::{Decimal, MathematicalOps}; -use crate::error::ErrorCode; use crate::utils::anchor_decimal::*; use crate::utils::*; diff --git a/programs/amm/src/utils/mod.rs b/programs/amm/src/utils/mod.rs index 429fd9b..e7bf4df 100644 --- a/programs/amm/src/utils/mod.rs +++ b/programs/amm/src/utils/mod.rs @@ -2,7 +2,6 @@ use crate::error::ErrorCode; use anchor_lang::prelude::*; pub use anchor_decimal::*; -pub use seeds::*; pub use token::*; pub mod anchor_decimal; diff --git a/programs/amm/src/utils/seeds.rs b/programs/amm/src/utils/seeds.rs index 28ffd08..76d9f38 100644 --- a/programs/amm/src/utils/seeds.rs +++ b/programs/amm/src/utils/seeds.rs @@ -1,5 +1,3 @@ -use crate::state::*; - #[macro_export] macro_rules! generate_vault_seeds { ($base_mint:expr, $quote_mint:expr, $swap_fee_bps:expr, $permissioned_caller:expr, $bump:expr) => {{ diff --git a/programs/autocrat/src/instructions/amm_cpi/add_liquidity.rs b/programs/autocrat/src/instructions/amm_cpi/add_liquidity.rs index 0c9c5a9..d8bd803 100644 --- a/programs/autocrat/src/instructions/amm_cpi/add_liquidity.rs +++ b/programs/autocrat/src/instructions/amm_cpi/add_liquidity.rs @@ -23,6 +23,7 @@ pub struct AddLiquidity<'info> { )] pub proposal: Box>, #[account( + has_one = proposal, seeds = [ PROPOSAL_VAULT_SEED_PREFIX, proposal.key().as_ref(), diff --git a/programs/autocrat/src/instructions/autocrat/add_proposal_instructions.rs b/programs/autocrat/src/instructions/autocrat/add_proposal_instructions.rs index 17fee80..b60eb98 100644 --- a/programs/autocrat/src/instructions/autocrat/add_proposal_instructions.rs +++ b/programs/autocrat/src/instructions/autocrat/add_proposal_instructions.rs @@ -13,6 +13,7 @@ pub struct AddProposalInstructions<'info> { has_one = proposer, seeds = [ PROPOSAL_SEED_PREFIX, + proposal.dao.as_ref(), proposal.number.to_le_bytes().as_ref() ], bump diff --git a/programs/autocrat/src/instructions/autocrat/create_proposal.rs b/programs/autocrat/src/instructions/autocrat/create_proposal.rs index 596ad5e..e25d73c 100644 --- a/programs/autocrat/src/instructions/autocrat/create_proposal.rs +++ b/programs/autocrat/src/instructions/autocrat/create_proposal.rs @@ -1,5 +1,4 @@ use anchor_lang::prelude::*; -use anchor_lang::solana_program::sysvar::instructions as tx_instructions; use anchor_spl::associated_token; use anchor_spl::associated_token::AssociatedToken; use anchor_spl::token; @@ -7,7 +6,6 @@ use anchor_spl::token::Mint; use anchor_spl::token::Token; use anchor_spl::token::TokenAccount; -use crate::error::ErrorCode; use crate::state::*; use crate::utils::*; @@ -19,7 +17,7 @@ pub struct CreateProposal<'info> { mut, has_one = meta_mint, has_one = usdc_mint, - seeds = [b"WWCACOTMICMIBMHAFTTWYGHMB"], + seeds = [dao.id.as_ref()], bump )] pub dao: Box>, @@ -29,6 +27,7 @@ pub struct CreateProposal<'info> { space = 8 + Proposal::INIT_SPACE, seeds = [ PROPOSAL_SEED_PREFIX, + dao.key().as_ref(), dao.proposal_count.to_le_bytes().as_ref(), ], bump @@ -107,6 +106,8 @@ pub fn handler( assert!(mint_cond_meta > 0); assert!(mint_cond_usdc >= dao.amm_initial_quote_liquidity_amount); + proposal.dao = dao.key(); + proposal.proposer = proposer.key(); proposal.state = ProposalState::Initialize; proposal.description_url = description_url; diff --git a/programs/autocrat/src/instructions/autocrat/create_proposal_instructions.rs b/programs/autocrat/src/instructions/autocrat/create_proposal_instructions.rs index b992616..9e00434 100644 --- a/programs/autocrat/src/instructions/autocrat/create_proposal_instructions.rs +++ b/programs/autocrat/src/instructions/autocrat/create_proposal_instructions.rs @@ -13,6 +13,7 @@ pub struct CreateProposalInstructions<'info> { has_one = proposer, seeds = [ PROPOSAL_SEED_PREFIX, + proposal.dao.as_ref(), proposal.number.to_le_bytes().as_ref() ], bump diff --git a/programs/autocrat/src/instructions/autocrat/create_proposal_market_side.rs b/programs/autocrat/src/instructions/autocrat/create_proposal_market_side.rs index 315b7a2..ec377e2 100644 --- a/programs/autocrat/src/instructions/autocrat/create_proposal_market_side.rs +++ b/programs/autocrat/src/instructions/autocrat/create_proposal_market_side.rs @@ -12,7 +12,6 @@ use amm::cpi::accounts::CreatePosition; use amm::instructions::create_amm::CreateAmmParams; use amm::program::Amm; -use crate::error::ErrorCode; use crate::generate_proposal_vault_seeds; use crate::program::Autocrat; use crate::state::*; @@ -25,8 +24,10 @@ pub struct CreateProposalMarketSide<'info> { #[account( mut, has_one = proposer, + has_one = dao, seeds = [ PROPOSAL_SEED_PREFIX, + proposal.dao.as_ref(), proposal.number.to_le_bytes().as_ref(), ], bump @@ -34,6 +35,7 @@ pub struct CreateProposalMarketSide<'info> { pub proposal: Box>, #[account( mut, + has_one = proposal, seeds = [ PROPOSAL_VAULT_SEED_PREFIX, proposal.key().as_ref(), @@ -42,10 +44,9 @@ pub struct CreateProposalMarketSide<'info> { )] pub proposal_vault: Box>, #[account( - mut, has_one = meta_mint, has_one = usdc_mint, - seeds = [b"WWCACOTMICMIBMHAFTTWYGHMB"], + seeds = [dao.id.as_ref()], bump )] pub dao: Box>, diff --git a/programs/autocrat/src/instructions/autocrat/finalize_proposal.rs b/programs/autocrat/src/instructions/autocrat/finalize_proposal.rs index 995f1e7..f868f37 100644 --- a/programs/autocrat/src/instructions/autocrat/finalize_proposal.rs +++ b/programs/autocrat/src/instructions/autocrat/finalize_proposal.rs @@ -13,10 +13,12 @@ pub struct FinalizeProposal<'info> { pub user: Signer<'info>, #[account( mut, + has_one = dao, has_one = pass_market_amm, has_one = fail_market_amm, seeds = [ PROPOSAL_SEED_PREFIX, + proposal.dao.as_ref(), proposal.number.to_le_bytes().as_ref() ], bump @@ -30,7 +32,7 @@ pub struct FinalizeProposal<'info> { pub proposal_instructions: Account<'info, ProposalInstructions>, #[account( mut, - seeds = [b"WWCACOTMICMIBMHAFTTWYGHMB"], + seeds = [dao.id.as_ref()], bump )] pub dao: Box>, diff --git a/programs/autocrat/src/instructions/autocrat/merge_conditional_tokens.rs b/programs/autocrat/src/instructions/autocrat/merge_conditional_tokens.rs index f2bb26c..0d8e0ed 100644 --- a/programs/autocrat/src/instructions/autocrat/merge_conditional_tokens.rs +++ b/programs/autocrat/src/instructions/autocrat/merge_conditional_tokens.rs @@ -4,7 +4,6 @@ use anchor_spl::associated_token::AssociatedToken; use anchor_spl::token; use anchor_spl::token::*; -use crate::error::ErrorCode; use crate::generate_proposal_vault_seeds; use crate::state::*; use crate::utils::token::*; @@ -22,6 +21,7 @@ pub struct MergeConditionalTokens<'info> { has_one = conditional_on_fail_usdc_mint, seeds = [ PROPOSAL_SEED_PREFIX, + proposal.dao.as_ref(), proposal.number.to_le_bytes().as_ref() ], bump @@ -29,6 +29,7 @@ pub struct MergeConditionalTokens<'info> { pub proposal: Box>, #[account( mut, + has_one = proposal, has_one = meta_vault_ata, has_one = usdc_vault_ata, seeds = [ diff --git a/programs/autocrat/src/instructions/autocrat/mint_conditional_tokens.rs b/programs/autocrat/src/instructions/autocrat/mint_conditional_tokens.rs index e38b947..4ce40ca 100644 --- a/programs/autocrat/src/instructions/autocrat/mint_conditional_tokens.rs +++ b/programs/autocrat/src/instructions/autocrat/mint_conditional_tokens.rs @@ -4,7 +4,6 @@ use anchor_spl::associated_token::AssociatedToken; use anchor_spl::token; use anchor_spl::token::*; -use crate::error::ErrorCode; use crate::generate_proposal_vault_seeds; use crate::state::*; use crate::utils::token::*; @@ -22,6 +21,7 @@ pub struct MintConditionalTokens<'info> { has_one = conditional_on_fail_usdc_mint, seeds = [ PROPOSAL_SEED_PREFIX, + proposal.dao.as_ref(), proposal.number.to_le_bytes().as_ref() ], bump @@ -29,6 +29,7 @@ pub struct MintConditionalTokens<'info> { pub proposal: Box>, #[account( mut, + has_one = proposal, has_one = meta_vault_ata, has_one = usdc_vault_ata, seeds = [ diff --git a/programs/autocrat/src/instructions/autocrat/redeem_conditional_tokens.rs b/programs/autocrat/src/instructions/autocrat/redeem_conditional_tokens.rs index d7ddd4d..90e25d8 100644 --- a/programs/autocrat/src/instructions/autocrat/redeem_conditional_tokens.rs +++ b/programs/autocrat/src/instructions/autocrat/redeem_conditional_tokens.rs @@ -22,6 +22,7 @@ pub struct RedeemConditionalTokens<'info> { has_one = conditional_on_fail_usdc_mint, seeds = [ PROPOSAL_SEED_PREFIX, + proposal.dao.as_ref(), proposal.number.to_le_bytes().as_ref() ], bump diff --git a/programs/autocrat/src/instructions/autocrat/submit_proposal.rs b/programs/autocrat/src/instructions/autocrat/submit_proposal.rs index 9d109e9..77fc092 100644 --- a/programs/autocrat/src/instructions/autocrat/submit_proposal.rs +++ b/programs/autocrat/src/instructions/autocrat/submit_proposal.rs @@ -10,7 +10,6 @@ use crate::program::Autocrat; use amm::cpi::accounts::UpdateLtwap; use amm::program::Amm; -use crate::error::ErrorCode; use crate::state::*; use crate::utils::*; @@ -21,7 +20,7 @@ pub struct SubmitProposal<'info> { #[account( mut, has_one = usdc_mint, - seeds = [b"WWCACOTMICMIBMHAFTTWYGHMB"], + seeds = [dao.id.as_ref()], bump )] pub dao: Box>, @@ -32,18 +31,21 @@ pub struct SubmitProposal<'info> { pub dao_treasury: Account<'info, DaoTreasury>, #[account( mut, + has_one = dao, has_one = proposer, has_one = pass_market_amm, has_one = fail_market_amm, seeds = [ PROPOSAL_SEED_PREFIX, + proposal.dao.as_ref(), proposal.number.to_le_bytes().as_ref() - ], + ], bump )] pub proposal: Box>, #[account( mut, + has_one = proposal, seeds = [ PROPOSAL_VAULT_SEED_PREFIX, proposal.key().as_ref(), diff --git a/programs/autocrat/src/instructions/dao/initialize.rs b/programs/autocrat/src/instructions/dao/initialize.rs index 555aaa9..702f43f 100644 --- a/programs/autocrat/src/instructions/dao/initialize.rs +++ b/programs/autocrat/src/instructions/dao/initialize.rs @@ -7,6 +7,7 @@ use anchor_spl::token::*; use crate::state::*; #[derive(Accounts)] +#[instruction(id: Pubkey)] pub struct InitializeDao<'info> { #[account(mut)] pub payer: Signer<'info>, @@ -14,10 +15,7 @@ pub struct InitializeDao<'info> { init, payer = payer, space = 8 + std::mem::size_of::(), - // We will create a civilization of the Mind in Cyberspace. May it be - // more humane and fair than the world your governments have made before. - // - John Perry Barlow, A Declaration of the Independence of Cyberspace - seeds = [b"WWCACOTMICMIBMHAFTTWYGHMB"], + seeds = [id.as_ref()], bump )] pub dao: Account<'info, Dao>, @@ -40,7 +38,7 @@ pub struct InitializeDao<'info> { pub system_program: Program<'info, System>, } -pub fn handler(ctx: Context) -> Result<()> { +pub fn handler(ctx: Context, id: Pubkey) -> Result<()> { let InitializeDao { payer: _, dao, @@ -55,6 +53,8 @@ pub fn handler(ctx: Context) -> Result<()> { dao_treasury.dao = dao.key(); dao_treasury.bump = ctx.bumps.dao_treasury; + dao.id = id; + dao.treasury_pda_bump = ctx.bumps.dao_treasury; dao.treasury_pda = dao_treasury.key(); diff --git a/programs/autocrat/src/instructions/dao/update.rs b/programs/autocrat/src/instructions/dao/update.rs index 2dbe922..f3dc363 100644 --- a/programs/autocrat/src/instructions/dao/update.rs +++ b/programs/autocrat/src/instructions/dao/update.rs @@ -6,7 +6,7 @@ use crate::state::*; pub struct UpdateDao<'info> { #[account( mut, - seeds = [b"WWCACOTMICMIBMHAFTTWYGHMB"], + seeds = [dao.id.as_ref()], bump )] pub dao: Account<'info, Dao>, diff --git a/programs/autocrat/src/lib.rs b/programs/autocrat/src/lib.rs index b535834..d46ef7e 100644 --- a/programs/autocrat/src/lib.rs +++ b/programs/autocrat/src/lib.rs @@ -20,10 +20,8 @@ pub mod instructions; pub mod state; pub mod utils; -use crate::error::*; use crate::instructions::*; use crate::state::*; -use crate::utils::*; declare_id!("66629qDqH5vJuz4ZgaL1HVpeAC9kJXnzamMpvMJfr3kE"); @@ -32,8 +30,8 @@ pub mod autocrat { use super::*; // ==== dao - pub fn initialize_dao(ctx: Context) -> Result<()> { - instructions::dao::initialize::handler(ctx) + pub fn initialize_dao(ctx: Context, id: Pubkey) -> Result<()> { + instructions::dao::initialize::handler(ctx, id) } pub fn update_dao(ctx: Context, dao_params: UpdateDaoParams) -> Result<()> { diff --git a/programs/autocrat/src/state/dao.rs b/programs/autocrat/src/state/dao.rs index 9b00160..5eb1d90 100644 --- a/programs/autocrat/src/state/dao.rs +++ b/programs/autocrat/src/state/dao.rs @@ -2,6 +2,8 @@ use anchor_lang::prelude::*; #[account] pub struct Dao { + pub id: Pubkey, + // treasury needed even though DAO is PDA for this reason: https://solana.stackexchange.com/questions/7667/a-peculiar-problem-with-cpis pub treasury_pda_bump: u8, pub treasury_pda: Pubkey, diff --git a/programs/autocrat/src/state/proposal.rs b/programs/autocrat/src/state/proposal.rs index 39ff737..2a0b003 100644 --- a/programs/autocrat/src/state/proposal.rs +++ b/programs/autocrat/src/state/proposal.rs @@ -12,6 +12,8 @@ pub enum ProposalState { #[account] #[derive(InitSpace)] pub struct Proposal { + pub dao: Pubkey, + pub number: u64, pub proposer: Pubkey, #[max_len(100)] diff --git a/scripts/submitProposal.ts b/scripts/submitProposal.ts index 0e4570f..baa21bc 100644 --- a/scripts/submitProposal.ts +++ b/scripts/submitProposal.ts @@ -1,107 +1,113 @@ import { Connection, Keypair, PublicKey } from "@solana/web3.js"; import { AnchorProvider, BN, Wallet } from "@coral-xyz/anchor"; -import path from 'path'; -import dotenv from 'dotenv'; +import path from "path"; +import dotenv from "dotenv"; import { readFileSync } from "fs"; import { AutocratClient } from "../app/src/AutocratClient"; import { getDaoAddr } from "../app/src/utils"; import { MEMO_PROGRAM_ID } from "@solana/spl-memo"; async function submitProposal() { - const rpcUrl = "my-rpc-node" - const connectionConfig = {} - const priorityFee = 10_000 - - const envPath = path.resolve(__dirname, '..', '.env'); // .env should include: KEYPAIR_PATH=/path/to/my/keypair.json - dotenv.config({ path: envPath }); - - const keypair = Keypair.fromSecretKey(new Uint8Array(JSON.parse(readFileSync(process.env.KEYPAIR_PATH, 'utf-8')))); - const wallet = new Wallet(keypair); - - const connection = new Connection(rpcUrl, connectionConfig) - const provider = new AnchorProvider(connection, wallet, AnchorProvider.defaultOptions()); - const autocratClient = await AutocratClient.createClient({ provider }) - - const daoAddr = getDaoAddr(autocratClient.program.programId)[0] - let daoAccount = await autocratClient.program.account.dao.fetch(daoAddr) - - // const proposalNumber = 10 - const proposalNumber = daoAccount.proposalCount.toNumber() - - const proposalUrl = "url-for-my-cool-proposal" - - const passMarketInitialPrice = 1030 - const failMarketInitialPrice = 1000 - - // ==== calculate LP deposit amounts - const minUsdcLiquidity = daoAccount.ammInitialQuoteLiquidityAmount.toNumber() / 10 ** 6 - - const cMetaForPassMarket = (minUsdcLiquidity / passMarketInitialPrice) * 10 ** 9 - const cMetaForFailMarket = (minUsdcLiquidity / failMarketInitialPrice) * 10 ** 9 - - const cMetaToMint = Math.max(cMetaForPassMarket, cMetaForFailMarket) - - // ==== create proposal - let createPropIxh = await autocratClient.createProposal( - proposalNumber, - proposalUrl, - new BN(cMetaToMint), - daoAccount.ammInitialQuoteLiquidityAmount, - ) - await createPropIxh - .setPriorityFee(priorityFee) - .rpc(); - - // ==== create proposal instruction account - const memoText = - "I, glorious autocrat of the divine Meta-DAO, " + - "hereby endorse this endeavor to elevate the futarchy domain. " + - "Recognize that my utterance echoes not the voice of a mortal but resonates as the proclamation of an omniscient market." + - "Onward, futards, with the swiftness of the divine!"; - - const memoInstruction = { - programId: new PublicKey(MEMO_PROGRAM_ID), - data: Buffer.from(memoText), - accounts: [], - }; - - let createPropIxIxh = await autocratClient.createProposalInstructions(proposalNumber, [memoInstruction]); - await createPropIxIxh - .setPriorityFee(priorityFee) - .rpc(); - - // ==== create pass market - let createPassMarketIxh = await autocratClient.createProposalMarketSide( - proposalNumber, - true, - new BN(cMetaForPassMarket), - daoAccount.ammInitialQuoteLiquidityAmount, - ) - await createPassMarketIxh - .setPriorityFee(priorityFee) - .setComputeUnits(400_000) - .rpc(); - - // ==== create fail market - let createFailMarketIxh = await autocratClient.createProposalMarketSide( - proposalNumber, - false, - new BN(cMetaForFailMarket), - daoAccount.ammInitialQuoteLiquidityAmount, - ) - await createFailMarketIxh - .setPriorityFee(priorityFee) - .setComputeUnits(400_000) - .rpc(); - - // ==== submit proposal - let submitProposalIxh = await autocratClient.submitProposal( - proposalNumber, - ); - await submitProposalIxh - .setPriorityFee(priorityFee) - .setComputeUnits(400_000) - .rpc(); + const rpcUrl = "my-rpc-node"; + const connectionConfig = {}; + const priorityFee = 10_000; + + const envPath = path.resolve(__dirname, "..", ".env"); // .env should include: KEYPAIR_PATH=/path/to/my/keypair.json + dotenv.config({ path: envPath }); + + const keypair = Keypair.fromSecretKey( + new Uint8Array(JSON.parse(readFileSync(process.env.KEYPAIR_PATH, "utf-8"))) + ); + const wallet = new Wallet(keypair); + + const connection = new Connection(rpcUrl, connectionConfig); + const provider = new AnchorProvider( + connection, + wallet, + AnchorProvider.defaultOptions() + ); + const autocratClient = await AutocratClient.createClient({ provider }); + + const daoAddr = getDaoAddr(autocratClient.program.programId)[0]; + let daoAccount = await autocratClient.program.account.dao.fetch(daoAddr); + + // const proposalNumber = 10 + const proposalNumber = daoAccount.proposalCount.toNumber(); + + const proposalUrl = "url-for-my-cool-proposal"; + + const passMarketInitialPrice = 1030; + const failMarketInitialPrice = 1000; + + // ==== calculate LP deposit amounts + const minUsdcLiquidity = + daoAccount.ammInitialQuoteLiquidityAmount.toNumber() / 10 ** 6; + + const cMetaForPassMarket = + (minUsdcLiquidity / passMarketInitialPrice) * 10 ** 9; + const cMetaForFailMarket = + (minUsdcLiquidity / failMarketInitialPrice) * 10 ** 9; + + const cMetaToMint = Math.max(cMetaForPassMarket, cMetaForFailMarket); + + // ==== create proposal + let createPropIxh = await autocratClient.createProposal( + proposalNumber, + proposalUrl, + new BN(cMetaToMint), + daoAccount.ammInitialQuoteLiquidityAmount + ); + await createPropIxh.setPriorityFee(priorityFee).rpc(); + + // ==== create proposal instruction account + const memoText = + "I, glorious autocrat of the divine Meta-DAO, " + + "hereby endorse this endeavor to elevate the futarchy domain. " + + "Recognize that my utterance echoes not the voice of a mortal but resonates as the proclamation of an omniscient market." + + "Onward, futards, with the swiftness of the divine!"; + + const memoInstruction = { + programId: new PublicKey(MEMO_PROGRAM_ID), + data: Buffer.from(memoText), + accounts: [], + }; + + let createPropIxIxh = await autocratClient.createProposalInstructions( + proposalNumber, + [memoInstruction] + ); + await createPropIxIxh.setPriorityFee(priorityFee).rpc(); + + // ==== create pass market + let createPassMarketIxh = await autocratClient.createProposalMarketSide( + proposalNumber, + true, + new BN(cMetaForPassMarket), + daoAccount.ammInitialQuoteLiquidityAmount + ); + await createPassMarketIxh + .setPriorityFee(priorityFee) + .setComputeUnits(400_000) + .rpc(); + + // ==== create fail market + let createFailMarketIxh = await autocratClient.createProposalMarketSide( + proposalNumber, + false, + new BN(cMetaForFailMarket), + daoAccount.ammInitialQuoteLiquidityAmount + ); + await createFailMarketIxh + .setPriorityFee(priorityFee) + .setComputeUnits(400_000) + .rpc(); + + // ==== submit proposal + let submitProposalIxh = await autocratClient.submitProposal(proposalNumber); + await submitProposalIxh + .setPriorityFee(priorityFee) + .setComputeUnits(400_000) + .rpc(); } -submitProposal() \ No newline at end of file +submitProposal(); diff --git a/tests/amm.ts b/tests/amm.ts index bcd0ef2..60e46b7 100644 --- a/tests/amm.ts +++ b/tests/amm.ts @@ -2,11 +2,7 @@ import * as anchor from "@coral-xyz/anchor"; import { BN } from "@coral-xyz/anchor"; import { BankrunProvider } from "anchor-bankrun"; -import { - startAnchor, - Clock, - ProgramTestContext, -} from "solana-bankrun"; +import { startAnchor, Clock, ProgramTestContext } from "solana-bankrun"; import { createMint, @@ -35,15 +31,11 @@ describe("amm", async function () { userUsdcAccount; before(async function () { - context = await startAnchor( - "./", - [], - [] - ); + context = await startAnchor("./", [], []); banksClient = context.banksClient; provider = new BankrunProvider(context); anchor.setProvider(provider); - ammClient = await AmmClient.createClient({ provider }) + ammClient = await AmmClient.createClient({ provider }); payer = provider.wallet.payer; META = await createMint( @@ -80,29 +72,25 @@ describe("amm", async function () { META, userMetaAccount, payer.publicKey, - 1000 * 10 ** 9, - ) + 1000 * 10 ** 9 + ); mintTo( banksClient, payer, USDC, userUsdcAccount, payer.publicKey, - 10000 * 10 ** 6, - ) + 10000 * 10 ** 6 + ); }); beforeEach(async function () { - await fastForward(context, 1n) - }) + await fastForward(context, 1n); + }); describe("#create_amm", async function () { it("create a permissionless amm", async function () { - let ixh = await ammClient.createAmm( - META, - USDC, - 1, - ); + let ixh = await ammClient.createAmm(META, USDC, 1); await ixh.bankrun(banksClient); [permissionlessAmmAddr] = getAmmAddr( @@ -113,14 +101,19 @@ describe("amm", async function () { PublicKey.default ); - const permissionlessAmmAcc = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); + const permissionlessAmmAcc = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); assert.equal(permissionlessAmmAcc.baseMint.toBase58(), META.toBase58()); assert.equal(permissionlessAmmAcc.quoteMint.toBase58(), USDC.toBase58()); assert.equal(permissionlessAmmAcc.baseMintDecimals, 9); assert.equal(permissionlessAmmAcc.quoteMintDecimals, 6); assert.equal(permissionlessAmmAcc.swapFeeBps, 1); - assert.equal(permissionlessAmmAcc.authProgram.toBase58(), PublicKey.default.toBase58()); + assert.equal( + permissionlessAmmAcc.authProgram.toBase58(), + PublicKey.default.toBase58() + ); }); it("create a permissioned amm (using the amm program as auth)", async function () { @@ -128,7 +121,7 @@ describe("amm", async function () { META, USDC, 300, - ammClient.program.programId, + ammClient.program.programId ); await ixh.bankrun(banksClient); @@ -137,28 +130,35 @@ describe("amm", async function () { META, USDC, 300, - ammClient.program.programId, + ammClient.program.programId ); - const permissionedAccessibleAmmAcc = await ammClient.program.account.amm.fetch(permissionedAccessibleAmmAddr); + const permissionedAccessibleAmmAcc = + await ammClient.program.account.amm.fetch( + permissionedAccessibleAmmAddr + ); - assert.equal(permissionedAccessibleAmmAcc.baseMint.toBase58(), META.toBase58()); - assert.equal(permissionedAccessibleAmmAcc.quoteMint.toBase58(), USDC.toBase58()); + assert.equal( + permissionedAccessibleAmmAcc.baseMint.toBase58(), + META.toBase58() + ); + assert.equal( + permissionedAccessibleAmmAcc.quoteMint.toBase58(), + USDC.toBase58() + ); assert.equal(permissionedAccessibleAmmAcc.baseMintDecimals, 9); assert.equal(permissionedAccessibleAmmAcc.quoteMintDecimals, 6); assert.equal(permissionedAccessibleAmmAcc.swapFeeBps, 300); - assert.equal(permissionedAccessibleAmmAcc.authProgram.toBase58(), ammClient.program.programId.toBase58()); + assert.equal( + permissionedAccessibleAmmAcc.authProgram.toBase58(), + ammClient.program.programId.toBase58() + ); }); it("create a permissioned amm (uncontrolled program)", async function () { - let randomAuthCaller = Keypair.generate().publicKey + let randomAuthCaller = Keypair.generate().publicKey; - let ixh = await ammClient.createAmm( - META, - USDC, - 200, - randomAuthCaller, - ); + let ixh = await ammClient.createAmm(META, USDC, 200, randomAuthCaller); await ixh.bankrun(banksClient); [permissionedInaccessibleAmmAddr] = getAmmAddr( @@ -166,17 +166,29 @@ describe("amm", async function () { META, USDC, 200, - randomAuthCaller, + randomAuthCaller ); - const permissionedInaccessibleAmmAcc = await ammClient.program.account.amm.fetch(permissionedInaccessibleAmmAddr); + const permissionedInaccessibleAmmAcc = + await ammClient.program.account.amm.fetch( + permissionedInaccessibleAmmAddr + ); - assert.equal(permissionedInaccessibleAmmAcc.baseMint.toBase58(), META.toBase58()); - assert.equal(permissionedInaccessibleAmmAcc.quoteMint.toBase58(), USDC.toBase58()); + assert.equal( + permissionedInaccessibleAmmAcc.baseMint.toBase58(), + META.toBase58() + ); + assert.equal( + permissionedInaccessibleAmmAcc.quoteMint.toBase58(), + USDC.toBase58() + ); assert.equal(permissionedInaccessibleAmmAcc.baseMintDecimals, 9); assert.equal(permissionedInaccessibleAmmAcc.quoteMintDecimals, 6); assert.equal(permissionedInaccessibleAmmAcc.swapFeeBps, 200); - assert.equal(permissionedInaccessibleAmmAcc.authProgram.toBase58(), randomAuthCaller.toBase58()); + assert.equal( + permissionedInaccessibleAmmAcc.authProgram.toBase58(), + randomAuthCaller.toBase58() + ); }); }); @@ -185,11 +197,24 @@ describe("amm", async function () { let ixh = await ammClient.createAmmPosition(permissionlessAmmAddr); await ixh.bankrun(banksClient); - let permissionlessMarketPositionAddr = getAmmPositionAddr(ammClient.program.programId, permissionlessAmmAddr, payer.publicKey)[0] - const permissionlessMarketPosition = await ammClient.program.account.ammPosition.fetch(permissionlessMarketPositionAddr); - - assert.equal(permissionlessMarketPosition.amm.toBase58(), permissionlessAmmAddr.toBase58()); - assert.equal(permissionlessMarketPosition.user.toBase58(), payer.publicKey.toBase58()); + let permissionlessMarketPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + permissionlessAmmAddr, + payer.publicKey + )[0]; + const permissionlessMarketPosition = + await ammClient.program.account.ammPosition.fetch( + permissionlessMarketPositionAddr + ); + + assert.equal( + permissionlessMarketPosition.amm.toBase58(), + permissionlessAmmAddr.toBase58() + ); + assert.equal( + permissionlessMarketPosition.user.toBase58(), + payer.publicKey.toBase58() + ); }); // it("create new permissioned amm position", async function () { @@ -212,10 +237,17 @@ describe("amm", async function () { describe("#add_liquidity", async function () { it("add liquidity to an amm position", async function () { - const permissionlessAmmStart = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); + const permissionlessAmmStart = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); - let ammPositionAddr = getAmmPositionAddr(ammClient.program.programId, permissionlessAmmAddr, payer.publicKey)[0] - const ammPositionStart = await ammClient.program.account.ammPosition.fetch(ammPositionAddr); + let ammPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + permissionlessAmmAddr, + payer.publicKey + )[0]; + const ammPositionStart = + await ammClient.program.account.ammPosition.fetch(ammPositionAddr); let ixh = await ammClient.addLiquidity( permissionlessAmmAddr, @@ -223,117 +255,173 @@ describe("amm", async function () { new BN(10 * 10 ** 9), new BN(100 * 10 ** 6), new BN(10 * 0.95 * 10 ** 9), - new BN(100 * 0.95 * 10 ** 6), + new BN(100 * 0.95 * 10 ** 6) ); await ixh.bankrun(banksClient); - const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); - const ammPositionEnd = await ammClient.program.account.ammPosition.fetch(ammPositionAddr); + const permissionlessAmmEnd = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); + const ammPositionEnd = await ammClient.program.account.ammPosition.fetch( + ammPositionAddr + ); - assert.isAbove(permissionlessAmmEnd.totalOwnership.toNumber(), permissionlessAmmStart.totalOwnership.toNumber()); - assert.isAbove(ammPositionEnd.ownership.toNumber(), ammPositionStart.ownership.toNumber()); + assert.isAbove( + permissionlessAmmEnd.totalOwnership.toNumber(), + permissionlessAmmStart.totalOwnership.toNumber() + ); + assert.isAbove( + ammPositionEnd.ownership.toNumber(), + ammPositionStart.ownership.toNumber() + ); - assert.isAbove(permissionlessAmmEnd.baseAmount.toNumber(), permissionlessAmmStart.baseAmount.toNumber()); - assert.isAbove(permissionlessAmmEnd.quoteAmount.toNumber(), permissionlessAmmStart.quoteAmount.toNumber()); + assert.isAbove( + permissionlessAmmEnd.baseAmount.toNumber(), + permissionlessAmmStart.baseAmount.toNumber() + ); + assert.isAbove( + permissionlessAmmEnd.quoteAmount.toNumber(), + permissionlessAmmStart.quoteAmount.toNumber() + ); }); }); describe("#swap", async function () { it("swap quote to base", async function () { - const permissionlessAmmStart = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); + const permissionlessAmmStart = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); let ixh = await ammClient.swap( permissionlessAmmAddr, true, new BN(10 * 10 ** 6), - new BN(0.8 * 10 ** 9), + new BN(0.8 * 10 ** 9) ); await ixh.bankrun(banksClient); - const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); + const permissionlessAmmEnd = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); - assert.isBelow(permissionlessAmmEnd.baseAmount.toNumber(), permissionlessAmmStart.baseAmount.toNumber()); - assert.isAbove(permissionlessAmmEnd.quoteAmount.toNumber(), permissionlessAmmStart.quoteAmount.toNumber()); + assert.isBelow( + permissionlessAmmEnd.baseAmount.toNumber(), + permissionlessAmmStart.baseAmount.toNumber() + ); + assert.isAbove( + permissionlessAmmEnd.quoteAmount.toNumber(), + permissionlessAmmStart.quoteAmount.toNumber() + ); }); it("swap base to quote", async function () { - const permissionlessAmmStart = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); + const permissionlessAmmStart = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); let ixh = await ammClient.swap( permissionlessAmmAddr, false, new BN(1 * 10 ** 9), - new BN(8 * 10 ** 6), + new BN(8 * 10 ** 6) ); await ixh.bankrun(banksClient); - const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); + const permissionlessAmmEnd = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); - assert.isAbove(permissionlessAmmEnd.baseAmount.toNumber(), permissionlessAmmStart.baseAmount.toNumber()); - assert.isBelow(permissionlessAmmEnd.quoteAmount.toNumber(), permissionlessAmmStart.quoteAmount.toNumber()); + assert.isAbove( + permissionlessAmmEnd.baseAmount.toNumber(), + permissionlessAmmStart.baseAmount.toNumber() + ); + assert.isBelow( + permissionlessAmmEnd.quoteAmount.toNumber(), + permissionlessAmmStart.quoteAmount.toNumber() + ); }); it("swap base to quote and back, should not be profitable", async function () { - const permissionlessAmmStart = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); + const permissionlessAmmStart = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); - let startingBaseSwapAmount = 1 * 10 ** 9 + let startingBaseSwapAmount = 1 * 10 ** 9; let ixh1 = await ammClient.swap( permissionlessAmmAddr, false, new BN(startingBaseSwapAmount), - new BN(1), + new BN(1) ); await ixh1.bankrun(banksClient); - await fastForward(context, 1n) + await fastForward(context, 1n); - const permissionlessAmmMiddle = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); - let quoteReceived = permissionlessAmmStart.quoteAmount.toNumber() - permissionlessAmmMiddle.quoteAmount.toNumber() + const permissionlessAmmMiddle = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); + let quoteReceived = + permissionlessAmmStart.quoteAmount.toNumber() - + permissionlessAmmMiddle.quoteAmount.toNumber(); let ixh2 = await ammClient.swap( permissionlessAmmAddr, true, new BN(quoteReceived), - new BN(1), + new BN(1) ); await ixh2.bankrun(banksClient); - const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); - let baseReceived = permissionlessAmmMiddle.baseAmount.toNumber() - permissionlessAmmEnd.baseAmount.toNumber() + const permissionlessAmmEnd = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); + let baseReceived = + permissionlessAmmMiddle.baseAmount.toNumber() - + permissionlessAmmEnd.baseAmount.toNumber(); assert.isAbove(startingBaseSwapAmount, baseReceived); assert.isBelow(startingBaseSwapAmount * 0.999, baseReceived); }); it("swap quote to base and back, should not be profitable", async function () { - const permissionlessAmmStart = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); + const permissionlessAmmStart = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); - let startingQuoteSwapAmount = 1 * 10 ** 6 + let startingQuoteSwapAmount = 1 * 10 ** 6; let ixh1 = await ammClient.swap( permissionlessAmmAddr, true, new BN(startingQuoteSwapAmount), - new BN(1), + new BN(1) ); await ixh1.bankrun(banksClient); - await fastForward(context, 1n) + await fastForward(context, 1n); - const permissionlessAmmMiddle = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); - let baseReceived = permissionlessAmmStart.baseAmount.toNumber() - permissionlessAmmMiddle.baseAmount.toNumber() + const permissionlessAmmMiddle = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); + let baseReceived = + permissionlessAmmStart.baseAmount.toNumber() - + permissionlessAmmMiddle.baseAmount.toNumber(); let ixh2 = await ammClient.swap( permissionlessAmmAddr, false, new BN(baseReceived), - new BN(1), + new BN(1) ); await ixh2.bankrun(banksClient); - const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); - let quoteReceived = permissionlessAmmMiddle.quoteAmount.toNumber() - permissionlessAmmEnd.quoteAmount.toNumber() + const permissionlessAmmEnd = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); + let quoteReceived = + permissionlessAmmMiddle.quoteAmount.toNumber() - + permissionlessAmmEnd.quoteAmount.toNumber(); assert.isAbove(startingQuoteSwapAmount, quoteReceived); assert.isBelow(startingQuoteSwapAmount * 0.999, quoteReceived); @@ -348,11 +436,11 @@ describe("amm", async function () { let ixh2 = await ammClient.swap( permissionlessAmmAddr, true, - new BN(2 * 10 ** 9), + new BN(2 * 10 ** 9) ); await ixh2.bankrun(banksClient); - await fastForward(context, 100n) + await fastForward(context, 100n); let ixh3 = await ammClient.updateLTWAP(permissionlessAmmAddr); await ixh3.bankrun(banksClient); @@ -364,11 +452,11 @@ describe("amm", async function () { let ixh4 = await ammClient.swap( permissionlessAmmAddr, false, - new BN(20 * 10 ** 6), + new BN(20 * 10 ** 6) ); await ixh4.bankrun(banksClient); - await fastForward(context, 100n) + await fastForward(context, 100n); let ixh5 = await ammClient.updateLTWAP(permissionlessAmmAddr); await ixh5.bankrun(banksClient); @@ -381,53 +469,96 @@ describe("amm", async function () { describe("#remove_liquidity", async function () { it("remove some liquidity from an amm position", async function () { + const permissionlessAmmStart = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); - const permissionlessAmmStart = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); - - let ammPositionAddr = getAmmPositionAddr(ammClient.program.programId, permissionlessAmmAddr, payer.publicKey)[0] - const ammPositionStart = await ammClient.program.account.ammPosition.fetch(ammPositionAddr); + let ammPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + permissionlessAmmAddr, + payer.publicKey + )[0]; + const ammPositionStart = + await ammClient.program.account.ammPosition.fetch(ammPositionAddr); let ixh = await ammClient.removeLiquidity( permissionlessAmmAddr, ammPositionAddr, - new BN(5_000), + new BN(5_000) ); await ixh.bankrun(banksClient); - const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); - const ammPositionEnd = await ammClient.program.account.ammPosition.fetch(ammPositionAddr); + const permissionlessAmmEnd = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); + const ammPositionEnd = await ammClient.program.account.ammPosition.fetch( + ammPositionAddr + ); - assert.isBelow(permissionlessAmmEnd.totalOwnership.toNumber(), permissionlessAmmStart.totalOwnership.toNumber()); - assert.isBelow(ammPositionEnd.ownership.toNumber(), ammPositionStart.ownership.toNumber()); + assert.isBelow( + permissionlessAmmEnd.totalOwnership.toNumber(), + permissionlessAmmStart.totalOwnership.toNumber() + ); + assert.isBelow( + ammPositionEnd.ownership.toNumber(), + ammPositionStart.ownership.toNumber() + ); - assert.isBelow(permissionlessAmmEnd.baseAmount.toNumber(), permissionlessAmmStart.baseAmount.toNumber()); - assert.isBelow(permissionlessAmmEnd.quoteAmount.toNumber(), permissionlessAmmStart.quoteAmount.toNumber()); + assert.isBelow( + permissionlessAmmEnd.baseAmount.toNumber(), + permissionlessAmmStart.baseAmount.toNumber() + ); + assert.isBelow( + permissionlessAmmEnd.quoteAmount.toNumber(), + permissionlessAmmStart.quoteAmount.toNumber() + ); }); it("remove all liquidity from an amm position", async function () { + const permissionlessAmmStart = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); - const permissionlessAmmStart = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); - - let ammPositionAddr = getAmmPositionAddr(ammClient.program.programId, permissionlessAmmAddr, payer.publicKey)[0] - const ammPositionStart = await ammClient.program.account.ammPosition.fetch(ammPositionAddr); + let ammPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + permissionlessAmmAddr, + payer.publicKey + )[0]; + const ammPositionStart = + await ammClient.program.account.ammPosition.fetch(ammPositionAddr); let ixh = await ammClient.removeLiquidity( permissionlessAmmAddr, ammPositionAddr, - new BN(10_000), + new BN(10_000) ); await ixh.bankrun(banksClient); - const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(permissionlessAmmAddr); - const ammPositionEnd = await ammClient.program.account.ammPosition.fetch(ammPositionAddr); + const permissionlessAmmEnd = await ammClient.program.account.amm.fetch( + permissionlessAmmAddr + ); + const ammPositionEnd = await ammClient.program.account.ammPosition.fetch( + ammPositionAddr + ); - assert.isBelow(permissionlessAmmEnd.totalOwnership.toNumber(), permissionlessAmmStart.totalOwnership.toNumber()); - assert.isBelow(ammPositionEnd.ownership.toNumber(), ammPositionStart.ownership.toNumber()); + assert.isBelow( + permissionlessAmmEnd.totalOwnership.toNumber(), + permissionlessAmmStart.totalOwnership.toNumber() + ); + assert.isBelow( + ammPositionEnd.ownership.toNumber(), + ammPositionStart.ownership.toNumber() + ); assert.equal(ammPositionEnd.ownership.toNumber(), 0); - assert.isBelow(permissionlessAmmEnd.baseAmount.toNumber(), permissionlessAmmStart.baseAmount.toNumber()); - assert.isBelow(permissionlessAmmEnd.quoteAmount.toNumber(), permissionlessAmmStart.quoteAmount.toNumber()); + assert.isBelow( + permissionlessAmmEnd.baseAmount.toNumber(), + permissionlessAmmStart.baseAmount.toNumber() + ); + assert.isBelow( + permissionlessAmmEnd.quoteAmount.toNumber(), + permissionlessAmmStart.quoteAmount.toNumber() + ); }); }); - }); diff --git a/tests/autocrat.ts b/tests/autocrat.ts index 2dd9ec5..d0a043f 100644 --- a/tests/autocrat.ts +++ b/tests/autocrat.ts @@ -4,708 +4,1229 @@ import { BN } from "@coral-xyz/anchor"; import { MEMO_PROGRAM_ID } from "@solana/spl-memo"; import { - startAnchor, - Clock, + startAnchor, + Clock, + BanksClient, + ProgramTestContext, } from "solana-bankrun"; import { - createMint, - createAssociatedTokenAccount, - getAccount, - mintTo, + createMint, + createAssociatedTokenAccount, + getAccount, + mintTo, } from "spl-token-bankrun"; import { assert } from "chai"; import { AutocratClient } from "../app/src/AutocratClient"; -import { getATA, getAmmPositionAddr, getDaoAddr, getDaoTreasuryAddr, getProposalAddr, getProposalInstructionsAddr, sleep } from "../app/src/utils"; +import { + getATA, + getAmmPositionAddr, + getDaoAddr, + getDaoTreasuryAddr, + getProposalAddr, + getProposalInstructionsAddr, + sleep, +} from "../app/src/utils"; import { Keypair, PublicKey } from "@solana/web3.js"; import { AmmClient } from "../app/src/AmmClient"; import { InstructionHandler } from "../app/src/InstructionHandler"; import { BankrunProvider } from "anchor-bankrun"; -import { fastForward } from "./utils"; +import { expectFailure, fastForward } from "./utils"; import { AMM_PROGRAM_ID } from "../app/src/constants"; +import { Dao } from "../app/src"; describe("autocrat", async function () { - let provider, - autocratClient, - ammClient, + let provider: any, + autocratClient: AutocratClient, + ammClient: AmmClient, + payer: Keypair, + context: ProgramTestContext, + banksClient: BanksClient, + daoId: PublicKey, + daoAddr: PublicKey, + dao: Dao, + daoTreasury: PublicKey, + META: PublicKey, + USDC: PublicKey, + proposalInstructionsAddr: PublicKey, + treasuryMetaAccount: PublicKey, + treasuryUsdcAccount: PublicKey, + userMetaAccount: PublicKey, + userUsdcAccount: PublicKey, + proposalNumber: number, + proposalAddr: PublicKey; + + before(async function () { + context = await startAnchor("./", [], []); + banksClient = context.banksClient; + provider = new BankrunProvider(context); + anchor.setProvider(provider); + + autocratClient = await AutocratClient.createClient({ provider }); + ammClient = await AmmClient.createClient({ provider }); + + payer = provider.wallet.payer; + + META = await createMint( + banksClient, + payer, + payer.publicKey, + payer.publicKey, + 9 + ); + USDC = await createMint( + banksClient, + payer, + payer.publicKey, + payer.publicKey, + 6 + ); + }); + + beforeEach(async function () { + await fastForward(context, 1n); + }); + + describe("#initialize_dao", async function () { + it("initializes the DAO", async function () { + daoId = Keypair.generate().publicKey; + + let ixh = await autocratClient.initializeDao(daoId, META, USDC); + await ixh.bankrun(banksClient); + + [daoAddr] = getDaoAddr(autocratClient.program.programId, daoId); + [daoTreasury] = getDaoTreasuryAddr( + autocratClient.program.programId, + daoId + ); + + const daoAcc = await autocratClient.program.account.dao.fetch(daoAddr); + + assert(daoAcc.metaMint.equals(META)); + assert(daoAcc.usdcMint.equals(USDC)); + + assert.equal(daoAcc.proposalCount.toNumber(), 10); + assert.equal(daoAcc.passThresholdBps.toNumber(), 500); + + treasuryMetaAccount = await createAssociatedTokenAccount( + banksClient, payer, - context, + META, + daoTreasury + ); + treasuryUsdcAccount = await createAssociatedTokenAccount( banksClient, - dao, - daoTreasury, + payer, + USDC, + daoTreasury + ); + + userMetaAccount = await createAssociatedTokenAccount( + banksClient, + payer, META, + payer.publicKey + ); + userUsdcAccount = await createAssociatedTokenAccount( + banksClient, + payer, USDC, - proposalInstructionsAddr, - treasuryMetaAccount, - treasuryUsdcAccount, + payer.publicKey + ); + + mintTo( + banksClient, + payer, + META, userMetaAccount, + payer.publicKey, + 1_000 * 10 ** 9 + ); + mintTo( + banksClient, + payer, + USDC, userUsdcAccount, - proposalNumber, - proposalAddr; - - before(async function () { - context = await startAnchor( - "./", - [], - [] - ); - banksClient = context.banksClient; - provider = new BankrunProvider(context); - anchor.setProvider(provider); - - autocratClient = await AutocratClient.createClient({ provider }) - ammClient = await AmmClient.createClient({ provider }) - - payer = provider.wallet.payer; - - META = await createMint( - banksClient, - payer, - payer.publicKey, - payer.publicKey, - 9 - ); - USDC = await createMint( - banksClient, - payer, - payer.publicKey, - payer.publicKey, - 6 - ); + payer.publicKey, + 1_000_000 * 10 ** 6 + ); }); - beforeEach(async function () { - await fastForward(context, 1n) - }) - - describe("#initialize_dao", async function () { - it("initializes the DAO", async function () { - - let ixh = await autocratClient.initializeDao(META, USDC); - await ixh.bankrun(banksClient); - - [dao] = getDaoAddr(autocratClient.program.programId); - [daoTreasury] = getDaoTreasuryAddr(autocratClient.program.programId); - - const daoAcc = await autocratClient.program.account.dao.fetch(dao); - - assert(daoAcc.metaMint.equals(META)); - assert(daoAcc.usdcMint.equals(USDC)); - - assert.equal(daoAcc.proposalCount, 10); - assert.equal(daoAcc.passThresholdBps, 500); - - treasuryMetaAccount = await createAssociatedTokenAccount( - banksClient, - payer, - META, - daoTreasury - ); - treasuryUsdcAccount = await createAssociatedTokenAccount( - banksClient, - payer, - USDC, - daoTreasury - ); - - userMetaAccount = await createAssociatedTokenAccount( - banksClient, - payer, - META, - payer.publicKey - ); - userUsdcAccount = await createAssociatedTokenAccount( - banksClient, - payer, - USDC, - payer.publicKey - ); - - mintTo( - banksClient, - payer, - META, - userMetaAccount, - payer.publicKey, - 1_000 * 10 ** 9, - ) - mintTo( - banksClient, - payer, - USDC, - userUsdcAccount, - payer.publicKey, - 1_000_000 * 10 ** 6, - ) - }); + it("fails when reusing the ID", async function () { + let ixh = await autocratClient.initializeDao(daoId, META, USDC); + await expectFailure(ixh.bankrun(banksClient)); }); - describe("#update_dao", async function () { - it("updates the DAO", async function () { - - let ixh = await autocratClient.updateDao({ - passThresholdBps: new BN(123), - proposalDurationSlots: new BN(69_420), - finalizeWindowSlots: new BN(69_420), - proposalFeeUsdc: new BN(1000 * 10 ** 6), - ammInitialQuoteLiquidityAmount: new BN(100_000_005), - ammSwapFeeBps: new BN(600), - ammLtwapDecimals: 9 - }); - await ixh.bankrun(banksClient); - - let [daoAddr] = getDaoAddr(autocratClient.program.programId); - dao = await autocratClient.program.account.dao.fetch(daoAddr); - - proposalNumber = dao.proposalCount - proposalAddr = getProposalAddr(autocratClient.program.programId, proposalNumber)[0] - - assert.equal(dao.passThresholdBps, 123); - assert.equal(dao.proposalDurationSlots, 69_420); - assert.equal(dao.finalizeWindowSlots, 69_420); - assert.equal(dao.ammInitialQuoteLiquidityAmount, 100_000_005); - assert.equal(dao.ammSwapFeeBps, 600); - assert.equal(dao.ammLtwapDecimals, 9); - }); - }); - - describe("#create_proposal", async function () { - it("creates a proposal", async function () { - - const proposalDescription = "https://based-proposals.com/10" - - let daoAddr = getDaoAddr(autocratClient.program.programId)[0]; - const daoAccStart = await autocratClient.program.account.dao.fetch(daoAddr); - - let ixh = await autocratClient.createProposal( - proposalNumber, - proposalDescription, - new BN(10 * 10 ** 9), - new BN(10_000 * 10 ** 6), - ) - await ixh - .bankrun(banksClient); - - const daoAccEnd = await autocratClient.program.account.dao.fetch(daoAddr); - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); + it("succeeds when using another ID", async function () { + const otherDaoId = Keypair.generate().publicKey; - assert.equal(proposalAcc.proposer.toBase58(), payer.publicKey); - - assert.equal(proposalAcc.proposerInititialConditionalMetaMinted.toNumber(), 10 * 10 ** 9); - assert.equal(proposalAcc.proposerInititialConditionalUsdcMinted.toNumber(), 10_000 * 10 ** 6); - - assert(proposalAcc.state['initialize']) - assert.equal(proposalAcc.descriptionUrl, proposalDescription); - - assert.equal(daoAccStart.proposalCount.toNumber() + 1, daoAccEnd.proposalCount.toNumber()) - }); + let ixh = await autocratClient.initializeDao(otherDaoId, META, USDC); + await ixh.bankrun(banksClient); }); - - describe("#create_proposal_instructions", async function () { - it("creates a proposal instructions account", async function () { - - const memoText = - "I, glorious autocrat of the divine Meta-DAO, " + - "hereby endorse this endeavor to elevate the futarchy domain. " + - "Recognize that my utterance echoes not the voice of a mortal but resonates as the proclamation of an omniscient market." + - "Onward, futards, with the swiftness of the divine!"; - - const memoInstruction = { - programId: new PublicKey(MEMO_PROGRAM_ID), - data: Buffer.from(memoText), - accounts: [], - }; - - let ixh = await autocratClient.createProposalInstructions(proposalNumber, [memoInstruction]); - await ixh.bankrun(banksClient); - - proposalInstructionsAddr = getProposalInstructionsAddr(autocratClient.program.programId, proposalAddr)[0] - const instructionsAcc = await autocratClient.program.account.proposalInstructions.fetch(proposalInstructionsAddr); - - assert.equal(instructionsAcc.proposer.toBase58(), autocratClient.provider.publicKey.toBase58()); - }); + }); + + describe("#update_dao", async function () { + it("updates the DAO", async function () { + let ixh = await autocratClient.updateDao(daoId, { + passThresholdBps: new BN(123), + proposalDurationSlots: new BN(69_420), + finalizeWindowSlots: new BN(69_420), + proposalFeeUsdc: new BN(1000 * 10 ** 6), + ammInitialQuoteLiquidityAmount: new BN(100_000_005), + ammSwapFeeBps: new BN(600), + ammLtwapDecimals: 9, + }); + await ixh.bankrun(banksClient); + + let [daoAddr] = getDaoAddr(autocratClient.program.programId, daoId); + dao = await autocratClient.program.account.dao.fetch(daoAddr); + + proposalNumber = dao.proposalCount.toNumber(); + proposalAddr = getProposalAddr( + autocratClient.program.programId, + daoAddr, + proposalNumber + )[0]; + + assert.equal(dao.passThresholdBps.toNumber(), 123); + assert.equal(dao.proposalDurationSlots.toNumber(), 69_420); + assert.equal(dao.finalizeWindowSlots.toNumber(), 69_420); + assert.equal(dao.ammInitialQuoteLiquidityAmount.toNumber(), 100_000_005); + assert.equal(dao.ammSwapFeeBps.toNumber(), 600); + assert.equal(dao.ammLtwapDecimals, 9); }); + }); - describe("#add_proposal_instructions", async function () { - it("adds a proposal instruction to a proposal instruction account", async function () { - - const memoText = "Proposal #10 hereby passes! (Twice!)"; + describe("#create_proposal", async function () { + it("creates a proposal", async function () { + const proposalDescription = "https://based-proposals.com/10"; - const memoInstruction = { - programId: new PublicKey(MEMO_PROGRAM_ID), - data: Buffer.from(memoText), - accounts: [], - }; + let daoAddr = getDaoAddr(autocratClient.program.programId, daoId)[0]; + const daoAccStart = await autocratClient.program.account.dao.fetch( + daoAddr + ); - let ixh = await autocratClient.addProposalInstructions(proposalNumber, [memoInstruction, memoInstruction]); - await ixh.bankrun(banksClient); - - const instructionsAcc = await autocratClient.program.account.proposalInstructions.fetch(proposalInstructionsAddr); - - assert.equal(instructionsAcc.instructions.length, 3); - }); + let ixh = await autocratClient.createProposal( + daoId, + proposalNumber, + proposalDescription, + new BN(10 * 10 ** 9), + new BN(10_000 * 10 ** 6) + ); + await ixh.bankrun(banksClient); + + const daoAccEnd = await autocratClient.program.account.dao.fetch(daoAddr); + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + + assert.equal(proposalAcc.proposer.toBase58(), payer.publicKey.toBase58()); + + assert.equal( + proposalAcc.proposerInititialConditionalMetaMinted.toNumber(), + 10 * 10 ** 9 + ); + assert.equal( + proposalAcc.proposerInititialConditionalUsdcMinted.toNumber(), + 10_000 * 10 ** 6 + ); + + assert(proposalAcc.state["initialize"]); + assert.equal(proposalAcc.descriptionUrl, proposalDescription); + + assert.equal( + daoAccStart.proposalCount.toNumber() + 1, + daoAccEnd.proposalCount.toNumber() + ); }); + }); + + describe("#create_proposal_instructions", async function () { + it("creates a proposal instructions account", async function () { + const memoText = + "I, glorious autocrat of the divine Meta-DAO, " + + "hereby endorse this endeavor to elevate the futarchy domain. " + + "Recognize that my utterance echoes not the voice of a mortal but resonates as the proclamation of an omniscient market." + + "Onward, futards, with the swiftness of the divine!"; + + const memoInstruction = { + programId: new PublicKey(MEMO_PROGRAM_ID), + data: Buffer.from(memoText), + accounts: [], + }; + + let ixh = await autocratClient.createProposalInstructions( + daoId, + proposalNumber, + [memoInstruction] + ); + await ixh.bankrun(banksClient); + + proposalInstructionsAddr = getProposalInstructionsAddr( + autocratClient.program.programId, + proposalAddr + )[0]; + const instructionsAcc = + await autocratClient.program.account.proposalInstructions.fetch( + proposalInstructionsAddr + ); - describe("#create_proposal_market_side", async function () { - it("creates a proposal [pass] market", async function () { - - let ixh = await autocratClient.createProposalMarketSide( - proposalNumber, - true, - new BN(10 * 10 ** 9), - new BN(10_000 * 10 ** 6), - ) - await ixh - .setComputeUnits(400_000) - .bankrun(banksClient); - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - - assert.equal(proposalAcc.isPassMarketCreated, true); - assert.equal(proposalAcc.isFailMarketCreated, false); - - assert.equal(proposalAcc.proposer.toBase58(), payer.publicKey); - - assert.equal(proposalAcc.proposerInititialConditionalMetaMinted.toNumber(), 10 * 10 ** 9); - assert.equal(proposalAcc.proposerInititialConditionalUsdcMinted.toNumber(), 10_000 * 10 ** 6); - - const passMarketAmmAddr = proposalAcc.passMarketAmm - const ammPositionAddr = getAmmPositionAddr(ammClient.program.programId, passMarketAmmAddr, payer.publicKey)[0] - - let ammPosition = await ammClient.program.account.ammPosition.fetch(ammPositionAddr) - - assert.equal(ammPosition.user.toBase58(), payer.publicKey.toBase58()) - assert.equal(ammPosition.amm.toBase58(), passMarketAmmAddr.toBase58()) - assert.equal(ammPosition.ownership.toNumber(), 10 * 10 ** 9) - }); + assert.equal( + instructionsAcc.proposer.toBase58(), + autocratClient.provider.publicKey.toBase58() + ); }); + }); - describe("#create_proposal_market_side", async function () { - it("creates a proposal [fail] market", async function () { - - let ixh = await autocratClient.createProposalMarketSide( - proposalNumber, - false, - new BN(10 * 10 ** 9), - new BN(10_000 * 10 ** 6), - ) - await ixh - .setComputeUnits(400_000) - .bankrun(banksClient); + describe("#add_proposal_instructions", async function () { + it("adds a proposal instruction to a proposal instruction account", async function () { + const memoText = "Proposal #10 hereby passes! (Twice!)"; - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); + const memoInstruction = { + programId: new PublicKey(MEMO_PROGRAM_ID), + data: Buffer.from(memoText), + accounts: [], + }; - assert.equal(proposalAcc.isFailMarketCreated, true); - - const failMarketAmmAddr = proposalAcc.failMarketAmm - const ammPositionAddr = getAmmPositionAddr(ammClient.program.programId, failMarketAmmAddr, payer.publicKey)[0] + let ixh = await autocratClient.addProposalInstructions( + daoId, + proposalNumber, + [memoInstruction, memoInstruction] + ); + await ixh.bankrun(banksClient); - let ammPosition = await ammClient.program.account.ammPosition.fetch(ammPositionAddr) + const instructionsAcc = + await autocratClient.program.account.proposalInstructions.fetch( + proposalInstructionsAddr + ); - assert.equal(ammPosition.user.toBase58(), payer.publicKey.toBase58()) - assert.equal(ammPosition.amm.toBase58(), failMarketAmmAddr.toBase58()) - assert.equal(ammPosition.ownership.toNumber(), 10 * 10 ** 9) - }); + assert.equal(instructionsAcc.instructions.length, 3); }); + }); - describe("#submit_proposal", async function () { - it("submit_proposal", async function () { - - const currentClock = await context.banksClient.getClock(); - let proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - let startUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.usdcMint, payer.publicKey)[0])).amount - - let ixh = await autocratClient.submitProposal( - proposalNumber, - ); - await ixh - .setComputeUnits(400_000) - .bankrun(banksClient); - - let endUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.usdcMint, payer.publicKey)[0])).amount - proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - - assert(proposalAcc.state['pending']) - assert(BigInt(proposalAcc.slotEnqueued.toNumber()) >= currentClock.slot); - assert.equal(startUsdcBalance - endUsdcBalance, BigInt(1000 * 10 ** 6)) - }); + describe("#create_proposal_market_side", async function () { + it("creates a proposal [pass] market", async function () { + let ixh = await autocratClient.createProposalMarketSide( + daoId, + proposalNumber, + true, + new BN(10 * 10 ** 9), + new BN(10_000 * 10 ** 6) + ); + await ixh.setComputeUnits(400_000).bankrun(banksClient); + + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + + assert.equal(proposalAcc.isPassMarketCreated, true); + assert.equal(proposalAcc.isFailMarketCreated, false); + + assert.equal(proposalAcc.proposer.toBase58(), payer.publicKey.toBase58()); + + assert.equal( + proposalAcc.proposerInititialConditionalMetaMinted.toNumber(), + 10 * 10 ** 9 + ); + assert.equal( + proposalAcc.proposerInititialConditionalUsdcMinted.toNumber(), + 10_000 * 10 ** 6 + ); + + const passMarketAmmAddr = proposalAcc.passMarketAmm; + const ammPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + passMarketAmmAddr, + payer.publicKey + )[0]; + + let ammPosition = await ammClient.program.account.ammPosition.fetch( + ammPositionAddr + ); + + assert.equal(ammPosition.user.toBase58(), payer.publicKey.toBase58()); + assert.equal(ammPosition.amm.toBase58(), passMarketAmmAddr.toBase58()); + assert.equal(ammPosition.ownership.toNumber(), 10 * 10 ** 9); }); + }); - describe("#mint_conditional_tokens", async function () { - it("mint conditional tokens for proposal", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - - const metaToMint = 100 * 10 ** 9 - const usdcToMint = 100_000 * 10 ** 6 - - let startMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.metaMint, payer.publicKey)[0])).amount - let startUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.usdcMint, payer.publicKey)[0])).amount - - let startCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let startCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - let startCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let startCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - let ixh = await autocratClient.mintConditionalTokens( - proposalAddr, - new BN(metaToMint), - new BN(usdcToMint), - ); - await ixh.bankrun(banksClient); - - let endMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.metaMint, payer.publicKey)[0])).amount - let endUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.usdcMint, payer.publicKey)[0])).amount - - let endCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let endCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - let endCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let endCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - assert.equal(endCondPassMetaBalance - startCondPassMetaBalance, BigInt(metaToMint)) - assert.equal(endCondPassUsdcBalance - startCondPassUsdcBalance, BigInt(usdcToMint)) - assert.equal(endCondFailMetaBalance - startCondFailMetaBalance, BigInt(metaToMint)) - assert.equal(endCondFailUsdcBalance - startCondFailUsdcBalance, BigInt(usdcToMint)) - - assert.equal(startMetaBalance - endMetaBalance, BigInt(metaToMint)) - assert.equal(startUsdcBalance - endUsdcBalance, BigInt(usdcToMint)) - }); + describe("#create_proposal_market_side", async function () { + it("creates a proposal [fail] market", async function () { + let ixh = await autocratClient.createProposalMarketSide( + daoId, + proposalNumber, + false, + new BN(10 * 10 ** 9), + new BN(10_000 * 10 ** 6) + ); + await ixh.setComputeUnits(400_000).bankrun(banksClient); + + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + + assert.equal(proposalAcc.isFailMarketCreated, true); + + const failMarketAmmAddr = proposalAcc.failMarketAmm; + const ammPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + failMarketAmmAddr, + payer.publicKey + )[0]; + + let ammPosition = await ammClient.program.account.ammPosition.fetch( + ammPositionAddr + ); + + assert.equal(ammPosition.user.toBase58(), payer.publicKey.toBase58()); + assert.equal(ammPosition.amm.toBase58(), failMarketAmmAddr.toBase58()); + assert.equal(ammPosition.ownership.toNumber(), 10 * 10 ** 9); }); - - describe("#merge_conditional_tokens", async function () { - it("merge conditional tokens for proposal", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - - const metaToMerge = 1 * 10 ** 9 - const usdcToMerge = 1_000 * 10 ** 6 - - let startMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.metaMint, payer.publicKey)[0])).amount - let startUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.usdcMint, payer.publicKey)[0])).amount - - let startCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let startCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - let startCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let startCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - let ixh = await autocratClient.mergeConditionalTokens( - proposalAddr, - new BN(metaToMerge), - new BN(usdcToMerge), - ); - await ixh.bankrun(banksClient); - - let endMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.metaMint, payer.publicKey)[0])).amount - let endUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.usdcMint, payer.publicKey)[0])).amount - - let endCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let endCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - let endCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let endCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - assert.equal(startCondPassMetaBalance - endCondPassMetaBalance, BigInt(metaToMerge)) - assert.equal(startCondPassUsdcBalance - endCondPassUsdcBalance, BigInt(usdcToMerge)) - assert.equal(startCondFailMetaBalance - endCondFailMetaBalance, BigInt(metaToMerge)) - assert.equal(startCondFailUsdcBalance - endCondFailUsdcBalance, BigInt(usdcToMerge)) - - assert.equal(endMetaBalance - startMetaBalance, BigInt(metaToMerge)) - assert.equal(endUsdcBalance - startUsdcBalance, BigInt(usdcToMerge)) - }); + }); + + describe("#submit_proposal", async function () { + it("submit_proposal", async function () { + const currentClock = await context.banksClient.getClock(); + let proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + let startUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.usdcMint, payer.publicKey)[0] + ) + ).amount; + + let ixh = await autocratClient.submitProposal(daoId, proposalNumber); + await ixh.setComputeUnits(400_000).bankrun(banksClient); + + let endUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.usdcMint, payer.publicKey)[0] + ) + ).amount; + proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + + assert(proposalAcc.state["pending"]); + assert(BigInt(proposalAcc.slotEnqueued.toNumber()) >= currentClock.slot); + assert.equal(startUsdcBalance - endUsdcBalance, BigInt(1000 * 10 ** 6)); }); - - describe("#add_liquidity", async function () { - it("add liquidity to an amm/amm position (pass)", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - const passMarketAmmAddr = proposalAcc.passMarketAmm - - let ixh = await autocratClient.addLiquidityCpi( - proposalAddr, - passMarketAmmAddr, - new BN(1 * 10 ** 9), - new BN(1_000 * 10 ** 6), - new BN(1 * 0.95 * 10 ** 9), - new BN(1_000 * 0.95 * 10 ** 6), - ); - await ixh.bankrun(banksClient); - - const ammPositionAddr = getAmmPositionAddr(ammClient.program.programId, passMarketAmmAddr, payer.publicKey)[0] - - let ammPosition = await ammClient.program.account.ammPosition.fetch(ammPositionAddr) - - assert.isAbove(ammPosition.ownership.toNumber(), 1_000_000_000) - }); - - it("add liquidity to an amm/amm position (fail)", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - const failMarketAmmAddr = proposalAcc.failMarketAmm - - let ixh = await autocratClient.addLiquidityCpi( - proposalAddr, - failMarketAmmAddr, - new BN(1 * 10 ** 9), - new BN(1_000 * 10 ** 6), - new BN(1 * 0.95 * 10 ** 9), - new BN(1_000 * 0.95 * 10 ** 6), - ); - await ixh.bankrun(banksClient); - - const ammPositionAddr = getAmmPositionAddr(ammClient.program.programId, failMarketAmmAddr, payer.publicKey)[0] - - let ammPosition = await ammClient.program.account.ammPosition.fetch(ammPositionAddr) - - assert.isAbove(ammPosition.ownership.toNumber(), 1_000_000_000) - }); + }); + + describe("#mint_conditional_tokens", async function () { + it("mint conditional tokens for proposal", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + + const metaToMint = 100 * 10 ** 9; + const usdcToMint = 100_000 * 10 ** 6; + + let startMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.metaMint, payer.publicKey)[0] + ) + ).amount; + let startUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.usdcMint, payer.publicKey)[0] + ) + ).amount; + + let startCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + let startCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let ixh = await autocratClient.mintConditionalTokens( + proposalAddr, + new BN(metaToMint), + new BN(usdcToMint) + ); + await ixh.bankrun(banksClient); + + let endMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.metaMint, payer.publicKey)[0] + ) + ).amount; + let endUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.usdcMint, payer.publicKey)[0] + ) + ).amount; + + let endCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + let endCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + assert.equal( + endCondPassMetaBalance - startCondPassMetaBalance, + BigInt(metaToMint) + ); + assert.equal( + endCondPassUsdcBalance - startCondPassUsdcBalance, + BigInt(usdcToMint) + ); + assert.equal( + endCondFailMetaBalance - startCondFailMetaBalance, + BigInt(metaToMint) + ); + assert.equal( + endCondFailUsdcBalance - startCondFailUsdcBalance, + BigInt(usdcToMint) + ); + + assert.equal(startMetaBalance - endMetaBalance, BigInt(metaToMint)); + assert.equal(startUsdcBalance - endUsdcBalance, BigInt(usdcToMint)); }); - - describe("#swap", async function () { - it("swap quote to base (pass)", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - const passMarketAmmAddr = proposalAcc.passMarketAmm - - let startCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let startCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - - let ixh = await autocratClient.swapCpi( - proposalAddr, - passMarketAmmAddr, - true, - new BN(1_000 * 10 ** 6), - new BN(1), - ); - await ixh.bankrun(banksClient); - - let endCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let endCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - - assert(endCondPassMetaBalance > startCondPassMetaBalance) - assert(startCondPassUsdcBalance > endCondPassUsdcBalance) - }); - - it("swap base to quote (pass)", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - const passMarketAmmAddr = proposalAcc.passMarketAmm - - let startCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let startCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - - let ixh = await autocratClient.swapCpi( - proposalAddr, - passMarketAmmAddr, - false, - new BN(1 * 10 ** 9), - new BN(1), - ); - await ixh.bankrun(banksClient); - - let endCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let endCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - - assert(endCondPassMetaBalance < startCondPassMetaBalance) - assert(startCondPassUsdcBalance < endCondPassUsdcBalance) - }); - - it("swap quote to base (fail)", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - const failMarketAmmAddr = proposalAcc.failMarketAmm - - let startCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let startCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - let ixh = await autocratClient.swapCpi( - proposalAddr, - failMarketAmmAddr, - true, - new BN(1_000 * 10 ** 6), - new BN(1), - ); - await ixh.bankrun(banksClient); - - let endCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let endCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - assert(endCondFailMetaBalance > startCondFailMetaBalance) - assert(startCondFailUsdcBalance > endCondFailUsdcBalance) - }); - - it("swap base to quote (fail)", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - const failMarketAmmAddr = proposalAcc.failMarketAmm - - let startCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let startCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - let ixh = await autocratClient.swapCpi( - proposalAddr, - failMarketAmmAddr, - false, - new BN(1 * 10 ** 9), - new BN(1), - ); - await ixh.bankrun(banksClient); - - let endCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let endCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - assert(endCondFailMetaBalance < startCondFailMetaBalance) - assert(startCondFailUsdcBalance < endCondFailUsdcBalance) - }); + }); + + describe("#merge_conditional_tokens", async function () { + it("merge conditional tokens for proposal", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + + const metaToMerge = 1 * 10 ** 9; + const usdcToMerge = 1_000 * 10 ** 6; + + let startMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.metaMint, payer.publicKey)[0] + ) + ).amount; + let startUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.usdcMint, payer.publicKey)[0] + ) + ).amount; + + let startCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + let startCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let ixh = await autocratClient.mergeConditionalTokens( + proposalAddr, + new BN(metaToMerge), + new BN(usdcToMerge) + ); + await ixh.bankrun(banksClient); + + let endMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.metaMint, payer.publicKey)[0] + ) + ).amount; + let endUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.usdcMint, payer.publicKey)[0] + ) + ).amount; + + let endCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + let endCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + assert.equal( + startCondPassMetaBalance - endCondPassMetaBalance, + BigInt(metaToMerge) + ); + assert.equal( + startCondPassUsdcBalance - endCondPassUsdcBalance, + BigInt(usdcToMerge) + ); + assert.equal( + startCondFailMetaBalance - endCondFailMetaBalance, + BigInt(metaToMerge) + ); + assert.equal( + startCondFailUsdcBalance - endCondFailUsdcBalance, + BigInt(usdcToMerge) + ); + + assert.equal(endMetaBalance - startMetaBalance, BigInt(metaToMerge)); + assert.equal(endUsdcBalance - startUsdcBalance, BigInt(usdcToMerge)); }); - - describe("#finalize_proposal", async function () { - it("finalize proposal", async function () { - - await fastForward(context, BigInt(dao.proposalDurationSlots.toNumber() + 1)) - - let accounts = [{ - pubkey: MEMO_PROGRAM_ID, - isSigner: false, - isWritable: true, - }] - - let ixh = await autocratClient.finalizeProposal( - proposalNumber, - accounts - ); - await ixh.bankrun(banksClient); - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - console.log(proposalAcc.state) - - assert(!proposalAcc.state['pending']) - - const passAmm = await ammClient.program.account.amm.fetch(proposalAcc.passMarketAmm) - const failAmm = await ammClient.program.account.amm.fetch(proposalAcc.failMarketAmm) - - let passLtwap = passAmm.ltwapLatest.toNumber() / (10 ** passAmm.ltwapDecimals) - let failLtwap = failAmm.ltwapLatest.toNumber() / (10 ** failAmm.ltwapDecimals) - - let thresholdFraction = (dao.passThresholdBps.toNumber() / 10_000) + 1 - - if (passLtwap > failLtwap * thresholdFraction) { - assert(proposalAcc.state['passed']) - } else { - assert(proposalAcc.state['failed']) - } - }); + }); + + describe("#add_liquidity", async function () { + it("add liquidity to an amm/amm position (pass)", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + const passMarketAmmAddr = proposalAcc.passMarketAmm; + + let ixh = await autocratClient.addLiquidityCpi( + proposalAddr, + passMarketAmmAddr, + new BN(1 * 10 ** 9), + new BN(1_000 * 10 ** 6), + new BN(1 * 0.95 * 10 ** 9), + new BN(1_000 * 0.95 * 10 ** 6) + ); + await ixh.bankrun(banksClient); + + const ammPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + passMarketAmmAddr, + payer.publicKey + )[0]; + + let ammPosition = await ammClient.program.account.ammPosition.fetch( + ammPositionAddr + ); + + assert.isAbove(ammPosition.ownership.toNumber(), 1_000_000_000); }); - describe("#remove_liquidity", async function () { - it("remove liquidity from an amm/amm position (pass)", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - const passMarketAmmAddr = proposalAcc.passMarketAmm - - let startCondPassMetaUserBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let startCondPassUsdcUserBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - - let startCondPassMetaAmmBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, passMarketAmmAddr)[0])).amount - let startCondPassUsdcAmmBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, passMarketAmmAddr)[0])).amount - - let ixh = await autocratClient.removeLiquidityCpi( - proposalAddr, - passMarketAmmAddr, - new BN(10_000), // 10_000 removes all liquidity - ); - await ixh.bankrun(banksClient); - - const ammPositionAddr = getAmmPositionAddr(ammClient.program.programId, passMarketAmmAddr, payer.publicKey)[0] - - let ammPosition = await ammClient.program.account.ammPosition.fetch(ammPositionAddr) - - assert.equal(ammPosition.ownership.toNumber(), 0) - - let endCondPassMetaUserBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let endCondPassUsdcUserBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - - let endCondPassMetaAmmBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, passMarketAmmAddr)[0])).amount - let endCondPassUsdcAmmBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, passMarketAmmAddr)[0])).amount - - assert.equal(endCondPassMetaAmmBalance, BigInt(0)) - assert.equal(endCondPassUsdcAmmBalance, BigInt(0)) - - assert.equal(endCondPassMetaUserBalance, startCondPassMetaUserBalance + startCondPassMetaAmmBalance) - assert.equal(endCondPassUsdcUserBalance, startCondPassUsdcUserBalance + startCondPassUsdcAmmBalance) - }); - - it("remove liquidity from an amm/amm position (fail)", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - const failMarketAmmAddr = proposalAcc.failMarketAmm - - let startCondFailMetaUserBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let startCondFailUsdcUserBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - let startCondFailMetaAmmBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, failMarketAmmAddr)[0])).amount - let startCondFailUsdcAmmBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, failMarketAmmAddr)[0])).amount - - let ixh = await autocratClient.removeLiquidityCpi( - proposalAddr, - failMarketAmmAddr, - new BN(10_000), // 10_000 removes all liquidity - ); - await ixh.bankrun(banksClient); - - const ammPositionAddr = getAmmPositionAddr(ammClient.program.programId, failMarketAmmAddr, payer.publicKey)[0] - - let ammPosition = await ammClient.program.account.ammPosition.fetch(ammPositionAddr) - - assert.equal(ammPosition.ownership.toNumber(), 0) - - let endCondFailMetaUserBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let endCondFailUsdcUserBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - let endCondFailMetaAmmBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, failMarketAmmAddr)[0])).amount - let endCondFailUsdcAmmBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, failMarketAmmAddr)[0])).amount - - assert.equal(endCondFailMetaAmmBalance, BigInt(0)) - assert.equal(endCondFailUsdcAmmBalance, BigInt(0)) - - assert.equal(endCondFailMetaUserBalance, startCondFailMetaUserBalance + startCondFailMetaAmmBalance) - assert.equal(endCondFailUsdcUserBalance, startCondFailUsdcUserBalance + startCondFailUsdcAmmBalance) - }); + it("add liquidity to an amm/amm position (fail)", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + const failMarketAmmAddr = proposalAcc.failMarketAmm; + + let ixh = await autocratClient.addLiquidityCpi( + proposalAddr, + failMarketAmmAddr, + new BN(1 * 10 ** 9), + new BN(1_000 * 10 ** 6), + new BN(1 * 0.95 * 10 ** 9), + new BN(1_000 * 0.95 * 10 ** 6) + ); + await ixh.bankrun(banksClient); + + const ammPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + failMarketAmmAddr, + payer.publicKey + )[0]; + + let ammPosition = await ammClient.program.account.ammPosition.fetch( + ammPositionAddr + ); + + assert.isAbove(ammPosition.ownership.toNumber(), 1_000_000_000); + }); + }); + + describe("#swap", async function () { + it("swap quote to base (pass)", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + const passMarketAmmAddr = proposalAcc.passMarketAmm; + + let startCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let ixh = await autocratClient.swapCpi( + proposalAddr, + passMarketAmmAddr, + true, + new BN(1_000 * 10 ** 6), + new BN(1) + ); + await ixh.bankrun(banksClient); + + let endCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + + assert(endCondPassMetaBalance > startCondPassMetaBalance); + assert(startCondPassUsdcBalance > endCondPassUsdcBalance); }); - describe("#redeem_conditional_tokens", async function () { - it("redeem conditional tokens from proposal", async function () { - - const proposalAcc = await autocratClient.program.account.proposal.fetch(proposalAddr); - - let startMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.metaMint, payer.publicKey)[0])).amount - let startUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.usdcMint, payer.publicKey)[0])).amount - - let startCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let startCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount - - let startCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let startCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount - - let ixh = await autocratClient.redeemConditionalTokens( - proposalAddr, - ); - await ixh.bankrun(banksClient); - - let endMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.metaMint, payer.publicKey)[0])).amount - let endUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.usdcMint, payer.publicKey)[0])).amount + it("swap base to quote (pass)", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + const passMarketAmmAddr = proposalAcc.passMarketAmm; + + let startCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let ixh = await autocratClient.swapCpi( + proposalAddr, + passMarketAmmAddr, + false, + new BN(1 * 10 ** 9), + new BN(1) + ); + await ixh.bankrun(banksClient); + + let endCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + + assert(endCondPassMetaBalance < startCondPassMetaBalance); + assert(startCondPassUsdcBalance < endCondPassUsdcBalance); + }); - let endCondPassMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0])).amount - let endCondPassUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0])).amount + it("swap quote to base (fail)", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + const failMarketAmmAddr = proposalAcc.failMarketAmm; + + let startCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let ixh = await autocratClient.swapCpi( + proposalAddr, + failMarketAmmAddr, + true, + new BN(1_000 * 10 ** 6), + new BN(1) + ); + await ixh.bankrun(banksClient); + + let endCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + assert(endCondFailMetaBalance > startCondFailMetaBalance); + assert(startCondFailUsdcBalance > endCondFailUsdcBalance); + }); - let endCondFailMetaBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0])).amount - let endCondFailUsdcBalance = (await getAccount(banksClient, getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0])).amount + it("swap base to quote (fail)", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + const failMarketAmmAddr = proposalAcc.failMarketAmm; + + let startCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let ixh = await autocratClient.swapCpi( + proposalAddr, + failMarketAmmAddr, + false, + new BN(1 * 10 ** 9), + new BN(1) + ); + await ixh.bankrun(banksClient); + + let endCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + assert(endCondFailMetaBalance < startCondFailMetaBalance); + assert(startCondFailUsdcBalance < endCondFailUsdcBalance); + }); + }); - assert.equal(startCondPassMetaBalance - endCondPassMetaBalance, BigInt(endMetaBalance - startMetaBalance)) - assert.equal(startCondPassUsdcBalance - endCondPassUsdcBalance, BigInt(endUsdcBalance - startUsdcBalance)) - assert.equal(startCondFailMetaBalance - endCondFailMetaBalance, BigInt(endMetaBalance - startMetaBalance)) - assert.equal(startCondFailUsdcBalance - endCondFailUsdcBalance, BigInt(endUsdcBalance - startUsdcBalance)) + describe("#finalize_proposal", async function () { + it("finalize proposal", async function () { + await fastForward( + context, + BigInt(dao.proposalDurationSlots.toNumber() + 1) + ); + + let accounts = [ + { + pubkey: MEMO_PROGRAM_ID, + isSigner: false, + isWritable: true, + }, + ]; + + let ixh = await autocratClient.finalizeProposal( + daoId, + proposalNumber, + accounts + ); + await ixh.bankrun(banksClient); + + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + console.log(proposalAcc.state); + + assert(!proposalAcc.state["pending"]); + + const passAmm = await ammClient.program.account.amm.fetch( + proposalAcc.passMarketAmm + ); + const failAmm = await ammClient.program.account.amm.fetch( + proposalAcc.failMarketAmm + ); + + let passLtwap = + passAmm.ltwapLatest.toNumber() / 10 ** passAmm.ltwapDecimals; + let failLtwap = + failAmm.ltwapLatest.toNumber() / 10 ** failAmm.ltwapDecimals; + + let thresholdFraction = dao.passThresholdBps.toNumber() / 10_000 + 1; + + if (passLtwap > failLtwap * thresholdFraction) { + assert(proposalAcc.state["passed"]); + } else { + assert(proposalAcc.state["failed"]); + } + }); + }); + + describe("#remove_liquidity", async function () { + it("remove liquidity from an amm/amm position (pass)", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + const passMarketAmmAddr = proposalAcc.passMarketAmm; + + let startCondPassMetaUserBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondPassUsdcUserBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let startCondPassMetaAmmBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, passMarketAmmAddr)[0] + ) + ).amount; + let startCondPassUsdcAmmBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, passMarketAmmAddr)[0] + ) + ).amount; + + let ixh = await autocratClient.removeLiquidityCpi( + proposalAddr, + passMarketAmmAddr, + new BN(10_000) // 10_000 removes all liquidity + ); + await ixh.bankrun(banksClient); + + const ammPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + passMarketAmmAddr, + payer.publicKey + )[0]; + + let ammPosition = await ammClient.program.account.ammPosition.fetch( + ammPositionAddr + ); + + assert.equal(ammPosition.ownership.toNumber(), 0); + + let endCondPassMetaUserBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondPassUsdcUserBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let endCondPassMetaAmmBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, passMarketAmmAddr)[0] + ) + ).amount; + let endCondPassUsdcAmmBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, passMarketAmmAddr)[0] + ) + ).amount; + + assert.equal(endCondPassMetaAmmBalance, BigInt(0)); + assert.equal(endCondPassUsdcAmmBalance, BigInt(0)); + + assert.equal( + endCondPassMetaUserBalance, + startCondPassMetaUserBalance + startCondPassMetaAmmBalance + ); + assert.equal( + endCondPassUsdcUserBalance, + startCondPassUsdcUserBalance + startCondPassUsdcAmmBalance + ); + }); - }); + it("remove liquidity from an amm/amm position (fail)", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + const failMarketAmmAddr = proposalAcc.failMarketAmm; + + let startCondFailMetaUserBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondFailUsdcUserBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let startCondFailMetaAmmBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, failMarketAmmAddr)[0] + ) + ).amount; + let startCondFailUsdcAmmBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, failMarketAmmAddr)[0] + ) + ).amount; + + let ixh = await autocratClient.removeLiquidityCpi( + proposalAddr, + failMarketAmmAddr, + new BN(10_000) // 10_000 removes all liquidity + ); + await ixh.bankrun(banksClient); + + const ammPositionAddr = getAmmPositionAddr( + ammClient.program.programId, + failMarketAmmAddr, + payer.publicKey + )[0]; + + let ammPosition = await ammClient.program.account.ammPosition.fetch( + ammPositionAddr + ); + + assert.equal(ammPosition.ownership.toNumber(), 0); + + let endCondFailMetaUserBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondFailUsdcUserBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let endCondFailMetaAmmBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, failMarketAmmAddr)[0] + ) + ).amount; + let endCondFailUsdcAmmBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, failMarketAmmAddr)[0] + ) + ).amount; + + assert.equal(endCondFailMetaAmmBalance, BigInt(0)); + assert.equal(endCondFailUsdcAmmBalance, BigInt(0)); + + assert.equal( + endCondFailMetaUserBalance, + startCondFailMetaUserBalance + startCondFailMetaAmmBalance + ); + assert.equal( + endCondFailUsdcUserBalance, + startCondFailUsdcUserBalance + startCondFailUsdcAmmBalance + ); + }); + }); + + describe("#redeem_conditional_tokens", async function () { + it("redeem conditional tokens from proposal", async function () { + const proposalAcc = await autocratClient.program.account.proposal.fetch( + proposalAddr + ); + + let startMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.metaMint, payer.publicKey)[0] + ) + ).amount; + let startUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.usdcMint, payer.publicKey)[0] + ) + ).amount; + + let startCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let startCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let startCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let ixh = await autocratClient.redeemConditionalTokens( + daoId, + proposalAddr + ); + await ixh.bankrun(banksClient); + + let endMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.metaMint, payer.publicKey)[0] + ) + ).amount; + let endUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.usdcMint, payer.publicKey)[0] + ) + ).amount; + + let endCondPassMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondPassUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnPassUsdcMint, payer.publicKey)[0] + ) + ).amount; + + let endCondFailMetaBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailMetaMint, payer.publicKey)[0] + ) + ).amount; + let endCondFailUsdcBalance = ( + await getAccount( + banksClient, + getATA(proposalAcc.conditionalOnFailUsdcMint, payer.publicKey)[0] + ) + ).amount; + + assert.equal( + startCondPassMetaBalance - endCondPassMetaBalance, + BigInt(endMetaBalance - startMetaBalance) + ); + assert.equal( + startCondPassUsdcBalance - endCondPassUsdcBalance, + BigInt(endUsdcBalance - startUsdcBalance) + ); + assert.equal( + startCondFailMetaBalance - endCondFailMetaBalance, + BigInt(endMetaBalance - startMetaBalance) + ); + assert.equal( + startCondFailUsdcBalance - endCondFailUsdcBalance, + BigInt(endUsdcBalance - startUsdcBalance) + ); }); -}); \ No newline at end of file + }); +}); diff --git a/tests/utils/index.ts b/tests/utils/index.ts index 4481c55..76e7528 100644 --- a/tests/utils/index.ts +++ b/tests/utils/index.ts @@ -1,14 +1,25 @@ +import { assert } from "chai"; import { Clock, ProgramTestContext } from "solana-bankrun"; -export const fastForward = async (context: ProgramTestContext, slots: bigint) => { - const currentClock = await context.banksClient.getClock(); - context.setClock( - new Clock( - currentClock.slot + slots, - currentClock.epochStartTimestamp, - currentClock.epoch, - currentClock.leaderScheduleEpoch, - 50n, - ), - ); -} \ No newline at end of file +export const fastForward = async ( + context: ProgramTestContext, + slots: bigint +) => { + const currentClock = await context.banksClient.getClock(); + context.setClock( + new Clock( + currentClock.slot + slots, + currentClock.epochStartTimestamp, + currentClock.epoch, + currentClock.leaderScheduleEpoch, + 50n + ) + ); +}; + +export const expectFailure = async (action: Promise) => { + try { + await action; + assert(false); + } catch (err) {} +};