From 65b549cd55b4d8093e2185f3667cfda84a251052 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sun, 18 Aug 2024 00:00:00 +0000 Subject: [PATCH 01/52] Add `Question` and `NewConditionalVault` structs --- Cargo.lock | 2 +- programs/conditional_vault/Cargo.toml | 2 +- programs/conditional_vault/src/lib.rs | 2 +- .../src/state/conditional_vault.rs | 40 ++++++++ sdk/src/types/conditional_vault.ts | 92 ++++++++++++++++++- 5 files changed, 133 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a6c8f2ae..1d6e03fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,7 +687,7 @@ dependencies = [ [[package]] name = "conditional_vault" -version = "0.3.0" +version = "0.4.0" dependencies = [ "anchor-lang", "anchor-spl", diff --git a/programs/conditional_vault/Cargo.toml b/programs/conditional_vault/Cargo.toml index 95da9691..4e66ebe1 100644 --- a/programs/conditional_vault/Cargo.toml +++ b/programs/conditional_vault/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "conditional_vault" -version = "0.3.0" +version = "0.4.0" description = "SVM-based program for minting conditional tokens" edition = "2021" diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index ed86fcac..d0b92d50 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -27,7 +27,7 @@ security_txt! { contacts: "email:metaproph3t@protonmail.com", policy: "The market will decide whether we pay a bug bounty.", source_code: "https://github.com/metaDAOproject/futarchy", - source_release: "v0.3", + source_release: "v0.4", auditors: "Neodyme", acknowledgements: "DCF = (CF1 / (1 + r)^1) + (CF2 / (1 + r)^2) + ... (CFn / (1 + r)^n)" } diff --git a/programs/conditional_vault/src/state/conditional_vault.rs b/programs/conditional_vault/src/state/conditional_vault.rs index 9637885c..afa4371c 100644 --- a/programs/conditional_vault/src/state/conditional_vault.rs +++ b/programs/conditional_vault/src/state/conditional_vault.rs @@ -7,6 +7,46 @@ pub enum VaultStatus { Reverted, } +/// Questions represent statements about future events. +/// +/// These statements include: +/// - "Will this proposal pass?" +/// - "Who, if anyone, will be hired?" +/// - "How effective will the grant committee deem this grant?" +/// +/// Questions have 2 or more conditions. For example, these conditions could be +/// "this proposal passes" and "this proposal fails" or "the committee deems this +/// grant effective" and "the committee deems this grant ineffective." +/// +/// Conditions resolve to a number between 0 and 1. Binary conditions like "will +/// this proposal pass" resolve to exactly 0 or 1. You can also have scalar +/// conditions. For example, the condition "the grant committee deems this grant +/// effective" could resolve to 0.5 if the committee finds the grant partially +/// effective. Once resolved, the sum of all condition resolutions is exactly 1. +#[account] +pub struct Question { + pub is_resolved: bool, + pub oracle: Pubkey, + pub payout_numerators: Vec, + pub payout_denominator: u32, +} + +impl Question { + pub fn num_conditions(&self) -> usize { + self.payout_numerators.len() + } +} + +#[account] +pub struct NewConditionalVault { + pub question: Pubkey, + pub underlying_token_mint: Pubkey, + pub underlying_token_account: Pubkey, + pub conditional_token_mints: Vec, + pub pda_bump: u8, + pub decimals: u8, +} + #[account] pub struct ConditionalVault { pub status: VaultStatus, diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index 0af7cb8d..c4c90e97 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -1,5 +1,5 @@ export type ConditionalVault = { - version: "0.3.0"; + version: "0.4.0"; name: "conditional_vault"; instructions: [ { @@ -316,6 +316,50 @@ export type ConditionalVault = { } ]; accounts: [ + { + name: "question"; + docs: [ + "Questions represent statements about future events.", + "", + "These statements include:", + '- "Will this proposal pass?"', + '- "Who, if anyone, will be hired?"', + '- "How effective will the grant committee deem this grant?"', + "", + "Questions have 2 or more conditions. For example, these conditions could be", + '"this proposal passes" and "this proposal fails" or "the committee deems this', + 'grant effective" and "the committee deems this grant ineffective."', + "", + 'Conditions resolve to a number between 0 and 1. Binary conditions like "will', + 'this proposal pass" resolve to exactly 0 or 1. You can also have scalar', + 'conditions. For example, the condition "the grant committee deems this grant', + 'effective" could resolve to 0.5 if the committee finds the grant partially', + "effective. Once resolved, the sum of all condition resolutions is exactly 1." + ]; + type: { + kind: "struct"; + fields: [ + { + name: "isResolved"; + type: "bool"; + }, + { + name: "oracle"; + type: "publicKey"; + }, + { + name: "payoutNumerators"; + type: { + vec: "u32"; + }; + }, + { + name: "payoutDenominator"; + type: "u32"; + } + ]; + }; + }, { name: "conditionalVault"; type: { @@ -447,7 +491,7 @@ export type ConditionalVault = { }; export const IDL: ConditionalVault = { - version: "0.3.0", + version: "0.4.0", name: "conditional_vault", instructions: [ { @@ -764,6 +808,50 @@ export const IDL: ConditionalVault = { }, ], accounts: [ + { + name: "question", + docs: [ + "Questions represent statements about future events.", + "", + "These statements include:", + '- "Will this proposal pass?"', + '- "Who, if anyone, will be hired?"', + '- "How effective will the grant committee deem this grant?"', + "", + "Questions have 2 or more conditions. For example, these conditions could be", + '"this proposal passes" and "this proposal fails" or "the committee deems this', + 'grant effective" and "the committee deems this grant ineffective."', + "", + 'Conditions resolve to a number between 0 and 1. Binary conditions like "will', + 'this proposal pass" resolve to exactly 0 or 1. You can also have scalar', + 'conditions. For example, the condition "the grant committee deems this grant', + 'effective" could resolve to 0.5 if the committee finds the grant partially', + "effective. Once resolved, the sum of all condition resolutions is exactly 1.", + ], + type: { + kind: "struct", + fields: [ + { + name: "isResolved", + type: "bool", + }, + { + name: "oracle", + type: "publicKey", + }, + { + name: "payoutNumerators", + type: { + vec: "u32", + }, + }, + { + name: "payoutDenominator", + type: "u32", + }, + ], + }, + }, { name: "conditionalVault", type: { From 6c66cec2a60efbd45888e477a06d66ad035def60 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Mon, 19 Aug 2024 00:00:00 +0000 Subject: [PATCH 02/52] Get the vault test working --- Anchor.toml | 23 ++++++++++++----------- test.sh => run.sh | 15 ++++++++++----- 2 files changed, 22 insertions(+), 16 deletions(-) rename test.sh => run.sh (86%) diff --git a/Anchor.toml b/Anchor.toml index 33b37858..81054dec 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -1,3 +1,15 @@ +[scripts] +test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/*.ts" +propose = "yarn run ts-node scripts/initializeProposal.ts" +initialize-dao = "yarn run ts-node scripts/initializeDao.ts" +finalize = "yarn run ts-node scripts/finalizeProposal.ts" +metadata = "yarn run ts-node scripts/uploadMetadata.ts" +send-tokens = "yarn run ts-node scripts/sendTokens.ts" +crank = "yarn run ts-node scripts/crankTwap.ts" +attach-metadata = "yarn run ts-node scripts/attachMetadata.ts" +reclaim-tokens = "yarn run ts-node scripts/reclaimTokens.ts" +merge-tokens = "yarn run ts-node scripts/mergeTokens.ts" + [toolchain] [features] @@ -18,17 +30,6 @@ url = "https://api.apr.dev" cluster = "Localnet" wallet = "~/.config/solana/id.json" -[scripts] -propose = "yarn run ts-node scripts/initializeProposal.ts" -initialize-dao = "yarn run ts-node scripts/initializeDao.ts" -finalize = "yarn run ts-node scripts/finalizeProposal.ts" -metadata = "yarn run ts-node scripts/uploadMetadata.ts" -send-tokens = "yarn run ts-node scripts/sendTokens.ts" -crank = "yarn run ts-node scripts/crankTwap.ts" -attach-metadata = "yarn run ts-node scripts/attachMetadata.ts" -reclaim-tokens = "yarn run ts-node scripts/reclaimTokens.ts" -merge-tokens = "yarn run ts-node scripts/mergeTokens.ts" -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/*.ts" [test] startup_wait = 5000 diff --git a/test.sh b/run.sh similarity index 86% rename from test.sh rename to run.sh index c7188e9f..b4f72854 100755 --- a/test.sh +++ b/run.sh @@ -4,6 +4,14 @@ test() { find programs tests sdk | entr -sc '(cd sdk && yarn build) && RUST_LOG= anchor test' } +test_vault() { + # anchor doesn't let you past test files, so we do this weird thing where we + # modify the Anchor.toml and then put it back + sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/conditionalVault.ts"/' Anchor.toml + sleep 1 && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/*.ts"/' Anchor.toml & + find programs tests | entr -csr 'anchor build -p conditional_vault && RUST_LOG= anchor test --skip-build' +} + test_no_build() { find programs tests sdk | entr -sc '(cd sdk && yarn build) && RUST_LOG= anchor test --skip-build' } @@ -46,10 +54,6 @@ test_amm_logs() { find programs tests | entr -csr 'anchor build -p amm && yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/amm.ts' } -# requires solana-test-validator to be running in the background -bankrun_vault() { - (find programs && find tests) | entr -csr 'anchor build -p conditional_vault && RUST_LOG= yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/conditionalVault.ts' -} bankrun_migrator() { (find programs && find tests) | entr -csr 'anchor build -p autocrat_migrator && RUST_LOG= yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/migrator.ts' @@ -69,6 +73,7 @@ bankrun_logs() { case "$1" in test) test ;; + vault) test_vault ;; test_no_build) test_no_build ;; build_verifiable) build_verifiable "$2" ;; deploy) deploy "$2" "$3" ;; @@ -83,4 +88,4 @@ case "$1" in bankrun_vault_logs) bankrun_vault_logs ;; bankrun_logs) bankrun_logs ;; *) echo "Unknown command: $1" ;; -esac \ No newline at end of file +esac From d43b26d21f3ea5076cd8909d54907a8157e3af60 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Mon, 19 Aug 2024 00:00:00 +0000 Subject: [PATCH 03/52] Add `initialize_question` --- .../src/instructions/initialize_question.rs | 51 ++ .../conditional_vault/src/instructions/mod.rs | 3 +- programs/conditional_vault/src/lib.rs | 7 + .../src/state/conditional_vault.rs | 3 +- run.sh | 7 +- scripts/mergeTokens.ts | 37 +- sdk/package.json | 11 +- sdk/src/ConditionalVaultClient.ts | 41 +- sdk/src/index.ts | 1 + sdk/src/types/conditional_vault.ts | 188 ++++++- sdk/src/utils/pda.ts | 17 + sdk/yarn.lock | 5 + tests/autocrat.ts | 5 +- tests/conditionalVault.ts | 29 +- tests/timelock.ts | 229 ++++---- tests/utils/programFacade.ts | 527 ------------------ tests/utils/testUtils.ts | 299 ---------- 17 files changed, 501 insertions(+), 959 deletions(-) create mode 100644 programs/conditional_vault/src/instructions/initialize_question.rs delete mode 100644 tests/utils/programFacade.ts delete mode 100644 tests/utils/testUtils.ts diff --git a/programs/conditional_vault/src/instructions/initialize_question.rs b/programs/conditional_vault/src/instructions/initialize_question.rs new file mode 100644 index 00000000..1ef12b46 --- /dev/null +++ b/programs/conditional_vault/src/instructions/initialize_question.rs @@ -0,0 +1,51 @@ +use super::*; + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct InitializeQuestionArgs { + pub question_id: [u8; 32], + pub oracle: Pubkey, + pub num_conditions: u8, +} + +#[derive(Accounts)] +#[instruction(args: InitializeQuestionArgs)] +pub struct InitializeQuestion<'info> { + #[account( + init, + payer = payer, + space = 8 + 32 + 32 + 1 + 4 + (args.num_conditions as usize * 4) + 4, + seeds = [ + b"question", + args.question_id.as_ref(), + args.oracle.key().as_ref(), + &[args.num_conditions], + ], + bump + )] + pub question: Box>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +impl InitializeQuestion<'_> { + pub fn handle(ctx: Context, args: InitializeQuestionArgs) -> Result<()> { + let question = &mut ctx.accounts.question; + + let InitializeQuestionArgs { + question_id, + oracle, + num_conditions, + } = args; + + question.set_inner(Question { + question_id, + oracle, + is_resolved: false, + payout_numerators: vec![0; num_conditions as usize], + payout_denominator: 0, + }); + + Ok(()) + } +} diff --git a/programs/conditional_vault/src/instructions/mod.rs b/programs/conditional_vault/src/instructions/mod.rs index 13626b8c..1074c6f8 100644 --- a/programs/conditional_vault/src/instructions/mod.rs +++ b/programs/conditional_vault/src/instructions/mod.rs @@ -2,14 +2,15 @@ use super::*; pub mod add_metadata_to_conditional_tokens; pub mod common; +pub mod initialize_question; pub mod initialize_conditional_vault; pub mod merge_conditional_tokens; pub mod mint_conditional_tokens; pub mod redeem_conditional_tokens_for_underlying_tokens; pub mod settle_conditional_vault; +pub use initialize_question::*; pub use add_metadata_to_conditional_tokens::*; pub use common::*; pub use initialize_conditional_vault::*; - pub use settle_conditional_vault::*; diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index d0b92d50..71c3602d 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -38,6 +38,13 @@ declare_id!("VAU1T7S5UuEHmMvXtXMVmpEoQtZ2ya7eRb7gcN47wDp"); pub mod conditional_vault { use super::*; + pub fn initialize_question( + ctx: Context, + args: InitializeQuestionArgs, + ) -> Result<()> { + InitializeQuestion::handle(ctx, args) + } + pub fn initialize_conditional_vault( ctx: Context, args: InitializeConditionalVaultArgs, diff --git a/programs/conditional_vault/src/state/conditional_vault.rs b/programs/conditional_vault/src/state/conditional_vault.rs index afa4371c..0d6f3326 100644 --- a/programs/conditional_vault/src/state/conditional_vault.rs +++ b/programs/conditional_vault/src/state/conditional_vault.rs @@ -25,8 +25,9 @@ pub enum VaultStatus { /// effective. Once resolved, the sum of all condition resolutions is exactly 1. #[account] pub struct Question { - pub is_resolved: bool, + pub question_id: [u8; 32], pub oracle: Pubkey, + pub is_resolved: bool, pub payout_numerators: Vec, pub payout_denominator: u32, } diff --git a/run.sh b/run.sh index b4f72854..e98bb65b 100755 --- a/run.sh +++ b/run.sh @@ -7,9 +7,10 @@ test() { test_vault() { # anchor doesn't let you past test files, so we do this weird thing where we # modify the Anchor.toml and then put it back - sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/conditionalVault.ts"/' Anchor.toml - sleep 1 && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/*.ts"/' Anchor.toml & - find programs tests | entr -csr 'anchor build -p conditional_vault && RUST_LOG= anchor test --skip-build' + #sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/conditionalVault.ts"/' Anchor.toml + #sleep 10 && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/*.ts"/' Anchor.toml & + find programs tests sdk | entr -sc \ + "anchor build -p conditional_vault && (cd sdk && yarn build) && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/conditionalVault.ts\"/' Anchor.toml && (sleep 3 && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/*.ts\"/' Anchor.toml) & RUST_LOG= anchor test --skip-build" } test_no_build() { diff --git a/scripts/mergeTokens.ts b/scripts/mergeTokens.ts index 1a74347e..8d257e8c 100644 --- a/scripts/mergeTokens.ts +++ b/scripts/mergeTokens.ts @@ -55,14 +55,39 @@ async function main() { DAO ); - const basePassBalance = (await token.getOrCreateAssociatedTokenAccount(provider.connection, payer, passBaseMint, payer.publicKey)).amount; - const quotePassBalance = (await token.getOrCreateAssociatedTokenAccount(provider.connection, payer, passQuoteMint, payer.publicKey)).amount; + const basePassBalance = ( + await token.getOrCreateAssociatedTokenAccount( + provider.connection, + payer, + passBaseMint, + payer.publicKey + ) + ).amount; + const quotePassBalance = ( + await token.getOrCreateAssociatedTokenAccount( + provider.connection, + payer, + passQuoteMint, + payer.publicKey + ) + ).amount; - await vaultClient.mergeConditionalTokensIx(baseVault, storedDao.tokenMint, new BN(basePassBalance.toString())) + await vaultClient + .mergeConditionalTokensIx( + baseVault, + storedDao.tokenMint, + new BN(basePassBalance.toString()) + ) .preInstructions([ - ComputeBudgetProgram.setComputeUnitLimit({ units: 150_000 }), - ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 100 }), - await vaultClient.mergeConditionalTokensIx(quoteVault, storedDao.usdcMint, new BN(quotePassBalance.toString())).instruction(), + ComputeBudgetProgram.setComputeUnitLimit({ units: 150_000 }), + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 100 }), + await vaultClient + .mergeConditionalTokensIx( + quoteVault, + storedDao.usdcMint, + new BN(quotePassBalance.toString()) + ) + .instruction(), ]) .rpc(); } diff --git a/sdk/package.json b/sdk/package.json index bcb7122f..75a94bae 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "@coral-xyz/anchor": "^0.29.0", + "@noble/hashes": "^1.4.0", "@solana/spl-token": "^0.3.7", "@solana/web3.js": "^1.74.0", "bn.js": "^5.2.1", @@ -20,16 +21,16 @@ "esbuild": "^0.17.15" }, "devDependencies": { - "chai": "^4.3.4", - "mocha": "^9.0.3", - "ts-mocha": "^10.0.0", "@types/bn.js": "^5.1.0", "@types/chai": "^4.3.0", "@types/mocha": "^9.0.0", + "chai": "^4.3.4", + "mocha": "^9.0.3", + "prettier": "^2.6.2", "solana-bankrun": "^0.2.0", "spl-token-bankrun": "0.2.3", - "typescript": "^4.3.5", - "prettier": "^2.6.2" + "ts-mocha": "^10.0.0", + "typescript": "^4.3.5" }, "packageManager": "yarn@3.3.1" } diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index e9feb0c5..49811eb2 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -1,4 +1,4 @@ -import { AnchorProvider, Program } from "@coral-xyz/anchor"; +import { AnchorProvider, Program, utils } from "@coral-xyz/anchor"; import { AddressLookupTableAccount, Keypair, PublicKey } from "@solana/web3.js"; import { @@ -12,6 +12,7 @@ import { MPL_TOKEN_METADATA_PROGRAM_ID, } from "./constants"; import { + getQuestionAddr, getMetadataAddr, getVaultAddr, getVaultFinalizeMintAddr, @@ -32,20 +33,14 @@ export type CreateVaultClientParams = { export class ConditionalVaultClient { public readonly provider: AnchorProvider; public readonly vaultProgram: Program; - public readonly luts: AddressLookupTableAccount[]; - constructor( - provider: AnchorProvider, - conditionalVaultProgramId: PublicKey, - luts: AddressLookupTableAccount[] - ) { + constructor(provider: AnchorProvider, conditionalVaultProgramId: PublicKey) { this.provider = provider; this.vaultProgram = new Program( ConditionalVaultIDL, conditionalVaultProgramId, provider ); - this.luts = luts; } public static createClient( @@ -53,12 +48,9 @@ export class ConditionalVaultClient { ): ConditionalVaultClient { let { provider, conditionalVaultProgramId } = createVaultClientParams; - const luts: AddressLookupTableAccount[] = []; - return new ConditionalVaultClient( provider, - conditionalVaultProgramId || CONDITIONAL_VAULT_PROGRAM_ID, - luts + conditionalVaultProgramId || CONDITIONAL_VAULT_PROGRAM_ID ); } @@ -66,6 +58,31 @@ export class ConditionalVaultClient { return this.vaultProgram.account.conditionalVault.fetch(vault); } + initializeQuestionIx( + questionId: number[], + oracle: PublicKey, + numConditions: number + ) { + //assert(questionId.length == 32); + + const [question] = getQuestionAddr( + this.vaultProgram.programId, + questionId, + oracle, + numConditions + ); + + return this.vaultProgram.methods + .initializeQuestion({ + questionId, + oracle, + numConditions, + }) + .accounts({ + question, + }); + } + async mintConditionalTokens( vault: PublicKey, uiAmount: number, diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 20730eed..3f35a889 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -4,3 +4,4 @@ export * from "./constants"; export * from "./AmmClient"; export * from "./AutocratClient"; export * from "./ConditionalVaultClient"; +export { sha256 } from "@noble/hashes/sha256"; diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index c4c90e97..bb08581e 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -2,6 +2,34 @@ export type ConditionalVault = { version: "0.4.0"; name: "conditional_vault"; instructions: [ + { + name: "initializeQuestion"; + accounts: [ + { + name: "question"; + isMut: true; + isSigner: false; + }, + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "args"; + type: { + defined: "InitializeQuestionArgs"; + }; + } + ]; + }, { name: "initializeConditionalVault"; accounts: [ @@ -340,13 +368,19 @@ export type ConditionalVault = { kind: "struct"; fields: [ { - name: "isResolved"; - type: "bool"; + name: "questionId"; + type: { + array: ["u8", 32]; + }; }, { name: "oracle"; type: "publicKey"; }, + { + name: "isResolved"; + type: "bool"; + }, { name: "payoutNumerators"; type: { @@ -360,6 +394,40 @@ export type ConditionalVault = { ]; }; }, + { + name: "newConditionalVault"; + type: { + kind: "struct"; + fields: [ + { + name: "question"; + type: "publicKey"; + }, + { + name: "underlyingTokenMint"; + type: "publicKey"; + }, + { + name: "underlyingTokenAccount"; + type: "publicKey"; + }, + { + name: "conditionalTokenMints"; + type: { + vec: "publicKey"; + }; + }, + { + name: "pdaBump"; + type: "u8"; + }, + { + name: "decimals"; + type: "u8"; + } + ]; + }; + }, { name: "conditionalVault"; type: { @@ -443,6 +511,28 @@ export type ConditionalVault = { ]; }; }, + { + name: "InitializeQuestionArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "questionId"; + type: { + array: ["u8", 32]; + }; + }, + { + name: "oracle"; + type: "publicKey"; + }, + { + name: "numConditions"; + type: "u8"; + } + ]; + }; + }, { name: "VaultStatus"; type: { @@ -494,6 +584,34 @@ export const IDL: ConditionalVault = { version: "0.4.0", name: "conditional_vault", instructions: [ + { + name: "initializeQuestion", + accounts: [ + { + name: "question", + isMut: true, + isSigner: false, + }, + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "InitializeQuestionArgs", + }, + }, + ], + }, { name: "initializeConditionalVault", accounts: [ @@ -832,13 +950,19 @@ export const IDL: ConditionalVault = { kind: "struct", fields: [ { - name: "isResolved", - type: "bool", + name: "questionId", + type: { + array: ["u8", 32], + }, }, { name: "oracle", type: "publicKey", }, + { + name: "isResolved", + type: "bool", + }, { name: "payoutNumerators", type: { @@ -852,6 +976,40 @@ export const IDL: ConditionalVault = { ], }, }, + { + name: "newConditionalVault", + type: { + kind: "struct", + fields: [ + { + name: "question", + type: "publicKey", + }, + { + name: "underlyingTokenMint", + type: "publicKey", + }, + { + name: "underlyingTokenAccount", + type: "publicKey", + }, + { + name: "conditionalTokenMints", + type: { + vec: "publicKey", + }, + }, + { + name: "pdaBump", + type: "u8", + }, + { + name: "decimals", + type: "u8", + }, + ], + }, + }, { name: "conditionalVault", type: { @@ -935,6 +1093,28 @@ export const IDL: ConditionalVault = { ], }, }, + { + name: "InitializeQuestionArgs", + type: { + kind: "struct", + fields: [ + { + name: "questionId", + type: { + array: ["u8", 32], + }, + }, + { + name: "oracle", + type: "publicKey", + }, + { + name: "numConditions", + type: "u8", + }, + ], + }, + }, { name: "VaultStatus", type: { diff --git a/sdk/src/utils/pda.ts b/sdk/src/utils/pda.ts index 115043a8..a11dceab 100644 --- a/sdk/src/utils/pda.ts +++ b/sdk/src/utils/pda.ts @@ -11,6 +11,23 @@ import { } from "@metaplex-foundation/umi-web3js-adapters"; import { MPL_TOKEN_METADATA_PROGRAM_ID } from "../constants"; +export const getQuestionAddr = ( + programId: PublicKey, + questionId: number[], + oracle: PublicKey, + numConditions: number +) => { + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("question"), + Buffer.from(questionId), + oracle.toBuffer(), + new BN(numConditions).toBuffer("le", 1), + ], + programId + ); +}; + export const getVaultAddr = ( programId: PublicKey, settlementAuthority: PublicKey, diff --git a/sdk/yarn.lock b/sdk/yarn.lock index 030d5a20..cc59b3f3 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -159,6 +159,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== +"@noble/hashes@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + "@solana/buffer-layout-utils@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" diff --git a/tests/autocrat.ts b/tests/autocrat.ts index 87e9bfae..5f498934 100644 --- a/tests/autocrat.ts +++ b/tests/autocrat.ts @@ -41,7 +41,10 @@ import { getVaultAddr, } from "@metadaoproject/futarchy"; import { PriceMath } from "@metadaoproject/futarchy"; -import { AutocratClient, ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { + AutocratClient, + ConditionalVaultClient, +} from "@metadaoproject/futarchy"; import { ComputeBudgetInstruction, ComputeBudgetProgram, diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index 5e5f60d8..e623f07f 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -40,6 +40,7 @@ import { getVaultAddr, getVaultFinalizeMintAddr, getVaultRevertMintAddr, + sha256, } from "@metadaoproject/futarchy"; const ConditionalVaultIDL: ConditionalVault = require("../target/idl/conditional_vault.json"); @@ -141,6 +142,18 @@ describe("conditional_vault", async function () { ); }); + describe("#initialize_question", async function () { + it("initializes questions", async () => { + let digest = sha256(new Uint8Array([1, 2, 3])); + console.log(digest); + console.log(digest.length); + + await vaultClient + .initializeQuestionIx([...digest], settlementAuthority.publicKey, 10) + .rpc(); + }); + }); + describe("#initialize_conditional_vault", async function () { it("initializes vaults", async function () { await vaultClient @@ -895,13 +908,15 @@ async function generateRandomVault( // rent: SYSVAR_RENT_PUBKEY, // }) // .instruction(); - const addMetadataToConditionalTokensIx = await vaultClient.addMetadataToConditionalTokensIx( - vault, - underlyingTokenMint, - 1, - METADATA_URI, - METADATA_URI - ).instruction(); + const addMetadataToConditionalTokensIx = await vaultClient + .addMetadataToConditionalTokensIx( + vault, + underlyingTokenMint, + 1, + METADATA_URI, + METADATA_URI + ) + .instruction(); await vaultClient .initializeVaultIx( diff --git a/tests/timelock.ts b/tests/timelock.ts index e68c5ed7..e24f317c 100644 --- a/tests/timelock.ts +++ b/tests/timelock.ts @@ -148,7 +148,9 @@ describe("timelock", async function () { .rpc(); const transactionBatchAccount = - await timelockProgram.account.transactionBatch.fetch(transactionBatch.publicKey); + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Assertions to check the transaction batch creation assert.strictEqual( transactionBatchAccount.transactionBatchAuthority.toString(), @@ -197,15 +199,13 @@ describe("timelock", async function () { // Transaction 2: Set Delay in Slots const newDelayInSlots = new BN(2); - let setDelayInSlotsInstruction = timelockProgram.instruction.setDelayInSlots( - newDelayInSlots, - { + let setDelayInSlotsInstruction = + timelockProgram.instruction.setDelayInSlots(newDelayInSlots, { accounts: { timelock: timelockKp.publicKey, timelockSigner: timelockSignerPubkey, }, - } - ); + }); await timelockProgram.methods .addTransaction( @@ -253,15 +253,13 @@ describe("timelock", async function () { .rpc(); // Transaction 4: Add enqueuer - let addEnqueuerInstruction = timelockProgram.instruction.addOptimisticProposer( - enqueuer4.publicKey, - { + let addEnqueuerInstruction = + timelockProgram.instruction.addOptimisticProposer(enqueuer4.publicKey, { accounts: { timelock: timelockKp.publicKey, timelockSigner: timelockSignerPubkey, }, - } - ); + }); await timelockProgram.methods .addTransaction( @@ -281,15 +279,16 @@ describe("timelock", async function () { .rpc(); // Transaction 5: Remove enqueuer - let removeOptimisticProposerInstruction = timelockProgram.instruction.removeOptimisticProposer( - enqueuer3.publicKey, - { - accounts: { - timelock: timelockKp.publicKey, - timelockSigner: timelockSignerPubkey, - }, - } - ); + let removeOptimisticProposerInstruction = + timelockProgram.instruction.removeOptimisticProposer( + enqueuer3.publicKey, + { + accounts: { + timelock: timelockKp.publicKey, + timelockSigner: timelockSignerPubkey, + }, + } + ); await timelockProgram.methods .addTransaction( @@ -310,7 +309,9 @@ describe("timelock", async function () { // Verify the transactions const transactionBatchAccount = - await timelockProgram.account.transactionBatch.fetch(transactionBatch.publicKey); + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); assert.strictEqual( transactionBatchAccount.transactions.length, 5, @@ -325,7 +326,7 @@ describe("timelock", async function () { .enqueueTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:timelockAuthority.publicKey, + authority: timelockAuthority.publicKey, timelock: timelockKp.publicKey, }) .signers([timelockAuthority]) @@ -349,7 +350,9 @@ describe("timelock", async function () { .rpc(); const sealedTransactionBatch = - await timelockProgram.account.transactionBatch.fetch(transactionBatch.publicKey); + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Assert the transaction batch is now sealed assert.ok( @@ -365,7 +368,7 @@ describe("timelock", async function () { .cancelTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:timelockAuthority.publicKey, + authority: timelockAuthority.publicKey, timelock: timelockKp.publicKey, }) .signers([timelockAuthority]) @@ -457,7 +460,7 @@ describe("timelock", async function () { .enqueueTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:fakeAuthority.publicKey, + authority: fakeAuthority.publicKey, timelock: timelockKp.publicKey, }) .signers([fakeAuthority]) @@ -475,14 +478,16 @@ describe("timelock", async function () { .enqueueTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:timelockAuthority.publicKey, + authority: timelockAuthority.publicKey, timelock: timelockKp.publicKey, }) .signers([timelockAuthority]) .rpc(); const enqueuedTransactionBatch = - await timelockProgram.account.transactionBatch.fetch(transactionBatch.publicKey); + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Assert the transaction batch is now in TimelockStarted status assert.ok( @@ -508,7 +513,7 @@ describe("timelock", async function () { .cancelTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:enqueuer1.publicKey, + authority: enqueuer1.publicKey, timelock: timelockKp.publicKey, }) .signers([enqueuer1]) @@ -571,7 +576,9 @@ describe("timelock", async function () { // Verification step const transactionBatchAccount = - await timelockProgram.account.transactionBatch.fetch(transactionBatch.publicKey); + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Check if the first transaction did execute assert.strictEqual( @@ -599,13 +606,19 @@ describe("timelock", async function () { .remainingAccounts([ { pubkey: timelockSignerPubkey, isWritable: false, isSigner: false }, { pubkey: timelockKp.publicKey, isWritable: true, isSigner: false }, - { pubkey: timelockProgram.programId, isWritable: false, isSigner: false }, + { + pubkey: timelockProgram.programId, + isWritable: false, + isSigner: false, + }, ]) .rpc(); // Verification step const transactionBatchAccount = - await timelockProgram.account.transactionBatch.fetch(transactionBatch.publicKey); + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Check if the second transaction did execute assert.strictEqual( @@ -640,7 +653,11 @@ describe("timelock", async function () { .remainingAccounts([ { pubkey: timelockSignerPubkey, isWritable: false, isSigner: false }, { pubkey: timelockKp.publicKey, isWritable: true, isSigner: false }, - { pubkey: timelockProgram.programId, isWritable: false, isSigner: false }, + { + pubkey: timelockProgram.programId, + isWritable: false, + isSigner: false, + }, ]) .preInstructions([ // this is to get around bankrun thinking we've processed the same transaction multiple times @@ -652,7 +669,9 @@ describe("timelock", async function () { // Fetch the updated TransactionBatch and timelock account to verify changes const updatedTransactionBatch = - await timelockProgram.account.transactionBatch.fetch(transactionBatch.publicKey); + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); const updatedTimelockAccount = await timelockProgram.account.timelock.fetch( timelockKp.publicKey ); @@ -682,13 +701,19 @@ describe("timelock", async function () { .remainingAccounts([ { pubkey: timelockSignerPubkey, isWritable: false, isSigner: false }, { pubkey: timelockKp.publicKey, isWritable: true, isSigner: false }, - { pubkey: timelockProgram.programId, isWritable: false, isSigner: false }, + { + pubkey: timelockProgram.programId, + isWritable: false, + isSigner: false, + }, ]) .rpc(); // Fetch the updated TransactionBatch and timelock account to verify changes const updatedTransactionBatch = - await timelockProgram.account.transactionBatch.fetch(transactionBatch.publicKey); + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); const updatedTimelockAccount = await timelockProgram.account.timelock.fetch( timelockKp.publicKey ); @@ -720,7 +745,11 @@ describe("timelock", async function () { .remainingAccounts([ { pubkey: timelockSignerPubkey, isWritable: false, isSigner: false }, { pubkey: timelockKp.publicKey, isWritable: true, isSigner: false }, - { pubkey: timelockProgram.programId, isWritable: false, isSigner: false }, + { + pubkey: timelockProgram.programId, + isWritable: false, + isSigner: false, + }, ]) .preInstructions([ // this is to get around bankrun thinking we've processed the same transaction multiple times @@ -732,7 +761,9 @@ describe("timelock", async function () { // Fetch the updated TransactionBatch and timelock account to verify changes const updatedTransactionBatch = - await timelockProgram.account.transactionBatch.fetch(transactionBatch.publicKey); + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); const updatedTimelockAccount = await timelockProgram.account.timelock.fetch( timelockKp.publicKey ); @@ -781,9 +812,10 @@ describe("timelock", async function () { .signers([enqueuer1, transactionBatch]) .rpc(); - let transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + let transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been created assert.ok( @@ -801,9 +833,10 @@ describe("timelock", async function () { .signers([enqueuer1]) .rpc(); - transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been sealed assert.ok( @@ -816,15 +849,16 @@ describe("timelock", async function () { .enqueueTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:enqueuer1.publicKey, // Assuming recipient as the new authority + authority: enqueuer1.publicKey, // Assuming recipient as the new authority timelock: timelockKp.publicKey, }) .signers([enqueuer1]) .rpc(); - transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been enqueued assert.ok( @@ -842,15 +876,16 @@ describe("timelock", async function () { .cancelTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:enqueuer1.publicKey, + authority: enqueuer1.publicKey, timelock: timelockKp.publicKey, }) .signers([enqueuer1]) .rpc(); - transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been cancelled assert.ok( @@ -881,9 +916,10 @@ describe("timelock", async function () { .signers([enqueuer1, transactionBatch]) .rpc(); - let transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + let transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been created assert.ok( @@ -901,9 +937,10 @@ describe("timelock", async function () { .signers([enqueuer1]) .rpc(); - transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been sealed assert.ok( @@ -919,7 +956,7 @@ describe("timelock", async function () { .enqueueTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:enqueuer1.publicKey, // Assuming recipient as the new authority + authority: enqueuer1.publicKey, // Assuming recipient as the new authority timelock: timelockKp.publicKey, }) .signers([enqueuer1]) @@ -943,24 +980,25 @@ describe("timelock", async function () { // now it'll succeed await timelockProgram.methods - .enqueueTransactionBatch() - .accounts({ - transactionBatch: transactionBatch.publicKey, - authority:enqueuer1.publicKey, // Assuming recipient as the new authority - timelock: timelockKp.publicKey, - }) - .signers([enqueuer1]) - .preInstructions([ - // this is to get around bankrun thinking we've processed the same transaction multiple times - ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: 69, - }), - ]) - .rpc(); + .enqueueTransactionBatch() + .accounts({ + transactionBatch: transactionBatch.publicKey, + authority: enqueuer1.publicKey, // Assuming recipient as the new authority + timelock: timelockKp.publicKey, + }) + .signers([enqueuer1]) + .preInstructions([ + // this is to get around bankrun thinking we've processed the same transaction multiple times + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: 69, + }), + ]) + .rpc(); - transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been enqueued assert.ok( @@ -973,15 +1011,16 @@ describe("timelock", async function () { .cancelTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:enqueuer2.publicKey, + authority: enqueuer2.publicKey, timelock: timelockKp.publicKey, }) .signers([enqueuer2]) .rpc(); - transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been cancelled assert.ok( @@ -1012,9 +1051,10 @@ describe("timelock", async function () { .signers([enqueuer2, transactionBatch]) .rpc(); - let transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + let transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been created assert.ok( @@ -1032,9 +1072,10 @@ describe("timelock", async function () { .signers([enqueuer2]) .rpc(); - transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been sealed assert.ok( @@ -1047,15 +1088,16 @@ describe("timelock", async function () { .enqueueTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:enqueuer2.publicKey, // Assuming recipient as the new authority + authority: enqueuer2.publicKey, // Assuming recipient as the new authority timelock: timelockKp.publicKey, }) .signers([enqueuer2]) .rpc(); - transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been enqueued assert.ok( @@ -1068,15 +1110,16 @@ describe("timelock", async function () { .cancelTransactionBatch() .accounts({ transactionBatch: transactionBatch.publicKey, - authority:recipient.publicKey, + authority: recipient.publicKey, timelock: timelockKp.publicKey, }) .signers([recipient]) .rpc(); - transactionBatchAccount = await timelockProgram.account.transactionBatch.fetch( - transactionBatch.publicKey - ); + transactionBatchAccount = + await timelockProgram.account.transactionBatch.fetch( + transactionBatch.publicKey + ); // Verify the transaction batch has been cancelled assert.ok( diff --git a/tests/utils/programFacade.ts b/tests/utils/programFacade.ts deleted file mode 100644 index e3a83d91..00000000 --- a/tests/utils/programFacade.ts +++ /dev/null @@ -1,527 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { assert } from "chai"; -import * as token from "@solana/spl-token"; - -import { randomMemberName } from "./testUtils"; - -import { PDAGenerator } from "./pdaGenerator"; -import { Program, PublicKey, Signer } from "./metaDAO"; - -export class ProgramFacade { - program: Program; - generator: PDAGenerator; - connection: anchor.web3.Connection; - payer: Signer; - metaDAO: PublicKey; - - constructor(program: Program) { - this.generator = new PDAGenerator(program); - this.program = program; - this.connection = program.provider.connection; - this.payer = program.provider.wallet.payer; - } - - async createTokenAccount( - mint: PublicKey, - owner: PublicKey - ): Promise { - return await token.createAccount(this.connection, this.payer, mint, owner); - } - - async createMint( - decimals?: number, - mintAuthority?: PublicKey - ): Promise<[PublicKey, Signer | PublicKey]> { - let mintAuthorityToUse; - let mintAuthorityToReturn; - - if (typeof mintAuthority != "undefined") { - mintAuthorityToUse = mintAuthority; - mintAuthorityToReturn = mintAuthority; - } else { - let signer = anchor.web3.Keypair.generate(); - mintAuthorityToUse = signer.publicKey; - mintAuthorityToReturn = signer; - } - - const mint = await token.createMint( - this.connection, - this.payer, - mintAuthorityToUse, - mintAuthorityToUse, - typeof decimals != "undefined" ? decimals : 2 - ); - - return [mint, mintAuthorityToReturn]; - } - - async mintTo( - mint: PublicKey, - destination: PublicKey, - mintAuthority: Signer, - amount: number - ) { - await token.mintTo( - this.connection, - this.payer, - mint, - destination, - mintAuthority, - amount - ); - } - - async transfer( - from: PublicKey, - to: PublicKey, - amount: number, - authority: Signer - ) { - await token.transfer( - this.connection, - this.payer, - from, - to, - authority, - amount - ); - } - - async initializeMember(name: string): Promise { - const [member] = this.generator.generateMemberPDAAddress(name); - const [treasury] = this.generator.generateTreasuryPDAAddress(member); - - const tokenMint = anchor.web3.Keypair.generate(); - - await this.program.methods - .initializeMember(name) - .accounts({ - member, - treasury, - tokenMint: tokenMint.publicKey, - initializer: this.payer.publicKey, - }) - .signers([tokenMint]) - .rpc(); - - const storedMember = await this.program.account.member.fetch(member); - - assert.equal(storedMember.name, name); - assert.ok(storedMember.tokenMint.equals(tokenMint.publicKey)); - - return member; - } - - async initializeMetaDAO(seedMember: PublicKey): Promise { - const [metaDAO] = this.generator.generateMetaDAOPDAAddress(); - - await this.program.methods - .initializeMetaDao() - .accounts({ - metaDao: metaDAO, - seedMember, - initializer: this.payer.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }) - .rpc(); - - const storedMetaDAO = await this.program.account.metaDao.fetch(metaDAO); - - assert.equal(storedMetaDAO.members.length, 1); - - this.metaDAO = metaDAO; - - return metaDAO; - } - - async getOrCreateMetaDAO() { - if (this.metaDAO == null) { - let seedMember = await this.initializeMember(randomMemberName()); - await this.initializeMetaDAO(seedMember); - } - - return this.metaDAO; - } - - async initializeProposal( - metaDAO: PublicKey, - instructions: [], - accounts: [] - ): Promise { - const provider = this.program.provider; - const proposalKeypair = anchor.web3.Keypair.generate(); - - await this.program.methods - .initializeProposal(instructions, accounts) - .preInstructions([ - await this.program.account.proposal.createInstruction( - proposalKeypair, - 1000 - ), - ]) - .accounts({ - proposal: proposalKeypair.publicKey, - metaDao: metaDAO, - initializer: this.payer.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }) - .signers([proposalKeypair]) - .rpc(); - - const storedProposal = await this.program.account.proposal.fetch( - proposalKeypair.publicKey - ); - - assert.exists(storedProposal.status.pending); - assert.equal(storedProposal.instructions.length, instructions.length); - - for (let i = 0; i < instructions.length; i++) { - const ix = instructions[i]; - const storedIx = storedProposal.instructions[i]; - - // assert.ok(storedIx.memberSigner.equals(ix.memberSigner)); - assert.ok(storedIx.programId.equals(ix.programId)); - assert.deepEqual(storedIx.accounts, ix.accounts); - assert.deepEqual(storedIx.data, ix.data); - } - - return proposalKeypair.publicKey; - } - - async executeProposal(proposal: PublicKey, remainingAccounts?: []) { - let builder = this.program.methods.executeProposal().accounts({ - proposal, - }); - - if (typeof remainingAccounts != "undefined") { - builder = builder.remainingAccounts(remainingAccounts); - } - - await builder.rpc(); - - const storedProposal = await this.program.account.proposal.fetch(proposal); - - assert.notExists(storedProposal.status.pending); - assert.exists(storedProposal.status.passed); - } - - async initializeConditionalExpression( - proposal: anchor.web3.PublicKey, - redeemableOnPass: boolean - ): Promise { - const [conditionalExpression] = - this.generator.generateConditionalExpressionPDAAddress( - proposal, - redeemableOnPass - ); - - const passOrFail = redeemableOnPass ? { pass: {} } : { fail: {} }; - - await this.program.methods - .initializeConditionalExpression(passOrFail) - .accounts({ - conditionalExpression, - initializer: this.payer.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - proposal, - }) - .rpc(); - - const storedConditionalExpression = - await this.program.account.conditionalExpression.fetch( - conditionalExpression - ); - - assert.ok(storedConditionalExpression.proposal.equals(proposal)); - assert.deepEqual(storedConditionalExpression.passOrFail, passOrFail); - - return conditionalExpression; - } - - async initializeVault( - conditionalExpression: PublicKey, - underlyingTokenMint: PublicKey - ): Promise<[PublicKey, PublicKey, PublicKey]> { - const [vault] = this.generator.generateVaultPDAAddress( - conditionalExpression, - underlyingTokenMint - ); - - const conditionalTokenMint: Signer = anchor.web3.Keypair.generate(); - const vaultUnderlyingTokenAccount = await token.getAssociatedTokenAddress( - underlyingTokenMint, - vault, - true - ); - - await this.program.methods - .initializeVault() - .accounts({ - vault, - conditionalExpression, - underlyingTokenMint, - conditionalTokenMint: conditionalTokenMint.publicKey, - vaultUnderlyingTokenAccount, - initializer: this.payer.publicKey, - }) - .signers([conditionalTokenMint]) - .rpc(); - - const storedVault = await this.program.account.vault.fetch(vault); - - assert.ok(storedVault.conditionalExpression.equals(conditionalExpression)); - assert.ok( - storedVault.underlyingTokenAccount.equals(vaultUnderlyingTokenAccount) - ); - assert.ok(storedVault.underlyingTokenMint.equals(underlyingTokenMint)); - assert.ok( - storedVault.conditionalTokenMint.equals(conditionalTokenMint.publicKey) - ); - - return [vault, conditionalTokenMint.publicKey, vaultUnderlyingTokenAccount]; - } - - async initializeDepositSlip( - vault: PublicKey, - _depositor?: PublicKey - ): Promise { - const provider = this.program.provider; - - let depositor = - typeof _depositor == "undefined" ? this.payer.publicKey : _depositor; - - const [depositSlip] = this.generator.generateDepositSlipPDAAddress( - vault, - depositor - ); - - await this.program.methods - .initializeDepositSlip(depositor) - .accounts({ - vault, - depositSlip, - initializer: this.payer.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }) - .rpc(); - - let storedDepositSlip = await this.program.account.depositSlip.fetch( - depositSlip - ); - - assert.ok(storedDepositSlip.vault.equals(vault)); - assert.ok(storedDepositSlip.user.equals(depositor)); - assert.ok(storedDepositSlip.depositedAmount.eq(new anchor.BN(0))); - - return depositSlip; - } - - async mintConditionalTokens( - amount: number, - user: Signer, - depositSlip: PublicKey, - vault: PublicKey, - vaultUnderlyingTokenAccount: PublicKey, - userUnderlyingTokenAccount: PublicKey, - conditionalTokenMint: PublicKey, - userConditionalTokenAccount: PublicKey - ) { - const depositSlipBefore = await this.program.account.depositSlip.fetch( - depositSlip - ); - const vaultUnderlyingTokenAccountBefore = await token.getAccount( - this.connection, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountBefore = await token.getAccount( - this.connection, - userUnderlyingTokenAccount - ); - const userConditionalTokenAccountBefore = await token.getAccount( - this.connection, - userConditionalTokenAccount - ); - - const bnAmount = new anchor.BN(amount); - await this.program.methods - .mintConditionalTokens(bnAmount) - .accounts({ - user: user.publicKey, - depositSlip, - vault, - vaultUnderlyingTokenAccount, - userUnderlyingTokenAccount, - conditionalTokenMint, - userConditionalTokenAccount, - tokenProgram: token.TOKEN_PROGRAM_ID, - }) - .signers([user]) - .rpc(); - - const depositSlipAfter = await this.program.account.depositSlip.fetch( - depositSlip - ); - const vaultUnderlyingTokenAccountAfter = await token.getAccount( - this.connection, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountAfter = await token.getAccount( - this.connection, - userUnderlyingTokenAccount - ); - const userConditionalTokenAccountAfter = await token.getAccount( - this.connection, - userConditionalTokenAccount - ); - - assert.ok( - depositSlipAfter.depositedAmount.eq( - depositSlipBefore.depositedAmount.add(bnAmount) - ) - ); - assert.equal( - vaultUnderlyingTokenAccountAfter.amount, - vaultUnderlyingTokenAccountBefore.amount + BigInt(amount) - ); - assert.equal( - userUnderlyingTokenAccountAfter.amount, - userUnderlyingTokenAccountBefore.amount - BigInt(amount) - ); - assert.equal( - userConditionalTokenAccountAfter.amount, - userConditionalTokenAccountBefore.amount + BigInt(amount) - ); - } - - async redeemConditionalForUnderlyingTokens( - user: Signer, - userConditionalTokenAccount: PublicKey, - userUnderlyingTokenAccount: PublicKey, - vaultUnderlyingTokenAccount: PublicKey, - vault: PublicKey, - proposal: PublicKey, - conditionalExpression: PublicKey, - conditionalTokenMint: PublicKey - ) { - const vaultUnderlyingTokenAccountBefore = await token.getAccount( - this.connection, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountBefore = await token.getAccount( - this.connection, - userUnderlyingTokenAccount - ); - const userConditionalTokenAccountBefore = await token.getAccount( - this.connection, - userConditionalTokenAccount - ); - - await this.program.methods - .redeemConditionalTokensForUnderlyingTokens() - .accounts({ - user: user.publicKey, - userConditionalTokenAccount, - userUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - proposal, - tokenProgram: token.TOKEN_PROGRAM_ID, - conditionalExpression, - conditionalTokenMint, - }) - .signers([user]) - .rpc(); - - const vaultUnderlyingTokenAccountAfter = await token.getAccount( - this.connection, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountAfter = await token.getAccount( - this.connection, - userUnderlyingTokenAccount - ); - const userConditionalTokenAccountAfter = await token.getAccount( - this.connection, - userConditionalTokenAccount - ); - - assert.equal( - vaultUnderlyingTokenAccountAfter.amount, - vaultUnderlyingTokenAccountBefore.amount - - BigInt(userConditionalTokenAccountBefore.amount) - ); - assert.equal( - userUnderlyingTokenAccountAfter.amount, - userUnderlyingTokenAccountBefore.amount + - BigInt(userConditionalTokenAccountBefore.amount) - ); - assert.equal(userConditionalTokenAccountAfter.amount, BigInt(0)); - } - - async redeemDepositSlipForUnderlyingTokens( - user: Signer, - userDepositSlip: PublicKey, - userUnderlyingTokenAccount: PublicKey, - vaultUnderlyingTokenAccount: PublicKey, - vault: PublicKey, - proposal: PublicKey, - conditionalExpression: PublicKey - ) { - // const vaultUnderlyingTokenAccountBefore = await token.getAccount( - // this.connection, - // vaultUnderlyingTokenAccount - // ); - // const userUnderlyingTokenAccountBefore = await token.getAccount( - // this.connection, - // userUnderlyingTokenAccount - // ); - // const userConditionalTokenAccountBefore = await token.getAccount( - // this.connection, - // userConditionalTokenAccount - // ); - - await this.program.methods - .redeemDepositSlipForUnderlyingTokens() - .accounts({ - user: user.publicKey, - userDepositSlip, - userUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - proposal, - tokenProgram: token.TOKEN_PROGRAM_ID, - conditionalExpression, - }) - .signers([user]) - .rpc(); - - // const vaultUnderlyingTokenAccountAfter = await token.getAccount( - // this.connection, - // vaultUnderlyingTokenAccount - // ); - // const userUnderlyingTokenAccountAfter = await token.getAccount( - // this.connection, - // userUnderlyingTokenAccount - // ); - // const userConditionalTokenAccountAfter = await token.getAccount( - // this.connection, - // userConditionalTokenAccount - // ); - - // assert.equal( - // vaultUnderlyingTokenAccountAfter.amount, - // vaultUnderlyingTokenAccountBefore.amount - - // BigInt(userConditionalTokenAccountBefore.amount) - // ); - // assert.equal( - // userUnderlyingTokenAccountAfter.amount, - // userUnderlyingTokenAccountBefore.amount + - // BigInt(userConditionalTokenAccountBefore.amount) - // ); - // assert.equal(userConditionalTokenAccountAfter.amount, BigInt(0)); - } - - async failProposal(proposal: PublicKey) { - await this.program.methods.failProposal().accounts({ proposal }).rpc(); - } -} diff --git a/tests/utils/testUtils.ts b/tests/utils/testUtils.ts deleted file mode 100644 index 64496d90..00000000 --- a/tests/utils/testUtils.ts +++ /dev/null @@ -1,299 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; - -import { expect, assert } from "chai"; -import { randomBytes } from "crypto"; - -import { ProgramFacade } from "./programFacade"; - -import { - Program, - PublicKey, - Signer, - ProposalState, - RedemptionType, -} from "./autocrat"; - -export const expectError = ( - expectedErrorCode: string, - message: string -): [() => void, (e: any) => void] => { - return [ - () => assert.fail(message), - (e) => { - console.log(e); - assert( - e["error"] != undefined, - `the program threw for a reason that we didn't expect. error: ${e}` - ); - assert.equal(e.error.errorCode.code, expectedErrorCode); - }, - ]; -}; - -export const randomMemberName = () => randomBytes(5).toString("hex"); - -export const initializeSampleProposal = async ( - program: ProgramFacade -): Promise<[PublicKey, PublicKey]> => { - const memberToAdd = await program.initializeMember(randomMemberName()); - const metaDAO = await program.getOrCreateMetaDAO(); - - const [proposalAccounts, proposalInstructions] = - sampleProposalAccountsAndInstructions( - program.program, - metaDAO, - memberToAdd - ); - const proposal = await program.initializeProposal( - metaDAO, - proposalInstructions, - proposalAccounts - ); - - return [proposal, memberToAdd]; -}; - -export const executeSampleProposal = async ( - sampleProposal: PublicKey, - memberToAdd: PublicKey, - programFacade: ProgramFacade -) => { - const metaDAO = await programFacade.getOrCreateMetaDAO(); - const program = programFacade.program; - - let accountInfos = program.instruction.addMember - .accounts({ metaDao: metaDAO, member: memberToAdd }) - .map((accountInfo) => - accountInfo.pubkey.equals(metaDAO) - ? { ...accountInfo, isSigner: false } - : accountInfo - ) - .concat({ - pubkey: program.programId, - isWritable: false, - isSigner: false, - }); - - await programFacade.executeProposal(sampleProposal, accountInfos); -}; - -export const initializeSampleConditionalExpression = async ( - program: ProgramFacade, - passOrFailFlag: boolean = true -): Promise<[PublicKey, PublicKey, PublicKey]> => { - const [proposal, memberToAdd] = await initializeSampleProposal(program); - - const conditionalExpression = await program.initializeConditionalExpression( - proposal, - passOrFailFlag - ); - - return [conditionalExpression, proposal, memberToAdd]; -}; - -export const initializeSampleVault = async ( - program: ProgramFacade, - passOrFailFlag: boolean = true -): Promise< - [ - PublicKey, - PublicKey, - PublicKey, - PublicKey, - Signer, - PublicKey, - PublicKey, - PublicKey - ] -> => { - const [conditionalExpression, proposal, memberToAdd] = - await initializeSampleConditionalExpression(program, passOrFailFlag); - - const [underlyingTokenMint, underlyingTokenMintAuthority] = - await program.createMint(); - - const [vault, conditionalTokenMint, vaultUnderlyingTokenAccount] = - await program.initializeVault(conditionalExpression, underlyingTokenMint); - - return [ - vault, - conditionalTokenMint, - vaultUnderlyingTokenAccount, - underlyingTokenMint, - underlyingTokenMintAuthority, - conditionalExpression, - proposal, - memberToAdd, - ]; -}; - -// have `minter` mint conditional tokens, then move them to `holder` -export const mintConditionalTokens = async ( - program: ProgramFacade, - minter: Signer, - holder: Signer, - amount: number, - vault: PublicKey, - vaultUnderlyingTokenAccount: PublicKey, - underlyingTokenMint: PublicKey, - underlyingTokenMintAuthority: Signer, - conditionalTokenMint: PublicKey -) => { - let minterUnderlyingTokenAccount = await program.createTokenAccount( - underlyingTokenMint, - minter.publicKey - ); - - let holderUnderlyingTokenAccount = await program.createTokenAccount( - underlyingTokenMint, - holder.publicKey - ); - - let minterConditionalTokenAccount = await program.createTokenAccount( - conditionalTokenMint, - minter.publicKey - ); - - let holderConditionalTokenAccount = await program.createTokenAccount( - conditionalTokenMint, - holder.publicKey - ); - - let minterDepositSlip = await program.initializeDepositSlip( - vault, - minter.publicKey - ); - - let holderDepositSlip = await program.initializeDepositSlip( - vault, - holder.publicKey - ); - - await program.mintTo( - underlyingTokenMint, - minterUnderlyingTokenAccount, - underlyingTokenMintAuthority, - amount - ); - - await program.mintConditionalTokens( - amount, - minter, - minterDepositSlip, - vault, - vaultUnderlyingTokenAccount, - minterUnderlyingTokenAccount, - conditionalTokenMint, - minterConditionalTokenAccount - ); - - await program.transfer( - minterConditionalTokenAccount, - holderConditionalTokenAccount, - amount, - minter - ); - - return [ - minterUnderlyingTokenAccount, - minterConditionalTokenAccount, - minterDepositSlip, - holderUnderlyingTokenAccount, - holderConditionalTokenAccount, - holderDepositSlip, - ]; -}; - -export const testRedemption = async ( - programFacade: ProgramFacade, - passOrFailFlag: boolean, - desiredProposalState: ProposalState, - redemptionType: RedemptionType, - shouldGoThrough: boolean -) => { - const [ - vault, - conditionalTokenMint, - vaultUnderlyingTokenAccount, - underlyingTokenMint, - underlyingTokenMintAuthority, - conditionalExpression, - proposal, - memberToAdd, - ] = await initializeSampleVault(programFacade, passOrFailFlag); - - let minter = anchor.web3.Keypair.generate(); - let holder = anchor.web3.Keypair.generate(); - let amount = 1000; - - const [ - minterUnderlyingTokenAccount, - minterConditionalTokenAccount, - minterDepositSlip, - holderUnderlyingTokenAccount, - holderConditionalTokenAccount, - holderDepositSlip, - ] = await mintConditionalTokens( - programFacade, - minter, - holder, - amount, - vault, - vaultUnderlyingTokenAccount, - underlyingTokenMint, - underlyingTokenMintAuthority, - conditionalTokenMint - ); - - if (desiredProposalState == ProposalState.Passed) { - await executeSampleProposal(proposal, memberToAdd, programFacade); - } else if (desiredProposalState == ProposalState.Failed) { - await programFacade.failProposal(proposal); - } - - let expectedErrorCode; - if (desiredProposalState == ProposalState.Pending) { - expectedErrorCode = "ConditionalExpressionNotEvaluatable"; - } else if (redemptionType == RedemptionType.ConditionalToken) { - expectedErrorCode = "CantRedeemConditionalTokens"; - } else { - expectedErrorCode = "CantRedeemDepositSlip"; - } - - let callbacks; - if (shouldGoThrough) { - callbacks = [() => {}, (e) => assert.fail(e)]; - } else { - callbacks = expectError( - expectedErrorCode, - "a redemption should have failed but instead suceeded" - ); - } - - if (redemptionType == RedemptionType.ConditionalToken) { - await programFacade - .redeemConditionalForUnderlyingTokens( - holder, - holderConditionalTokenAccount, - holderUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - proposal, - conditionalExpression, - conditionalTokenMint - ) - .then(callbacks[0], callbacks[1]); - } else { - await programFacade - .redeemDepositSlipForUnderlyingTokens( - minter, - minterDepositSlip, - minterUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - proposal, - conditionalExpression - ) - .then(callbacks[0], callbacks[1]); - } -}; From 6be4f2f77a803375ccb28292dc8d90c4b7109b10 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Tue, 20 Aug 2024 00:00:00 +0000 Subject: [PATCH 04/52] Initialize a new conditional vault --- programs/conditional_vault/src/error.rs | 2 + .../initialize_new_conditional_vault.rs | 104 ++++++++++++++++++ .../src/instructions/initialize_question.rs | 8 ++ .../conditional_vault/src/instructions/mod.rs | 2 + programs/conditional_vault/src/lib.rs | 6 + run.sh | 5 + sdk/src/ConditionalVaultClient.ts | 81 +++++++++++++- sdk/src/types/conditional_vault.ts | 102 +++++++++++++++++ sdk/src/utils/pda.ts | 15 +++ tests/conditionalVault.ts | 65 ++++++++++- 10 files changed, 384 insertions(+), 6 deletions(-) create mode 100644 programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index 44e47245..32a2e543 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -12,4 +12,6 @@ pub enum VaultError { CantRedeemConditionalTokens, #[msg("Once a vault has been settled, its status as either finalized or reverted cannot be changed")] VaultAlreadySettled, + #[msg("Questions need 2 or more conditions")] + InsufficientNumConditions, } diff --git a/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs b/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs new file mode 100644 index 00000000..65908589 --- /dev/null +++ b/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs @@ -0,0 +1,104 @@ +use super::*; + +use anchor_lang::system_program; +use anchor_spl::token; + +#[derive(Accounts)] +pub struct InitializeNewConditionalVault<'info> { + #[account( + init, + payer = payer, + space = 8 + std::mem::size_of::() + (32 * question.num_conditions()), + seeds = [ + b"conditional_vault", + question.key().as_ref(), + underlying_token_mint.key().as_ref(), + ], + bump + )] + pub vault: Box>, + pub question: Account<'info, Question>, + pub underlying_token_mint: Account<'info, Mint>, + #[account( + associated_token::authority = vault, + associated_token::mint = underlying_token_mint + )] + pub vault_underlying_token_account: Box>, + #[account(mut)] + pub payer: Signer<'info>, + pub token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, + pub system_program: Program<'info, System>, +} + +impl<'info, 'c: 'info> InitializeNewConditionalVault<'info> { + pub fn handle(ctx: Context<'_, '_, 'c, 'info, Self>) -> Result<()> { + let vault = &mut ctx.accounts.vault; + + let decimals = ctx.accounts.underlying_token_mint.decimals; + + let remaining_accs = &mut ctx.remaining_accounts.iter(); + + let expected_num_conditional_tokens = ctx.accounts.question.num_conditions(); + let mut conditional_token_mints = vec![]; + + let mint_lamports = Rent::get()?.minimum_balance(Mint::LEN); + for i in 0..expected_num_conditional_tokens { + let (conditional_token_mint_address, pda_bump) = Pubkey::find_program_address( + &[b"conditional_token", vault.key().as_ref(), &[i as u8]], + ctx.program_id, + ); + + let conditional_token_mint = next_account_info(remaining_accs)?; + require_eq!(conditional_token_mint.key(), conditional_token_mint_address); + + conditional_token_mints.push(conditional_token_mint_address); + + let cpi_program = ctx.accounts.system_program.to_account_info(); + let cpi_accounts = system_program::CreateAccount { + from: ctx.accounts.payer.to_account_info(), + to: conditional_token_mint.to_account_info(), + }; + let vault_key = vault.key(); + let seeds = &[ + b"conditional_token", + vault_key.as_ref(), + &[i as u8], + &[pda_bump], + ]; + let signer = &[&seeds[..]]; + let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); + + system_program::create_account( + cpi_ctx, + mint_lamports, + Mint::LEN as u64, + ctx.accounts.token_program.key, + )?; + + let cpi_program = ctx.accounts.token_program.to_account_info(); + let cpi_accounts = token::InitializeMint2 { + mint: conditional_token_mint.to_account_info(), + }; + let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + + token::initialize_mint2( + cpi_ctx, + decimals, + &vault.key(), + None, + )?; + } + + vault.set_inner(NewConditionalVault { + question: ctx.accounts.question.key(), + underlying_token_mint: ctx.accounts.underlying_token_mint.key(), + underlying_token_account: ctx.accounts.vault_underlying_token_account.key(), + conditional_token_mints, + pda_bump: ctx.bumps.vault, + decimals, + }); + + Ok(()) + } +} diff --git a/programs/conditional_vault/src/instructions/initialize_question.rs b/programs/conditional_vault/src/instructions/initialize_question.rs index 1ef12b46..dd0340e5 100644 --- a/programs/conditional_vault/src/instructions/initialize_question.rs +++ b/programs/conditional_vault/src/instructions/initialize_question.rs @@ -29,7 +29,15 @@ pub struct InitializeQuestion<'info> { } impl InitializeQuestion<'_> { + pub fn validate(&self, args: InitializeQuestionArgs) -> Result<()> { + require_gt!(args.num_conditions, 1); + + Ok(()) + } + pub fn handle(ctx: Context, args: InitializeQuestionArgs) -> Result<()> { + require_gte!(args.num_conditions, 2, VaultError::InsufficientNumConditions); + let question = &mut ctx.accounts.question; let InitializeQuestionArgs { diff --git a/programs/conditional_vault/src/instructions/mod.rs b/programs/conditional_vault/src/instructions/mod.rs index 1074c6f8..6f83dada 100644 --- a/programs/conditional_vault/src/instructions/mod.rs +++ b/programs/conditional_vault/src/instructions/mod.rs @@ -4,6 +4,7 @@ pub mod add_metadata_to_conditional_tokens; pub mod common; pub mod initialize_question; pub mod initialize_conditional_vault; +pub mod initialize_new_conditional_vault; pub mod merge_conditional_tokens; pub mod mint_conditional_tokens; pub mod redeem_conditional_tokens_for_underlying_tokens; @@ -13,4 +14,5 @@ pub use initialize_question::*; pub use add_metadata_to_conditional_tokens::*; pub use common::*; pub use initialize_conditional_vault::*; +pub use initialize_new_conditional_vault::*; pub use settle_conditional_vault::*; diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index 71c3602d..7ef30763 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -45,6 +45,12 @@ pub mod conditional_vault { InitializeQuestion::handle(ctx, args) } + pub fn initialize_new_conditional_vault<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, InitializeNewConditionalVault<'info>>, + ) -> Result<()> { + InitializeNewConditionalVault::handle(ctx) + } + pub fn initialize_conditional_vault( ctx: Context, args: InitializeConditionalVaultArgs, diff --git a/run.sh b/run.sh index e98bb65b..efddfb05 100755 --- a/run.sh +++ b/run.sh @@ -4,6 +4,10 @@ test() { find programs tests sdk | entr -sc '(cd sdk && yarn build) && RUST_LOG= anchor test' } +build_vault() { + find programs | entr -sc 'anchor build -p conditional_vault' +} + test_vault() { # anchor doesn't let you past test files, so we do this weird thing where we # modify the Anchor.toml and then put it back @@ -75,6 +79,7 @@ bankrun_logs() { case "$1" in test) test ;; vault) test_vault ;; + build_vault) build_vault ;; test_no_build) test_no_build ;; build_verifiable) build_verifiable "$2" ;; deploy) deploy "$2" "$3" ;; diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 49811eb2..68c5af0c 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -1,5 +1,10 @@ import { AnchorProvider, Program, utils } from "@coral-xyz/anchor"; -import { AddressLookupTableAccount, Keypair, PublicKey } from "@solana/web3.js"; +import { + AddressLookupTableAccount, + Keypair, + PublicKey, + SystemProgram, +} from "@solana/web3.js"; import { ConditionalVault, @@ -17,12 +22,14 @@ import { getVaultAddr, getVaultFinalizeMintAddr, getVaultRevertMintAddr, + getConditionalTokenMintAddr, } from "./utils"; import { MethodsBuilder } from "@coral-xyz/anchor/dist/cjs/program/namespace/methods"; import { createAssociatedTokenAccountIdempotentInstruction, createAssociatedTokenAccountInstruction, getAssociatedTokenAddressSync, + TOKEN_PROGRAM_ID, } from "@solana/spl-token"; export type CreateVaultClientParams = { @@ -54,6 +61,14 @@ export class ConditionalVaultClient { ); } + async fetchQuestion(question: PublicKey) { + return this.vaultProgram.account.question.fetch(question); + } + + async fetchVault(vault: PublicKey) { + return this.vaultProgram.account.newConditionalVault.fetch(vault); + } + async getVault(vault: PublicKey) { return this.vaultProgram.account.conditionalVault.fetch(vault); } @@ -83,6 +98,70 @@ export class ConditionalVaultClient { }); } + initializeNewVaultIx( + question: PublicKey, + underlyingTokenMint: PublicKey, + numConditions: number + ): MethodsBuilder { + const [vault] = getVaultAddr( + this.vaultProgram.programId, + question, + underlyingTokenMint + ); + + let conditionalTokenMintAddrs = []; + for (let i = 0; i < numConditions; i++) { + const [conditionalTokenMint] = getConditionalTokenMintAddr( + this.vaultProgram.programId, + vault, + i + ); + conditionalTokenMintAddrs.push(conditionalTokenMint); + } + console.log(conditionalTokenMintAddrs); + + const vaultUnderlyingTokenAccount = getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ); + + // const conditionalReject = Keypair.generate(); + + return this.vaultProgram.methods + .initializeNewConditionalVault() + .accounts({ + vault, + question, + underlyingTokenMint, + vaultUnderlyingTokenAccount, + }) + .preInstructions([ + createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + vaultUnderlyingTokenAccount, + vault, + underlyingTokenMint + ), + // SystemProgram.createAccount({ + // fromPubkey: this.provider.wallet.publicKey, + // newAccountPubkey: conditionalReject.publicKey, + // lamports: 1000000000, + // space: 82, + // programId: TOKEN_PROGRAM_ID, + // }), + ]) + .remainingAccounts( + conditionalTokenMintAddrs.map((conditionalTokenMint) => { + return { + pubkey: conditionalTokenMint, + isWritable: true, + isSigner: false, + }; + }) + ); + } + async mintConditionalTokens( vault: PublicKey, uiAmount: number, diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index bb08581e..6ceae845 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -30,6 +30,52 @@ export type ConditionalVault = { } ]; }, + { + name: "initializeNewConditionalVault"; + accounts: [ + { + name: "vault"; + isMut: true; + isSigner: false; + }, + { + name: "question"; + isMut: false; + isSigner: false; + }, + { + name: "underlyingTokenMint"; + isMut: false; + isSigner: false; + }, + { + name: "vaultUnderlyingTokenAccount"; + isMut: false; + isSigner: false; + }, + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "associatedTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + } + ]; + args: []; + }, { name: "initializeConditionalVault"; accounts: [ @@ -576,6 +622,11 @@ export type ConditionalVault = { code: 6004; name: "VaultAlreadySettled"; msg: "Once a vault has been settled, its status as either finalized or reverted cannot be changed"; + }, + { + code: 6005; + name: "InsufficientNumConditions"; + msg: "Questions need 2 or more conditions"; } ]; }; @@ -612,6 +663,52 @@ export const IDL: ConditionalVault = { }, ], }, + { + name: "initializeNewConditionalVault", + accounts: [ + { + name: "vault", + isMut: true, + isSigner: false, + }, + { + name: "question", + isMut: false, + isSigner: false, + }, + { + name: "underlyingTokenMint", + isMut: false, + isSigner: false, + }, + { + name: "vaultUnderlyingTokenAccount", + isMut: false, + isSigner: false, + }, + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "associatedTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, { name: "initializeConditionalVault", accounts: [ @@ -1159,5 +1256,10 @@ export const IDL: ConditionalVault = { name: "VaultAlreadySettled", msg: "Once a vault has been settled, its status as either finalized or reverted cannot be changed", }, + { + code: 6005, + name: "InsufficientNumConditions", + msg: "Questions need 2 or more conditions", + }, ], }; diff --git a/sdk/src/utils/pda.ts b/sdk/src/utils/pda.ts index a11dceab..b899709d 100644 --- a/sdk/src/utils/pda.ts +++ b/sdk/src/utils/pda.ts @@ -43,6 +43,21 @@ export const getVaultAddr = ( ); }; +export const getConditionalTokenMintAddr = ( + programId: PublicKey, + vault: PublicKey, + index: number +) => { + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("conditional_token"), + vault.toBuffer(), + new BN(index).toBuffer("le", 1), + ], + programId + ); +}; + export const getVaultFinalizeMintAddr = ( programId: PublicKey, vault: PublicKey diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index e623f07f..a1bbac3a 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -37,6 +37,7 @@ import { expectError } from "./utils/utils"; import { CONDITIONAL_VAULT_PROGRAM_ID, ConditionalVaultClient, + getQuestionAddr, getVaultAddr, getVaultFinalizeMintAddr, getVaultRevertMintAddr, @@ -67,6 +68,7 @@ describe("conditional_vault", async function () { let provider: anchor.Provider; let vaultClient: ConditionalVaultClient; + let questions: PublicKey[] = []; let vault: PublicKey; let proposal: PublicKey; let vaultUnderlyingTokenAccount: anchor.web3.PublicKey; @@ -143,14 +145,67 @@ describe("conditional_vault", async function () { }); describe("#initialize_question", async function () { - it("initializes questions", async () => { - let digest = sha256(new Uint8Array([1, 2, 3])); - console.log(digest); - console.log(digest.length); + it("initializes questions", async function () { + let questionId = sha256(new Uint8Array([1, 2, 3])); await vaultClient - .initializeQuestionIx([...digest], settlementAuthority.publicKey, 10) + .initializeQuestionIx([...questionId], settlementAuthority.publicKey, 2) .rpc(); + + let [question] = getQuestionAddr( + vaultProgram.programId, + questionId, + settlementAuthority.publicKey, + 2 + ); + + questions.push(question); + }); + }); + + describe("#initialize_new_conditional_vault", async function () { + it("initializes vaults", async function () { + const question = questions.shift(); + + await vaultClient + .initializeNewVaultIx( + question, + underlyingTokenMint, + 2 + ) + .rpc(); + + const [vault] = getVaultAddr( + vaultProgram.programId, + question, + underlyingTokenMint, + ); + + //console.log(await vaultProgram.account.newConditionalVault.fetch(vault)); + const storedVault = await vaultClient.fetchVault(vault); + console.log(storedVault); + assert.ok( + storedVault.question.equals(question) + ); + assert.ok(storedVault.underlyingTokenMint.equals(underlyingTokenMint)); + + const vaultUnderlyingTokenAccount = token.getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ); + assert.ok( + storedVault.underlyingTokenAccount.equals(vaultUnderlyingTokenAccount) + ); + //assert.ok( + // storedVault.conditionalOnFinalizeTokenMint.equals( + // conditionalOnFinalizeMint + // ) + //); + //assert.ok( + // storedVault.conditionalOnRevertTokenMint.equals(conditionalOnRevertMint) + //); + }); }); From d0f928ec7fb9ed284107487ada9f698cc4ebfdb5 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Tue, 20 Aug 2024 00:00:00 +0000 Subject: [PATCH 05/52] Add `resolve_question` --- programs/conditional_vault/src/error.rs | 2 + .../src/instructions/initialize_question.rs | 6 -- .../conditional_vault/src/instructions/mod.rs | 2 + .../src/instructions/resolve_question.rs | 32 +++++++ programs/conditional_vault/src/lib.rs | 7 ++ sdk/src/ConditionalVaultClient.ts | 18 +++- sdk/src/types/conditional_vault.ts | 84 +++++++++++++++++++ tests/conditionalVault.ts | 32 +++++-- 8 files changed, 166 insertions(+), 17 deletions(-) create mode 100644 programs/conditional_vault/src/instructions/resolve_question.rs diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index 32a2e543..b963d223 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -14,4 +14,6 @@ pub enum VaultError { VaultAlreadySettled, #[msg("Questions need 2 or more conditions")] InsufficientNumConditions, + #[msg("Invalid number of payout numerators")] + InvalidNumPayoutNumerators, } diff --git a/programs/conditional_vault/src/instructions/initialize_question.rs b/programs/conditional_vault/src/instructions/initialize_question.rs index dd0340e5..82a89f34 100644 --- a/programs/conditional_vault/src/instructions/initialize_question.rs +++ b/programs/conditional_vault/src/instructions/initialize_question.rs @@ -29,12 +29,6 @@ pub struct InitializeQuestion<'info> { } impl InitializeQuestion<'_> { - pub fn validate(&self, args: InitializeQuestionArgs) -> Result<()> { - require_gt!(args.num_conditions, 1); - - Ok(()) - } - pub fn handle(ctx: Context, args: InitializeQuestionArgs) -> Result<()> { require_gte!(args.num_conditions, 2, VaultError::InsufficientNumConditions); diff --git a/programs/conditional_vault/src/instructions/mod.rs b/programs/conditional_vault/src/instructions/mod.rs index 6f83dada..a65aee90 100644 --- a/programs/conditional_vault/src/instructions/mod.rs +++ b/programs/conditional_vault/src/instructions/mod.rs @@ -9,6 +9,7 @@ pub mod merge_conditional_tokens; pub mod mint_conditional_tokens; pub mod redeem_conditional_tokens_for_underlying_tokens; pub mod settle_conditional_vault; +pub mod resolve_question; pub use initialize_question::*; pub use add_metadata_to_conditional_tokens::*; @@ -16,3 +17,4 @@ pub use common::*; pub use initialize_conditional_vault::*; pub use initialize_new_conditional_vault::*; pub use settle_conditional_vault::*; +pub use resolve_question::*; diff --git a/programs/conditional_vault/src/instructions/resolve_question.rs b/programs/conditional_vault/src/instructions/resolve_question.rs new file mode 100644 index 00000000..2754b9c1 --- /dev/null +++ b/programs/conditional_vault/src/instructions/resolve_question.rs @@ -0,0 +1,32 @@ +use super::*; + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct ResolveQuestionArgs { + pub payout_numerators: Vec, +} + +#[derive(Accounts)] +#[instruction(args: ResolveQuestionArgs)] +pub struct ResolveQuestion<'info> { + #[account(mut, has_one = oracle)] + pub question: Account<'info, Question>, + pub oracle: Signer<'info>, +} + +impl ResolveQuestion<'_> { + pub fn handle(ctx: Context, args: ResolveQuestionArgs) -> Result<()> { + let question = &mut ctx.accounts.question; + + require_eq!( + args.payout_numerators.len(), + question.num_conditions(), + VaultError::InvalidNumPayoutNumerators + ); + + question.is_resolved = true; + question.payout_denominator = args.payout_numerators.iter().sum(); + question.payout_numerators = args.payout_numerators; + + Ok(()) + } +} diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index 7ef30763..a3a4d232 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -45,6 +45,13 @@ pub mod conditional_vault { InitializeQuestion::handle(ctx, args) } + pub fn resolve_question( + ctx: Context, + args: ResolveQuestionArgs, + ) -> Result<()> { + ResolveQuestion::handle(ctx, args) + } + pub fn initialize_new_conditional_vault<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, InitializeNewConditionalVault<'info>>, ) -> Result<()> { diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 68c5af0c..3b003d50 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -126,8 +126,6 @@ export class ConditionalVaultClient { true ); - // const conditionalReject = Keypair.generate(); - return this.vaultProgram.methods .initializeNewConditionalVault() .accounts({ @@ -162,6 +160,22 @@ export class ConditionalVaultClient { ); } + resolveQuestionIx( + question: PublicKey, + oracle: Keypair, + payoutNumerators: number[] + ) { + return this.vaultProgram.methods + .resolveQuestion({ + payoutNumerators, + }) + .accounts({ + question, + oracle: oracle.publicKey, + }) + .signers([oracle]); + } + async mintConditionalTokens( vault: PublicKey, uiAmount: number, diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index 6ceae845..6456fe95 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -30,6 +30,29 @@ export type ConditionalVault = { } ]; }, + { + name: "resolveQuestion"; + accounts: [ + { + name: "question"; + isMut: true; + isSigner: false; + }, + { + name: "oracle"; + isMut: false; + isSigner: true; + } + ]; + args: [ + { + name: "args"; + type: { + defined: "ResolveQuestionArgs"; + }; + } + ]; + }, { name: "initializeNewConditionalVault"; accounts: [ @@ -579,6 +602,20 @@ export type ConditionalVault = { ]; }; }, + { + name: "ResolveQuestionArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "payoutNumerators"; + type: { + vec: "u32"; + }; + } + ]; + }; + }, { name: "VaultStatus"; type: { @@ -627,6 +664,11 @@ export type ConditionalVault = { code: 6005; name: "InsufficientNumConditions"; msg: "Questions need 2 or more conditions"; + }, + { + code: 6006; + name: "InvalidNumPayoutNumerators"; + msg: "Invalid number of payout numerators"; } ]; }; @@ -663,6 +705,29 @@ export const IDL: ConditionalVault = { }, ], }, + { + name: "resolveQuestion", + accounts: [ + { + name: "question", + isMut: true, + isSigner: false, + }, + { + name: "oracle", + isMut: false, + isSigner: true, + }, + ], + args: [ + { + name: "args", + type: { + defined: "ResolveQuestionArgs", + }, + }, + ], + }, { name: "initializeNewConditionalVault", accounts: [ @@ -1212,6 +1277,20 @@ export const IDL: ConditionalVault = { ], }, }, + { + name: "ResolveQuestionArgs", + type: { + kind: "struct", + fields: [ + { + name: "payoutNumerators", + type: { + vec: "u32", + }, + }, + ], + }, + }, { name: "VaultStatus", type: { @@ -1261,5 +1340,10 @@ export const IDL: ConditionalVault = { name: "InsufficientNumConditions", msg: "Questions need 2 or more conditions", }, + { + code: 6006, + name: "InvalidNumPayoutNumerators", + msg: "Invalid number of payout numerators", + }, ], }; diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index a1bbac3a..e673c25d 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -43,6 +43,7 @@ import { getVaultRevertMintAddr, sha256, } from "@metadaoproject/futarchy"; +import { set } from "@metaplex-foundation/umi/serializers"; const ConditionalVaultIDL: ConditionalVault = require("../target/idl/conditional_vault.json"); export type VaultProgram = anchor.Program; @@ -165,7 +166,7 @@ describe("conditional_vault", async function () { describe("#initialize_new_conditional_vault", async function () { it("initializes vaults", async function () { - const question = questions.shift(); + const question = questions[0]; await vaultClient .initializeNewVaultIx( @@ -197,15 +198,28 @@ describe("conditional_vault", async function () { assert.ok( storedVault.underlyingTokenAccount.equals(vaultUnderlyingTokenAccount) ); - //assert.ok( - // storedVault.conditionalOnFinalizeTokenMint.equals( - // conditionalOnFinalizeMint - // ) - //); - //assert.ok( - // storedVault.conditionalOnRevertTokenMint.equals(conditionalOnRevertMint) - //); + }); + }); + + describe("#resolve_question", async function () { + it("resolves questions", async function () { + const question = questions[0]; + + let storedQuestion = await vaultClient.fetchQuestion(question); + + assert.isFalse(storedQuestion.isResolved); + assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); + assert.equal(storedQuestion.payoutDenominator, 0); + + await vaultClient + .resolveQuestionIx(question, settlementAuthority, [1, 0]) + .rpc(); + + storedQuestion = await vaultClient.fetchQuestion(question); + assert.isTrue(storedQuestion.isResolved); + assert.deepEqual(storedQuestion.payoutNumerators, [1, 0]); + assert.equal(storedQuestion.payoutDenominator, 1); }); }); From 667c7c65e834bca946733e0361a64988125e164f Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Wed, 21 Aug 2024 00:00:00 +0000 Subject: [PATCH 06/52] Clean up tests and start `split_tokens` --- .../initialize_new_conditional_vault.rs | 4 +- .../conditional_vault/src/instructions/mod.rs | 4 + .../src/instructions/new_common.rs | 47 +++++++ .../src/instructions/resolve_question.rs | 2 +- .../src/instructions/split_tokens.rs | 93 +++++++++++++ programs/conditional_vault/src/lib.rs | 12 ++ .../src/state/conditional_vault.rs | 34 +++-- sdk/src/ConditionalVaultClient.ts | 97 ++++++++++++-- sdk/src/types/conditional_vault.ts | 122 +++++++++++++++--- sdk/src/utils/pda.ts | 6 +- tests/conditionalVault.ts | 46 ++++--- 11 files changed, 407 insertions(+), 60 deletions(-) create mode 100644 programs/conditional_vault/src/instructions/new_common.rs create mode 100644 programs/conditional_vault/src/instructions/split_tokens.rs diff --git a/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs b/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs index 65908589..3b50569e 100644 --- a/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs +++ b/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs @@ -8,7 +8,7 @@ pub struct InitializeNewConditionalVault<'info> { #[account( init, payer = payer, - space = 8 + std::mem::size_of::() + (32 * question.num_conditions()), + space = 8 + std::mem::size_of::() + (32 * question.num_outcomes()), seeds = [ b"conditional_vault", question.key().as_ref(), @@ -39,7 +39,7 @@ impl<'info, 'c: 'info> InitializeNewConditionalVault<'info> { let remaining_accs = &mut ctx.remaining_accounts.iter(); - let expected_num_conditional_tokens = ctx.accounts.question.num_conditions(); + let expected_num_conditional_tokens = ctx.accounts.question.num_outcomes(); let mut conditional_token_mints = vec![]; let mint_lamports = Rent::get()?.minimum_balance(Mint::LEN); diff --git a/programs/conditional_vault/src/instructions/mod.rs b/programs/conditional_vault/src/instructions/mod.rs index a65aee90..c4490d66 100644 --- a/programs/conditional_vault/src/instructions/mod.rs +++ b/programs/conditional_vault/src/instructions/mod.rs @@ -10,6 +10,8 @@ pub mod mint_conditional_tokens; pub mod redeem_conditional_tokens_for_underlying_tokens; pub mod settle_conditional_vault; pub mod resolve_question; +pub mod new_common; +pub mod split_tokens; pub use initialize_question::*; pub use add_metadata_to_conditional_tokens::*; @@ -18,3 +20,5 @@ pub use initialize_conditional_vault::*; pub use initialize_new_conditional_vault::*; pub use settle_conditional_vault::*; pub use resolve_question::*; +pub use new_common::*; +pub use split_tokens::*; diff --git a/programs/conditional_vault/src/instructions/new_common.rs b/programs/conditional_vault/src/instructions/new_common.rs new file mode 100644 index 00000000..6204cb47 --- /dev/null +++ b/programs/conditional_vault/src/instructions/new_common.rs @@ -0,0 +1,47 @@ +use super::*; + +#[derive(Accounts)] +pub struct InteractWithNewVault<'info> { + pub question: Account<'info, Question>, + #[account(has_one = question)] + pub vault: Account<'info, NewConditionalVault>, + #[account( + mut, + constraint = vault_underlying_token_account.key() == vault.underlying_token_account @ VaultError::InvalidVaultUnderlyingTokenAccount + )] + pub vault_underlying_token_account: Account<'info, TokenAccount>, + pub authority: Signer<'info>, + #[account( + mut, + token::authority = authority, + token::mint = vault.underlying_token_mint + )] + pub user_underlying_token_account: Account<'info, TokenAccount>, + pub token_program: Program<'info, Token>, +} + +impl<'info, 'c: 'info> InteractWithNewVault<'info> { + pub fn get_mints_and_user_token_accounts( + ctx: &Context<'_, '_, 'c, 'info, Self>, + ) -> Result<(Vec>, Vec>)> { + let remaining_accs = &mut ctx.remaining_accounts.iter(); + + let expected_num_conditional_tokens = ctx.accounts.question.num_outcomes(); + assert_eq!(remaining_accs.len(), expected_num_conditional_tokens * 2); + + let mut conditional_token_mints = vec![]; + let mut user_conditional_token_accounts = vec![]; + + for _ in 0..expected_num_conditional_tokens { + let conditional_token_mint = next_account_info(remaining_accs)?; + conditional_token_mints.push(Account::::try_from(conditional_token_mint)?); + + let user_conditional_token_account = next_account_info(remaining_accs)?; + user_conditional_token_accounts.push(Account::::try_from( + user_conditional_token_account, + )?); + } + + Ok((conditional_token_mints, user_conditional_token_accounts)) + } +} diff --git a/programs/conditional_vault/src/instructions/resolve_question.rs b/programs/conditional_vault/src/instructions/resolve_question.rs index 2754b9c1..9c0c71d5 100644 --- a/programs/conditional_vault/src/instructions/resolve_question.rs +++ b/programs/conditional_vault/src/instructions/resolve_question.rs @@ -19,7 +19,7 @@ impl ResolveQuestion<'_> { require_eq!( args.payout_numerators.len(), - question.num_conditions(), + question.num_outcomes(), VaultError::InvalidNumPayoutNumerators ); diff --git a/programs/conditional_vault/src/instructions/split_tokens.rs b/programs/conditional_vault/src/instructions/split_tokens.rs new file mode 100644 index 00000000..2033ce6c --- /dev/null +++ b/programs/conditional_vault/src/instructions/split_tokens.rs @@ -0,0 +1,93 @@ +use super::*; + +impl<'info, 'c: 'info> InteractWithNewVault<'info> { + pub fn handle_split_tokens(ctx: Context<'_, '_, 'c, 'info, Self>, amount: u64) -> Result<()> { + let accs = &ctx.accounts; + + let (conditional_token_mints, user_conditional_token_accounts) = + Self::get_mints_and_user_token_accounts(&ctx)?; + + let pre_vault_underlying_balance = accs.vault_underlying_token_account.amount; + // let pre_user_conditional_on_finalize_balance = + // accs.user_conditional_on_finalize_token_account.amount; + // let pre_user_conditional_on_revert_balance = + // accs.user_conditional_on_revert_token_account.amount; + // let pre_finalize_mint_supply = accs.conditional_on_finalize_token_mint.supply; + // let pre_revert_mint_supply = accs.conditional_on_revert_token_mint.supply; + + require!( + accs.user_underlying_token_account.amount >= amount, + VaultError::InsufficientUnderlyingTokens + ); + + let vault = &accs.vault; + + let seeds = generate_new_vault_seeds!(vault); + let signer = &[&seeds[..]]; + + token::transfer( + CpiContext::new( + accs.token_program.to_account_info(), + Transfer { + from: accs.user_underlying_token_account.to_account_info(), + to: accs.vault_underlying_token_account.to_account_info(), + authority: accs.authority.to_account_info(), + }, + ), + amount, + )?; + + for (conditional_mint, user_conditional_token_account) in conditional_token_mints + .iter() + .zip(user_conditional_token_accounts.iter()) + { + token::mint_to( + CpiContext::new_with_signer( + accs.token_program.to_account_info(), + MintTo { + mint: conditional_mint.to_account_info(), + to: user_conditional_token_account.to_account_info(), + authority: accs.vault.to_account_info(), + }, + signer, + ), + amount, + )?; + } + + // ctx.accounts + // .user_conditional_on_finalize_token_account + // .reload()?; + // ctx.accounts + // .user_conditional_on_revert_token_account + // .reload()?; + // ctx.accounts.vault_underlying_token_account.reload()?; + // ctx.accounts.conditional_on_finalize_token_mint.reload()?; + // ctx.accounts.conditional_on_revert_token_mint.reload()?; + + // let post_user_conditional_on_finalize_balance = ctx + // .accounts + // .user_conditional_on_finalize_token_account + // .amount; + // let post_user_conditional_on_revert_balance = + // ctx.accounts.user_conditional_on_revert_token_account.amount; + // let post_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; + // let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; + // let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; + + // // Only the paranoid survive ;) + // assert!(post_vault_underlying_balance == pre_vault_underlying_balance + amount); + // assert!( + // post_user_conditional_on_finalize_balance + // == pre_user_conditional_on_finalize_balance + amount + // ); + // assert!( + // post_user_conditional_on_revert_balance + // == pre_user_conditional_on_revert_balance + amount + // ); + // assert!(post_finalize_mint_supply == pre_finalize_mint_supply + amount); + // assert!(post_revert_mint_supply == pre_revert_mint_supply + amount); + + Ok(()) + } +} diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index a3a4d232..7c33a1fb 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -58,6 +58,18 @@ pub mod conditional_vault { InitializeNewConditionalVault::handle(ctx) } + pub fn split_tokens<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, InteractWithNewVault<'info>>, + amount: u64, + ) -> Result<()> { + InteractWithNewVault::handle_split_tokens(ctx, amount) + } + + // pub fn split_tokens() + // merge tokens + + // redeem tokens + pub fn initialize_conditional_vault( ctx: Context, args: InitializeConditionalVaultArgs, diff --git a/programs/conditional_vault/src/state/conditional_vault.rs b/programs/conditional_vault/src/state/conditional_vault.rs index 0d6f3326..f9c5a231 100644 --- a/programs/conditional_vault/src/state/conditional_vault.rs +++ b/programs/conditional_vault/src/state/conditional_vault.rs @@ -14,15 +14,19 @@ pub enum VaultStatus { /// - "Who, if anyone, will be hired?" /// - "How effective will the grant committee deem this grant?" /// -/// Questions have 2 or more conditions. For example, these conditions could be -/// "this proposal passes" and "this proposal fails" or "the committee deems this -/// grant effective" and "the committee deems this grant ineffective." +/// Questions have 2 or more possible outcomes. For a question like "will this +/// proposal pass," the outcomes are "yes" and "no." For a question like "who +/// will be hired," the outcomes could be "Alice," "Bob," and "neither." /// -/// Conditions resolve to a number between 0 and 1. Binary conditions like "will -/// this proposal pass" resolve to exactly 0 or 1. You can also have scalar -/// conditions. For example, the condition "the grant committee deems this grant -/// effective" could resolve to 0.5 if the committee finds the grant partially -/// effective. Once resolved, the sum of all condition resolutions is exactly 1. +/// Outcomes resolve to a number between 0 and 1. Binary questions like "will +/// this proposal pass" have outcomes that resolve to exactly 0 or 1. You can +/// also have questions with scalar outcomes. For example, the question "how +/// effective will the grant committee deem this grant" could have two outcomes: +/// "ineffective" and "effective." If the grant committee deems the grant 70% +/// effective, the "effective" outcome would resolve to 0.7 and the "ineffective" +/// outcome would resolve to 0.3. +/// +/// Once resolved, the sum of all outcome resolutions is exactly 1. #[account] pub struct Question { pub question_id: [u8; 32], @@ -33,7 +37,7 @@ pub struct Question { } impl Question { - pub fn num_conditions(&self) -> usize { + pub fn num_outcomes(&self) -> usize { self.payout_numerators.len() } } @@ -48,6 +52,18 @@ pub struct NewConditionalVault { pub decimals: u8, } +#[macro_export] +macro_rules! generate_new_vault_seeds { + ($vault:expr) => {{ + &[ + b"conditional_vault", + $vault.question.as_ref(), + $vault.underlying_token_mint.as_ref(), + &[$vault.pda_bump], + ] + }}; +} + #[account] pub struct ConditionalVault { pub status: VaultStatus, diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 3b003d50..59bf43ed 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -74,12 +74,10 @@ export class ConditionalVaultClient { } initializeQuestionIx( - questionId: number[], + questionId: Uint8Array, oracle: PublicKey, numConditions: number ) { - //assert(questionId.length == 32); - const [question] = getQuestionAddr( this.vaultProgram.programId, questionId, @@ -89,7 +87,7 @@ export class ConditionalVaultClient { return this.vaultProgram.methods .initializeQuestion({ - questionId, + questionId: Array.from(questionId), oracle, numConditions, }) @@ -98,10 +96,27 @@ export class ConditionalVaultClient { }); } + async initializeQuestion( + questionId: Uint8Array, + oracle: PublicKey, + numConditions: number + ): Promise { + const [question] = getQuestionAddr( + this.vaultProgram.programId, + questionId, + oracle, + numConditions + ); + + await this.initializeQuestionIx(questionId, oracle, numConditions).rpc(); + + return question; + } + initializeNewVaultIx( question: PublicKey, underlyingTokenMint: PublicKey, - numConditions: number + numOutcomes: number ): MethodsBuilder { const [vault] = getVaultAddr( this.vaultProgram.programId, @@ -110,7 +125,7 @@ export class ConditionalVaultClient { ); let conditionalTokenMintAddrs = []; - for (let i = 0; i < numConditions; i++) { + for (let i = 0; i < numOutcomes; i++) { const [conditionalTokenMint] = getConditionalTokenMintAddr( this.vaultProgram.programId, vault, @@ -141,13 +156,6 @@ export class ConditionalVaultClient { vault, underlyingTokenMint ), - // SystemProgram.createAccount({ - // fromPubkey: this.provider.wallet.publicKey, - // newAccountPubkey: conditionalReject.publicKey, - // lamports: 1000000000, - // space: 82, - // programId: TOKEN_PROGRAM_ID, - // }), ]) .remainingAccounts( conditionalTokenMintAddrs.map((conditionalTokenMint) => { @@ -197,6 +205,69 @@ export class ConditionalVaultClient { ); } + splitTokensIx( + vault: PublicKey, + underlyingTokenMint: PublicKey, + amount: BN, + numOutcomes: number + ) { + // const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( + // this.vaultProgram.programId, + // vault + // ); + // const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + // this.vaultProgram.programId, + // vault + // ); + + // let userConditionalOnFinalizeTokenAccount = getAssociatedTokenAddressSync( + // conditionalOnFinalizeTokenMint, + // this.provider.publicKey + // ); + + // let userConditionalOnRevertTokenAccount = getAssociatedTokenAddressSync( + // conditionalOnRevertTokenMint, + // this.provider.publicKey + // ); + + let ix = this.vaultProgram.methods + .splitTokens(amount) + .accounts({ + authority: this.provider.publicKey, + vault, + vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + this.provider.publicKey, + true + ), + // conditionalOnFinalizeTokenMint, + // userConditionalOnFinalizeTokenAccount, + // conditionalOnRevertTokenMint, + // userConditionalOnRevertTokenAccount, + }) + .preInstructions([ + // createAssociatedTokenAccountIdempotentInstruction( + // this.provider.publicKey, + // userConditionalOnFinalizeTokenAccount, + // this.provider.publicKey, + // conditionalOnFinalizeTokenMint + // ), + // createAssociatedTokenAccountIdempotentInstruction( + // this.provider.publicKey, + // userConditionalOnRevertTokenAccount, + // this.provider.publicKey, + // conditionalOnRevertTokenMint + // ), + ]); + + return ix; + } + mintConditionalTokensIx( vault: PublicKey, underlyingTokenMint: PublicKey, diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index 6456fe95..91b3dcbf 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -99,6 +99,47 @@ export type ConditionalVault = { ]; args: []; }, + { + name: "splitTokens"; + accounts: [ + { + name: "question"; + isMut: false; + isSigner: false; + }, + { + name: "vault"; + isMut: false; + isSigner: false; + }, + { + name: "vaultUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "authority"; + isMut: false; + isSigner: true; + }, + { + name: "userUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "amount"; + type: "u64"; + } + ]; + }, { name: "initializeConditionalVault"; accounts: [ @@ -423,15 +464,19 @@ export type ConditionalVault = { '- "Who, if anyone, will be hired?"', '- "How effective will the grant committee deem this grant?"', "", - "Questions have 2 or more conditions. For example, these conditions could be", - '"this proposal passes" and "this proposal fails" or "the committee deems this', - 'grant effective" and "the committee deems this grant ineffective."', + 'Questions have 2 or more possible outcomes. For a question like "will this', + 'proposal pass," the outcomes are "yes" and "no." For a question like "who', + 'will be hired," the outcomes could be "Alice," "Bob," and "neither."', + "", + 'Outcomes resolve to a number between 0 and 1. Binary questions like "will', + 'this proposal pass" have outcomes that resolve to exactly 0 or 1. You can', + 'also have questions with scalar outcomes. For example, the question "how', + 'effective will the grant committee deem this grant" could have two outcomes:', + '"ineffective" and "effective." If the grant committee deems the grant 70%', + 'effective, the "effective" outcome would resolve to 0.7 and the "ineffective"', + "outcome would resolve to 0.3.", "", - 'Conditions resolve to a number between 0 and 1. Binary conditions like "will', - 'this proposal pass" resolve to exactly 0 or 1. You can also have scalar', - 'conditions. For example, the condition "the grant committee deems this grant', - 'effective" could resolve to 0.5 if the committee finds the grant partially', - "effective. Once resolved, the sum of all condition resolutions is exactly 1." + "Once resolved, the sum of all outcome resolutions is exactly 1." ]; type: { kind: "struct"; @@ -774,6 +819,47 @@ export const IDL: ConditionalVault = { ], args: [], }, + { + name: "splitTokens", + accounts: [ + { + name: "question", + isMut: false, + isSigner: false, + }, + { + name: "vault", + isMut: false, + isSigner: false, + }, + { + name: "vaultUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "authority", + isMut: false, + isSigner: true, + }, + { + name: "userUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "amount", + type: "u64", + }, + ], + }, { name: "initializeConditionalVault", accounts: [ @@ -1098,15 +1184,19 @@ export const IDL: ConditionalVault = { '- "Who, if anyone, will be hired?"', '- "How effective will the grant committee deem this grant?"', "", - "Questions have 2 or more conditions. For example, these conditions could be", - '"this proposal passes" and "this proposal fails" or "the committee deems this', - 'grant effective" and "the committee deems this grant ineffective."', + 'Questions have 2 or more possible outcomes. For a question like "will this', + 'proposal pass," the outcomes are "yes" and "no." For a question like "who', + 'will be hired," the outcomes could be "Alice," "Bob," and "neither."', + "", + 'Outcomes resolve to a number between 0 and 1. Binary questions like "will', + 'this proposal pass" have outcomes that resolve to exactly 0 or 1. You can', + 'also have questions with scalar outcomes. For example, the question "how', + 'effective will the grant committee deem this grant" could have two outcomes:', + '"ineffective" and "effective." If the grant committee deems the grant 70%', + 'effective, the "effective" outcome would resolve to 0.7 and the "ineffective"', + "outcome would resolve to 0.3.", "", - 'Conditions resolve to a number between 0 and 1. Binary conditions like "will', - 'this proposal pass" resolve to exactly 0 or 1. You can also have scalar', - 'conditions. For example, the condition "the grant committee deems this grant', - 'effective" could resolve to 0.5 if the committee finds the grant partially', - "effective. Once resolved, the sum of all condition resolutions is exactly 1.", + "Once resolved, the sum of all outcome resolutions is exactly 1.", ], type: { kind: "struct", diff --git a/sdk/src/utils/pda.ts b/sdk/src/utils/pda.ts index b899709d..9f2d4bbb 100644 --- a/sdk/src/utils/pda.ts +++ b/sdk/src/utils/pda.ts @@ -13,10 +13,14 @@ import { MPL_TOKEN_METADATA_PROGRAM_ID } from "../constants"; export const getQuestionAddr = ( programId: PublicKey, - questionId: number[], + questionId: Uint8Array, oracle: PublicKey, numConditions: number ) => { + if (questionId.length != 32) { + throw new Error("questionId must be 32 bytes"); + } + return PublicKey.findProgramAddressSync( [ utils.bytes.utf8.encode("question"), diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index e673c25d..38b022c5 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -69,7 +69,6 @@ describe("conditional_vault", async function () { let provider: anchor.Provider; let vaultClient: ConditionalVaultClient; - let questions: PublicKey[] = []; let vault: PublicKey; let proposal: PublicKey; let vaultUnderlyingTokenAccount: anchor.web3.PublicKey; @@ -146,11 +145,11 @@ describe("conditional_vault", async function () { }); describe("#initialize_question", async function () { - it("initializes questions", async function () { + it("initializes 2-outcome questions", async function () { let questionId = sha256(new Uint8Array([1, 2, 3])); await vaultClient - .initializeQuestionIx([...questionId], settlementAuthority.publicKey, 2) + .initializeQuestionIx(questionId, settlementAuthority.publicKey, 2) .rpc(); let [question] = getQuestionAddr( @@ -160,34 +159,38 @@ describe("conditional_vault", async function () { 2 ); - questions.push(question); + const storedQuestion = await vaultClient.fetchQuestion(question); + assert.deepEqual(storedQuestion.questionId, Array.from(questionId)); + assert.ok(storedQuestion.oracle.equals(settlementAuthority.publicKey)); + assert.isFalse(storedQuestion.isResolved); + assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); + assert.equal(storedQuestion.payoutDenominator, 0); }); }); describe("#initialize_new_conditional_vault", async function () { - it("initializes vaults", async function () { - const question = questions[0]; + let question: PublicKey; + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([3, 2, 1])); + question = await vaultClient.initializeQuestion(questionId, settlementAuthority.publicKey, 2); + }); + + it("initializes vaults", async function () { await vaultClient - .initializeNewVaultIx( - question, - underlyingTokenMint, - 2 - ) + .initializeNewVaultIx(question, underlyingTokenMint, 2) .rpc(); const [vault] = getVaultAddr( vaultProgram.programId, question, - underlyingTokenMint, + underlyingTokenMint ); - //console.log(await vaultProgram.account.newConditionalVault.fetch(vault)); const storedVault = await vaultClient.fetchVault(vault); console.log(storedVault); - assert.ok( - storedVault.question.equals(question) - ); + assert.ok(storedVault.question.equals(question)); assert.ok(storedVault.underlyingTokenMint.equals(underlyingTokenMint)); const vaultUnderlyingTokenAccount = token.getAssociatedTokenAddressSync( @@ -202,9 +205,16 @@ describe("conditional_vault", async function () { }); describe("#resolve_question", async function () { - it("resolves questions", async function () { - const question = questions[0]; + let question: PublicKey; + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([4, 2, 1])); + + question = await vaultClient.initializeQuestion(questionId, settlementAuthority.publicKey, 2); + }); + + it("resolves questions", async function () { let storedQuestion = await vaultClient.fetchQuestion(question); assert.isFalse(storedQuestion.isResolved); From 59ed2b521bc455df2f504be9434457e139e824d2 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Wed, 21 Aug 2024 00:00:00 +0000 Subject: [PATCH 07/52] Add multiple test cases for initializing conditional vault --- sdk/src/ConditionalVaultClient.ts | 1 - tests/conditionalVault.ts | 109 ++++++++++++++++++++---------- 2 files changed, 75 insertions(+), 35 deletions(-) diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 59bf43ed..970ae6f4 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -133,7 +133,6 @@ export class ConditionalVaultClient { ); conditionalTokenMintAddrs.push(conditionalTokenMint); } - console.log(conditionalTokenMintAddrs); const vaultUnderlyingTokenAccount = getAssociatedTokenAddressSync( underlyingTokenMint, diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index 38b022c5..a993f14d 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -27,6 +27,7 @@ import { createAssociatedTokenAccount, createMint, getAccount, + getMint, mintTo, } from "spl-token-bankrun"; @@ -37,6 +38,7 @@ import { expectError } from "./utils/utils"; import { CONDITIONAL_VAULT_PROGRAM_ID, ConditionalVaultClient, + getConditionalTokenMintAddr, getQuestionAddr, getVaultAddr, getVaultFinalizeMintAddr, @@ -169,38 +171,74 @@ describe("conditional_vault", async function () { }); describe("#initialize_new_conditional_vault", async function () { - let question: PublicKey; - - beforeEach(async function () { - let questionId = sha256(new Uint8Array([3, 2, 1])); - - question = await vaultClient.initializeQuestion(questionId, settlementAuthority.publicKey, 2); - }); - - it("initializes vaults", async function () { - await vaultClient - .initializeNewVaultIx(question, underlyingTokenMint, 2) - .rpc(); - - const [vault] = getVaultAddr( - vaultProgram.programId, - question, - underlyingTokenMint - ); - - const storedVault = await vaultClient.fetchVault(vault); - console.log(storedVault); - assert.ok(storedVault.question.equals(question)); - assert.ok(storedVault.underlyingTokenMint.equals(underlyingTokenMint)); - - const vaultUnderlyingTokenAccount = token.getAssociatedTokenAddressSync( - underlyingTokenMint, - vault, - true - ); - assert.ok( - storedVault.underlyingTokenAccount.equals(vaultUnderlyingTokenAccount) - ); + const testCases = [ + { name: "2-outcome question", idArray: [3, 2, 1], outcomes: 2 }, + { name: "3-outcome question", idArray: [4, 5, 6], outcomes: 3 }, + { name: "4-outcome question", idArray: [7, 8, 9], outcomes: 4 }, + ]; + + testCases.forEach(({ name, idArray, outcomes }) => { + describe(name, function () { + let question: PublicKey; + + beforeEach(async function () { + let questionId = sha256(new Uint8Array(idArray)); + question = await vaultClient.initializeQuestion( + questionId, + settlementAuthority.publicKey, + outcomes + ); + }); + + it("initializes vaults correctly", async function () { + await vaultClient + .initializeNewVaultIx(question, underlyingTokenMint, outcomes) + .rpc(); + + const [vault, pdaBump] = getVaultAddr( + vaultProgram.programId, + question, + underlyingTokenMint + ); + + const storedVault = await vaultClient.fetchVault(vault); + assert.ok(storedVault.question.equals(question)); + assert.ok( + storedVault.underlyingTokenMint.equals(underlyingTokenMint) + ); + + const vaultUnderlyingTokenAccount = + token.getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ); + assert.ok( + storedVault.underlyingTokenAccount.equals( + vaultUnderlyingTokenAccount + ) + ); + const storedConditionalTokenMints = storedVault.conditionalTokenMints; + storedConditionalTokenMints.forEach((mint, i) => { + const [expectedMint] = getConditionalTokenMintAddr( + vaultProgram.programId, + vault, + i + ); + assert.ok(mint.equals(expectedMint)); + }); + assert.equal(storedVault.pdaBump, pdaBump); + assert.equal(storedVault.decimals, 8); + + for (let mint of storedConditionalTokenMints) { + const storedMint = await getMint(banksClient, mint); + assert.ok(storedMint.mintAuthority.equals(vault)); + assert.equal(storedMint.supply.toString(), "0"); + assert.equal(storedMint.decimals, 8); + assert.isNull(storedMint.freezeAuthority); + } + }); + }); }); }); @@ -210,10 +248,13 @@ describe("conditional_vault", async function () { beforeEach(async function () { let questionId = sha256(new Uint8Array([4, 2, 1])); - question = await vaultClient.initializeQuestion(questionId, settlementAuthority.publicKey, 2); + question = await vaultClient.initializeQuestion( + questionId, + settlementAuthority.publicKey, + 2 + ); }); - it("resolves questions", async function () { let storedQuestion = await vaultClient.fetchQuestion(question); From 30cc743173ce57292e20f3a6132badda9dcddd8b Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Wed, 21 Aug 2024 00:00:00 +0000 Subject: [PATCH 08/52] Add `split_tokens` --- programs/conditional_vault/src/error.rs | 6 ++ .../src/instructions/new_common.rs | 24 +++-- sdk/src/ConditionalVaultClient.ts | 91 ++++++++++++++----- sdk/src/types/conditional_vault.ts | 30 ++++++ tests/conditionalVault.ts | 61 +++++++++++++ 5 files changed, 182 insertions(+), 30 deletions(-) diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index b963d223..a7fd0eb7 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -16,4 +16,10 @@ pub enum VaultError { InsufficientNumConditions, #[msg("Invalid number of payout numerators")] InvalidNumPayoutNumerators, + #[msg("Client needs to pass in the list of conditional mints for a vault followed by the user's token accounts for those tokens")] + InvalidConditionals, + #[msg("Unable to deserialize a conditional token mint")] + BadConditionalMint, + #[msg("Unable to deserialize a conditional token account")] + BadConditionalTokenAccount, } diff --git a/programs/conditional_vault/src/instructions/new_common.rs b/programs/conditional_vault/src/instructions/new_common.rs index 6204cb47..91d2af91 100644 --- a/programs/conditional_vault/src/instructions/new_common.rs +++ b/programs/conditional_vault/src/instructions/new_common.rs @@ -24,22 +24,34 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { pub fn get_mints_and_user_token_accounts( ctx: &Context<'_, '_, 'c, 'info, Self>, ) -> Result<(Vec>, Vec>)> { + msg!("HelloWorld"); let remaining_accs = &mut ctx.remaining_accounts.iter(); + msg!("{:?}", remaining_accs); let expected_num_conditional_tokens = ctx.accounts.question.num_outcomes(); - assert_eq!(remaining_accs.len(), expected_num_conditional_tokens * 2); + require_eq!( + remaining_accs.len(), + expected_num_conditional_tokens * 2, + VaultError::InvalidConditionals + ); let mut conditional_token_mints = vec![]; let mut user_conditional_token_accounts = vec![]; for _ in 0..expected_num_conditional_tokens { let conditional_token_mint = next_account_info(remaining_accs)?; - conditional_token_mints.push(Account::::try_from(conditional_token_mint)?); - + conditional_token_mints.push( + Account::::try_from(conditional_token_mint) + .or(Err(VaultError::BadConditionalMint))?, + ); + } + + for _ in 0..expected_num_conditional_tokens { let user_conditional_token_account = next_account_info(remaining_accs)?; - user_conditional_token_accounts.push(Account::::try_from( - user_conditional_token_account, - )?); + user_conditional_token_accounts.push( + Account::::try_from(user_conditional_token_account) + .or(Err(VaultError::BadConditionalTokenAccount))?, + ); } Ok((conditional_token_mints, user_conditional_token_accounts)) diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 970ae6f4..d627ea1f 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -167,6 +167,26 @@ export class ConditionalVaultClient { ); } + async initializeNewVault( + question: PublicKey, + underlyingTokenMint: PublicKey, + numOutcomes: number + ): Promise { + const [vault] = getVaultAddr( + this.vaultProgram.programId, + question, + underlyingTokenMint + ); + + await this.initializeNewVaultIx( + question, + underlyingTokenMint, + numOutcomes + ).rpc(); + + return vault; + } + resolveQuestionIx( question: PublicKey, oracle: Keypair, @@ -205,33 +225,37 @@ export class ConditionalVaultClient { } splitTokensIx( + question: PublicKey, vault: PublicKey, underlyingTokenMint: PublicKey, amount: BN, numOutcomes: number ) { - // const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( - // this.vaultProgram.programId, - // vault - // ); - // const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( - // this.vaultProgram.programId, - // vault - // ); - - // let userConditionalOnFinalizeTokenAccount = getAssociatedTokenAddressSync( - // conditionalOnFinalizeTokenMint, - // this.provider.publicKey - // ); - - // let userConditionalOnRevertTokenAccount = getAssociatedTokenAddressSync( - // conditionalOnRevertTokenMint, - // this.provider.publicKey - // ); + let conditionalTokenMintAddrs = []; + for (let i = 0; i < numOutcomes; i++) { + const [conditionalTokenMint] = getConditionalTokenMintAddr( + this.vaultProgram.programId, + vault, + i + ); + conditionalTokenMintAddrs.push(conditionalTokenMint); + } + + let userConditionalAccounts = []; + for (let conditionalTokenMint of conditionalTokenMintAddrs) { + userConditionalAccounts.push( + getAssociatedTokenAddressSync( + conditionalTokenMint, + this.provider.publicKey, + true + ) + ); + } let ix = this.vaultProgram.methods .splitTokens(amount) .accounts({ + question, authority: this.provider.publicKey, vault, vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( @@ -244,12 +268,20 @@ export class ConditionalVaultClient { this.provider.publicKey, true ), - // conditionalOnFinalizeTokenMint, - // userConditionalOnFinalizeTokenAccount, - // conditionalOnRevertTokenMint, - // userConditionalOnRevertTokenAccount, }) - .preInstructions([ + .preInstructions( + conditionalTokenMintAddrs.map((conditionalTokenMint) => { + return createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + getAssociatedTokenAddressSync( + conditionalTokenMint, + this.provider.publicKey + ), + this.provider.publicKey, + conditionalTokenMint + ); + }) + // createAssociatedTokenAccountIdempotentInstruction( // this.provider.publicKey, // userConditionalOnFinalizeTokenAccount, @@ -262,7 +294,18 @@ export class ConditionalVaultClient { // this.provider.publicKey, // conditionalOnRevertTokenMint // ), - ]); + ) + .remainingAccounts( + conditionalTokenMintAddrs + .concat(userConditionalAccounts) + .map((conditionalTokenMint) => { + return { + pubkey: conditionalTokenMint, + isWritable: true, + isSigner: false, + }; + }) + ); return ix; } diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index 91b3dcbf..6808d0a4 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -714,6 +714,21 @@ export type ConditionalVault = { code: 6006; name: "InvalidNumPayoutNumerators"; msg: "Invalid number of payout numerators"; + }, + { + code: 6007; + name: "InvalidConditionals"; + msg: "Client needs to pass in the list of conditional mints for a vault followed by the user's token accounts for those tokens"; + }, + { + code: 6008; + name: "BadConditionalMint"; + msg: "Unable to deserialize a conditional token mint"; + }, + { + code: 6009; + name: "BadConditionalTokenAccount"; + msg: "Unable to deserialize a conditional token account"; } ]; }; @@ -1435,5 +1450,20 @@ export const IDL: ConditionalVault = { name: "InvalidNumPayoutNumerators", msg: "Invalid number of payout numerators", }, + { + code: 6007, + name: "InvalidConditionals", + msg: "Client needs to pass in the list of conditional mints for a vault followed by the user's token accounts for those tokens", + }, + { + code: 6008, + name: "BadConditionalMint", + msg: "Unable to deserialize a conditional token mint", + }, + { + code: 6009, + name: "BadConditionalTokenAccount", + msg: "Unable to deserialize a conditional token account", + }, ], }; diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index a993f14d..6322be68 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -274,6 +274,67 @@ describe("conditional_vault", async function () { }); }); + describe("#split_tokens", async function () { + let question: PublicKey; + let vault: PublicKey; + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([5, 2, 1])); + + question = await vaultClient.initializeQuestion( + questionId, + settlementAuthority.publicKey, + 2 + ); + vault = await vaultClient.initializeNewVault( + question, + underlyingTokenMint, + 2 + ); + + let userUnderlyingTokenAccount = await createAssociatedTokenAccount( + banksClient, + payer, + underlyingTokenMint, + payer.publicKey + ); + + await mintTo( + banksClient, + payer, + underlyingTokenMint, + userUnderlyingTokenAccount, + underlyingMintAuthority, + 10_000_000_000n + ); + }); + + it("splits tokens", async function () { + await vaultClient + .splitTokensIx(question, vault, underlyingTokenMint, new BN(1000), 2) + .rpc(); + + const storedVault = await vaultClient.fetchVault(vault); + + let storedVaultUnderlyingAcc = await getAccount( + banksClient, + storedVault.underlyingTokenAccount + ); + assert.equal(storedVaultUnderlyingAcc.amount.toString(), "1000"); + + const storedConditionalTokenMints = storedVault.conditionalTokenMints; + for (let mint of storedConditionalTokenMints) { + let storedMint = await getMint(banksClient, mint); + assert.equal(storedMint.supply.toString(), "1000"); + let storedTokenAcc = await getAccount( + banksClient, + token.getAssociatedTokenAddressSync(mint, payer.publicKey) + ); + assert.equal(storedTokenAcc.amount.toString(), "1000"); + } + }); + }); + describe("#initialize_conditional_vault", async function () { it("initializes vaults", async function () { await vaultClient From c8a377f739f355a35a1420b02d5a135c584eb1bc Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Thu, 22 Aug 2024 00:00:00 +0000 Subject: [PATCH 09/52] Add `merge_tokens` --- .../src/instructions/merge_tokens.rs | 114 ++++++++++++++++++ .../conditional_vault/src/instructions/mod.rs | 2 + .../src/instructions/redeem_tokens.rs | 114 ++++++++++++++++++ programs/conditional_vault/src/lib.rs | 13 ++ sdk/src/ConditionalVaultClient.ts | 84 +++++++++++-- sdk/src/types/conditional_vault.ts | 82 +++++++++++++ tests/conditionalVault.ts | 52 ++++++++ 7 files changed, 449 insertions(+), 12 deletions(-) create mode 100644 programs/conditional_vault/src/instructions/merge_tokens.rs create mode 100644 programs/conditional_vault/src/instructions/redeem_tokens.rs diff --git a/programs/conditional_vault/src/instructions/merge_tokens.rs b/programs/conditional_vault/src/instructions/merge_tokens.rs new file mode 100644 index 00000000..65fe210a --- /dev/null +++ b/programs/conditional_vault/src/instructions/merge_tokens.rs @@ -0,0 +1,114 @@ +use super::*; + +impl<'info, 'c: 'info> InteractWithNewVault<'info> { + pub fn handle_merge_tokens(ctx: Context<'_, '_, 'c, 'info, Self>, amount: u64) -> Result<()> { + let accs = &ctx.accounts; + + let (conditional_token_mints, user_conditional_token_accounts) = + Self::get_mints_and_user_token_accounts(&ctx)?; + + + let vault = &accs.vault; + + // Store Pre-operation Balances + // let pre_user_conditional_on_finalize_balance = ctx + // .accounts + // .user_conditional_on_finalize_token_account + // .amount; + // let pre_user_conditional_on_revert_balance = + // ctx.accounts.user_conditional_on_revert_token_account.amount; + // let pre_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; + // let pre_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; + // let pre_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; + + let seeds = generate_new_vault_seeds!(vault); + let signer = &[&seeds[..]]; + + // burn `amount` from both token accounts + // for (conditional_mint, user_conditional_token_account) in [ + // ( + // &accs.conditional_on_finalize_token_mint, + // &accs.user_conditional_on_finalize_token_account, + // ), + // ( + // &accs.conditional_on_revert_token_mint, + // &accs.user_conditional_on_revert_token_account, + // ), + // ] { + for (conditional_mint, user_conditional_token_account) in conditional_token_mints + .iter() + .zip(user_conditional_token_accounts.iter()) + { + token::burn( + CpiContext::new( + accs.token_program.to_account_info(), + Burn { + mint: conditional_mint.to_account_info(), + from: user_conditional_token_account.to_account_info(), + authority: accs.authority.to_account_info(), + }, + ), + amount, + )?; + } + + // Transfer `amount` from vault to user + token::transfer( + CpiContext::new_with_signer( + accs.token_program.to_account_info(), + Transfer { + from: accs.vault_underlying_token_account.to_account_info(), + to: accs.user_underlying_token_account.to_account_info(), + authority: accs.vault.to_account_info(), + }, + signer, + ), + amount, + )?; + + // Reload Accounts to Reflect Changes + // ctx.accounts + // .user_conditional_on_finalize_token_account + // .reload()?; + // ctx.accounts + // .user_conditional_on_revert_token_account + // .reload()?; + // ctx.accounts.vault_underlying_token_account.reload()?; + // ctx.accounts.conditional_on_finalize_token_mint.reload()?; + // ctx.accounts.conditional_on_revert_token_mint.reload()?; + + // // Check post-operation balances + // let post_user_conditional_on_finalize_balance = ctx + // .accounts + // .user_conditional_on_finalize_token_account + // .amount; + // let post_user_conditional_on_revert_balance = + // ctx.accounts.user_conditional_on_revert_token_account.amount; + // let post_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; + // let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; + // let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; + + // Check that the user's conditional token balances are unchanged (since we're not necessarily burning all tokens) + // require_eq!( + // post_user_conditional_on_finalize_balance, + // pre_user_conditional_on_finalize_balance - amount + // ); + // require_eq!( + // post_user_conditional_on_revert_balance, + // pre_user_conditional_on_revert_balance - amount + // ); + + // // Check that the mint supplies have been reduced by the burned amounts + // require_eq!(post_finalize_mint_supply, pre_finalize_mint_supply - amount); + // require_eq!(post_revert_mint_supply, pre_revert_mint_supply - amount); + + // // Check that the vault's underlying balance has been reduced by the transferred amount + // require_eq!( + // post_vault_underlying_balance, + // pre_vault_underlying_balance - amount + // ); + + + Ok(()) + } +} diff --git a/programs/conditional_vault/src/instructions/mod.rs b/programs/conditional_vault/src/instructions/mod.rs index c4490d66..467a518b 100644 --- a/programs/conditional_vault/src/instructions/mod.rs +++ b/programs/conditional_vault/src/instructions/mod.rs @@ -12,6 +12,7 @@ pub mod settle_conditional_vault; pub mod resolve_question; pub mod new_common; pub mod split_tokens; +pub mod merge_tokens; pub use initialize_question::*; pub use add_metadata_to_conditional_tokens::*; @@ -22,3 +23,4 @@ pub use settle_conditional_vault::*; pub use resolve_question::*; pub use new_common::*; pub use split_tokens::*; +pub use merge_tokens::*; diff --git a/programs/conditional_vault/src/instructions/redeem_tokens.rs b/programs/conditional_vault/src/instructions/redeem_tokens.rs new file mode 100644 index 00000000..65fe210a --- /dev/null +++ b/programs/conditional_vault/src/instructions/redeem_tokens.rs @@ -0,0 +1,114 @@ +use super::*; + +impl<'info, 'c: 'info> InteractWithNewVault<'info> { + pub fn handle_merge_tokens(ctx: Context<'_, '_, 'c, 'info, Self>, amount: u64) -> Result<()> { + let accs = &ctx.accounts; + + let (conditional_token_mints, user_conditional_token_accounts) = + Self::get_mints_and_user_token_accounts(&ctx)?; + + + let vault = &accs.vault; + + // Store Pre-operation Balances + // let pre_user_conditional_on_finalize_balance = ctx + // .accounts + // .user_conditional_on_finalize_token_account + // .amount; + // let pre_user_conditional_on_revert_balance = + // ctx.accounts.user_conditional_on_revert_token_account.amount; + // let pre_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; + // let pre_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; + // let pre_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; + + let seeds = generate_new_vault_seeds!(vault); + let signer = &[&seeds[..]]; + + // burn `amount` from both token accounts + // for (conditional_mint, user_conditional_token_account) in [ + // ( + // &accs.conditional_on_finalize_token_mint, + // &accs.user_conditional_on_finalize_token_account, + // ), + // ( + // &accs.conditional_on_revert_token_mint, + // &accs.user_conditional_on_revert_token_account, + // ), + // ] { + for (conditional_mint, user_conditional_token_account) in conditional_token_mints + .iter() + .zip(user_conditional_token_accounts.iter()) + { + token::burn( + CpiContext::new( + accs.token_program.to_account_info(), + Burn { + mint: conditional_mint.to_account_info(), + from: user_conditional_token_account.to_account_info(), + authority: accs.authority.to_account_info(), + }, + ), + amount, + )?; + } + + // Transfer `amount` from vault to user + token::transfer( + CpiContext::new_with_signer( + accs.token_program.to_account_info(), + Transfer { + from: accs.vault_underlying_token_account.to_account_info(), + to: accs.user_underlying_token_account.to_account_info(), + authority: accs.vault.to_account_info(), + }, + signer, + ), + amount, + )?; + + // Reload Accounts to Reflect Changes + // ctx.accounts + // .user_conditional_on_finalize_token_account + // .reload()?; + // ctx.accounts + // .user_conditional_on_revert_token_account + // .reload()?; + // ctx.accounts.vault_underlying_token_account.reload()?; + // ctx.accounts.conditional_on_finalize_token_mint.reload()?; + // ctx.accounts.conditional_on_revert_token_mint.reload()?; + + // // Check post-operation balances + // let post_user_conditional_on_finalize_balance = ctx + // .accounts + // .user_conditional_on_finalize_token_account + // .amount; + // let post_user_conditional_on_revert_balance = + // ctx.accounts.user_conditional_on_revert_token_account.amount; + // let post_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; + // let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; + // let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; + + // Check that the user's conditional token balances are unchanged (since we're not necessarily burning all tokens) + // require_eq!( + // post_user_conditional_on_finalize_balance, + // pre_user_conditional_on_finalize_balance - amount + // ); + // require_eq!( + // post_user_conditional_on_revert_balance, + // pre_user_conditional_on_revert_balance - amount + // ); + + // // Check that the mint supplies have been reduced by the burned amounts + // require_eq!(post_finalize_mint_supply, pre_finalize_mint_supply - amount); + // require_eq!(post_revert_mint_supply, pre_revert_mint_supply - amount); + + // // Check that the vault's underlying balance has been reduced by the transferred amount + // require_eq!( + // post_vault_underlying_balance, + // pre_vault_underlying_balance - amount + // ); + + + Ok(()) + } +} diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index 7c33a1fb..54be5511 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -65,6 +65,19 @@ pub mod conditional_vault { InteractWithNewVault::handle_split_tokens(ctx, amount) } + pub fn merge_tokens<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, InteractWithNewVault<'info>>, + amount: u64, + ) -> Result<()> { + InteractWithNewVault::handle_merge_tokens(ctx, amount) + } + + // pub fn redeem_tokens<'c: 'info, 'info>( + // ctx: Context<'_, '_, 'c, 'info, InteractWithNewVault<'info>>, + // ) -> Result<()> { + // InteractWithNewVault::handle_redeem_tokens(ctx) + // } + // pub fn split_tokens() // merge tokens diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index d627ea1f..44082e61 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -281,19 +281,79 @@ export class ConditionalVaultClient { conditionalTokenMint ); }) + ) + .remainingAccounts( + conditionalTokenMintAddrs + .concat(userConditionalAccounts) + .map((conditionalTokenMint) => { + return { + pubkey: conditionalTokenMint, + isWritable: true, + isSigner: false, + }; + }) + ); + + return ix; + } + + mergeTokensIx( + question: PublicKey, + vault: PublicKey, + underlyingTokenMint: PublicKey, + amount: BN, + numOutcomes: number + ) { + let conditionalTokenMintAddrs = []; + for (let i = 0; i < numOutcomes; i++) { + const [conditionalTokenMint] = getConditionalTokenMintAddr( + this.vaultProgram.programId, + vault, + i + ); + conditionalTokenMintAddrs.push(conditionalTokenMint); + } + + let userConditionalAccounts = []; + for (let conditionalTokenMint of conditionalTokenMintAddrs) { + userConditionalAccounts.push( + getAssociatedTokenAddressSync( + conditionalTokenMint, + this.provider.publicKey, + true + ) + ); + } - // createAssociatedTokenAccountIdempotentInstruction( - // this.provider.publicKey, - // userConditionalOnFinalizeTokenAccount, - // this.provider.publicKey, - // conditionalOnFinalizeTokenMint - // ), - // createAssociatedTokenAccountIdempotentInstruction( - // this.provider.publicKey, - // userConditionalOnRevertTokenAccount, - // this.provider.publicKey, - // conditionalOnRevertTokenMint - // ), + let ix = this.vaultProgram.methods + .mergeTokens(amount) + .accounts({ + question, + authority: this.provider.publicKey, + vault, + vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + this.provider.publicKey, + true + ), + }) + .preInstructions( + conditionalTokenMintAddrs.map((conditionalTokenMint) => { + return createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + getAssociatedTokenAddressSync( + conditionalTokenMint, + this.provider.publicKey + ), + this.provider.publicKey, + conditionalTokenMint + ); + }) ) .remainingAccounts( conditionalTokenMintAddrs diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index 6808d0a4..ed578174 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -140,6 +140,47 @@ export type ConditionalVault = { } ]; }, + { + name: "mergeTokens"; + accounts: [ + { + name: "question"; + isMut: false; + isSigner: false; + }, + { + name: "vault"; + isMut: false; + isSigner: false; + }, + { + name: "vaultUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "authority"; + isMut: false; + isSigner: true; + }, + { + name: "userUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "amount"; + type: "u64"; + } + ]; + }, { name: "initializeConditionalVault"; accounts: [ @@ -875,6 +916,47 @@ export const IDL: ConditionalVault = { }, ], }, + { + name: "mergeTokens", + accounts: [ + { + name: "question", + isMut: false, + isSigner: false, + }, + { + name: "vault", + isMut: false, + isSigner: false, + }, + { + name: "vaultUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "authority", + isMut: false, + isSigner: true, + }, + { + name: "userUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "amount", + type: "u64", + }, + ], + }, { name: "initializeConditionalVault", accounts: [ diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index 6322be68..9f57a96f 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -335,6 +335,58 @@ describe("conditional_vault", async function () { }); }); + describe("#merge_tokens", async function () { + let question: PublicKey; + let vault: PublicKey; + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([9, 2, 1])); + + question = await vaultClient.initializeQuestion( + questionId, + settlementAuthority.publicKey, + 2 + ); + vault = await vaultClient.initializeNewVault( + question, + underlyingTokenMint, + 2 + ); + + // let userUnderlyingTokenAccount = await createAssociatedTokenAccount( + // banksClient, + // payer, + // underlyingTokenMint, + // payer.publicKey + // ); + + // await mintTo( + // banksClient, + // payer, + // underlyingTokenMint, + // userUnderlyingTokenAccount, + // underlyingMintAuthority, + // 10_000_000_000n + // ); + + await vaultClient + .splitTokensIx(question, vault, underlyingTokenMint, new BN(1000), 2) + .rpc(); + + }); + + it("merges tokens", async function () { + const balanceBefore = await getAccount(banksClient, token.getAssociatedTokenAddressSync(underlyingTokenMint, payer.publicKey)).then(acc => acc.amount); + await vaultClient + .mergeTokensIx(question, vault, underlyingTokenMint, new BN(600), 2) + .rpc(); + const balanceAfter = await getAccount(banksClient, token.getAssociatedTokenAddressSync(underlyingTokenMint, payer.publicKey)).then(acc => acc.amount); + + assert.isTrue(balanceAfter > balanceBefore); + assert.equal(balanceAfter - balanceBefore, 600n); + }); + }); + describe("#initialize_conditional_vault", async function () { it("initializes vaults", async function () { await vaultClient From 595662818f3caf6cf03ed548b4f3c9fb7c754042 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Thu, 22 Aug 2024 00:00:00 +0000 Subject: [PATCH 10/52] Add `redeem_tokens` --- programs/conditional_vault/src/error.rs | 2 + .../src/instructions/initialize_question.rs | 1 - .../conditional_vault/src/instructions/mod.rs | 2 + .../src/instructions/redeem_tokens.rs | 121 +++++++++++------- .../src/instructions/resolve_question.rs | 7 +- programs/conditional_vault/src/lib.rs | 11 +- .../src/state/conditional_vault.rs | 5 +- sdk/package.json | 3 +- sdk/src/ConditionalVaultClient.ts | 73 +++++++++++ sdk/src/types/conditional_vault.ts | 90 +++++++++++-- tests/conditionalVault.ts | 81 +++++++++--- 11 files changed, 310 insertions(+), 86 deletions(-) diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index a7fd0eb7..cc78af9b 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -22,4 +22,6 @@ pub enum VaultError { BadConditionalMint, #[msg("Unable to deserialize a conditional token account")] BadConditionalTokenAccount, + #[msg("Payouts must sum to 1 or more")] + PayoutZero, } diff --git a/programs/conditional_vault/src/instructions/initialize_question.rs b/programs/conditional_vault/src/instructions/initialize_question.rs index 82a89f34..3824e8ce 100644 --- a/programs/conditional_vault/src/instructions/initialize_question.rs +++ b/programs/conditional_vault/src/instructions/initialize_question.rs @@ -43,7 +43,6 @@ impl InitializeQuestion<'_> { question.set_inner(Question { question_id, oracle, - is_resolved: false, payout_numerators: vec![0; num_conditions as usize], payout_denominator: 0, }); diff --git a/programs/conditional_vault/src/instructions/mod.rs b/programs/conditional_vault/src/instructions/mod.rs index 467a518b..ce700980 100644 --- a/programs/conditional_vault/src/instructions/mod.rs +++ b/programs/conditional_vault/src/instructions/mod.rs @@ -13,6 +13,7 @@ pub mod resolve_question; pub mod new_common; pub mod split_tokens; pub mod merge_tokens; +pub mod redeem_tokens; pub use initialize_question::*; pub use add_metadata_to_conditional_tokens::*; @@ -24,3 +25,4 @@ pub use resolve_question::*; pub use new_common::*; pub use split_tokens::*; pub use merge_tokens::*; +pub use redeem_tokens::*; diff --git a/programs/conditional_vault/src/instructions/redeem_tokens.rs b/programs/conditional_vault/src/instructions/redeem_tokens.rs index 65fe210a..be99a1c8 100644 --- a/programs/conditional_vault/src/instructions/redeem_tokens.rs +++ b/programs/conditional_vault/src/instructions/redeem_tokens.rs @@ -1,30 +1,37 @@ use super::*; impl<'info, 'c: 'info> InteractWithNewVault<'info> { - pub fn handle_merge_tokens(ctx: Context<'_, '_, 'c, 'info, Self>, amount: u64) -> Result<()> { + pub fn validate_redeem_tokens(&self) -> Result<()> { + require!( + self.question.is_resolved(), + VaultError::CantRedeemConditionalTokens + ); + + Ok(()) + } + + pub fn handle_redeem_tokens(ctx: Context<'_, '_, 'c, 'info, Self>) -> Result<()> { let accs = &ctx.accounts; let (conditional_token_mints, user_conditional_token_accounts) = Self::get_mints_and_user_token_accounts(&ctx)?; - let vault = &accs.vault; + let question = &accs.question; - // Store Pre-operation Balances - // let pre_user_conditional_on_finalize_balance = ctx - // .accounts - // .user_conditional_on_finalize_token_account - // .amount; - // let pre_user_conditional_on_revert_balance = - // ctx.accounts.user_conditional_on_revert_token_account.amount; - // let pre_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; - // let pre_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; - // let pre_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; + // let pre_vault_underlying_balance = accs.vault_underlying_token_account.amount; + // let pre_finalize_mint_supply = accs.conditional_on_finalize_token_mint.supply; + // let pre_revert_mint_supply = accs.conditional_on_revert_token_mint.supply; + + // let pre_conditional_on_finalize_balance = + // accs.user_conditional_on_finalize_token_account.amount; + // let pre_conditional_on_revert_balance = + // accs.user_conditional_on_revert_token_account.amount; let seeds = generate_new_vault_seeds!(vault); let signer = &[&seeds[..]]; - // burn `amount` from both token accounts + // burn from both accounts even though we technically only need to burn from one // for (conditional_mint, user_conditional_token_account) in [ // ( // &accs.conditional_on_finalize_token_mint, @@ -39,6 +46,16 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { .iter() .zip(user_conditional_token_accounts.iter()) { + // this is safe because we check that every conditional mint is a part of the vault + let payout_index = vault + .conditional_token_mints + .iter() + .position(|mint| mint == &conditional_mint.key()) + .unwrap(); + + let redeemable = ((user_conditional_token_account.amount as u128 + * question.payout_numerators[payout_index] as u128) + / question.payout_denominator as u128) as u64; token::burn( CpiContext::new( accs.token_program.to_account_info(), @@ -48,25 +65,29 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { authority: accs.authority.to_account_info(), }, ), - amount, + user_conditional_token_account.amount, + )?; + + token::transfer( + CpiContext::new_with_signer( + accs.token_program.to_account_info(), + Transfer { + from: accs.vault_underlying_token_account.to_account_info(), + to: accs.user_underlying_token_account.to_account_info(), + authority: accs.vault.to_account_info(), + }, + signer, + ), + redeemable, )?; } - // Transfer `amount` from vault to user - token::transfer( - CpiContext::new_with_signer( - accs.token_program.to_account_info(), - Transfer { - from: accs.vault_underlying_token_account.to_account_info(), - to: accs.user_underlying_token_account.to_account_info(), - authority: accs.vault.to_account_info(), - }, - signer, - ), - amount, - )?; - - // Reload Accounts to Reflect Changes + // let redeemable = match vault_status { + // VaultStatus::Finalized => pre_conditional_on_finalize_balance, + // VaultStatus::Reverted => pre_conditional_on_revert_balance, + // _ => unreachable!(), + // }; + // ctx.accounts // .user_conditional_on_finalize_token_account // .reload()?; @@ -77,7 +98,6 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { // ctx.accounts.conditional_on_finalize_token_mint.reload()?; // ctx.accounts.conditional_on_revert_token_mint.reload()?; - // // Check post-operation balances // let post_user_conditional_on_finalize_balance = ctx // .accounts // .user_conditional_on_finalize_token_account @@ -88,26 +108,31 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { // let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; // let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; - // Check that the user's conditional token balances are unchanged (since we're not necessarily burning all tokens) - // require_eq!( - // post_user_conditional_on_finalize_balance, - // pre_user_conditional_on_finalize_balance - amount + // assert!(post_user_conditional_on_finalize_balance == 0); + // assert!(post_user_conditional_on_revert_balance == 0); + // assert!( + // post_finalize_mint_supply + // == pre_finalize_mint_supply - pre_conditional_on_finalize_balance // ); - // require_eq!( - // post_user_conditional_on_revert_balance, - // pre_user_conditional_on_revert_balance - amount + // assert!( + // post_revert_mint_supply == pre_revert_mint_supply - pre_conditional_on_revert_balance // ); - - // // Check that the mint supplies have been reduced by the burned amounts - // require_eq!(post_finalize_mint_supply, pre_finalize_mint_supply - amount); - // require_eq!(post_revert_mint_supply, pre_revert_mint_supply - amount); - - // // Check that the vault's underlying balance has been reduced by the transferred amount - // require_eq!( - // post_vault_underlying_balance, - // pre_vault_underlying_balance - amount - // ); - + // match vault_status { + // VaultStatus::Finalized => { + // assert!( + // post_vault_underlying_balance + // == pre_vault_underlying_balance - pre_conditional_on_finalize_balance + // ); + // } + // VaultStatus::Reverted => { + // assert!(vault_status == VaultStatus::Reverted); + // assert!( + // post_vault_underlying_balance + // == pre_vault_underlying_balance - pre_conditional_on_revert_balance + // ); + // } + // _ => unreachable!(), + // } Ok(()) } diff --git a/programs/conditional_vault/src/instructions/resolve_question.rs b/programs/conditional_vault/src/instructions/resolve_question.rs index 9c0c71d5..66af74a5 100644 --- a/programs/conditional_vault/src/instructions/resolve_question.rs +++ b/programs/conditional_vault/src/instructions/resolve_question.rs @@ -23,10 +23,15 @@ impl ResolveQuestion<'_> { VaultError::InvalidNumPayoutNumerators ); - question.is_resolved = true; question.payout_denominator = args.payout_numerators.iter().sum(); question.payout_numerators = args.payout_numerators; + require_gt!( + question.payout_denominator, + 0, + VaultError::PayoutZero + ); + Ok(()) } } diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index 54be5511..1c634237 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -72,11 +72,12 @@ pub mod conditional_vault { InteractWithNewVault::handle_merge_tokens(ctx, amount) } - // pub fn redeem_tokens<'c: 'info, 'info>( - // ctx: Context<'_, '_, 'c, 'info, InteractWithNewVault<'info>>, - // ) -> Result<()> { - // InteractWithNewVault::handle_redeem_tokens(ctx) - // } + #[access_control(ctx.accounts.validate_redeem_tokens())] + pub fn redeem_tokens<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, InteractWithNewVault<'info>>, + ) -> Result<()> { + InteractWithNewVault::handle_redeem_tokens(ctx) + } // pub fn split_tokens() // merge tokens diff --git a/programs/conditional_vault/src/state/conditional_vault.rs b/programs/conditional_vault/src/state/conditional_vault.rs index f9c5a231..78e408b2 100644 --- a/programs/conditional_vault/src/state/conditional_vault.rs +++ b/programs/conditional_vault/src/state/conditional_vault.rs @@ -31,7 +31,6 @@ pub enum VaultStatus { pub struct Question { pub question_id: [u8; 32], pub oracle: Pubkey, - pub is_resolved: bool, pub payout_numerators: Vec, pub payout_denominator: u32, } @@ -40,6 +39,10 @@ impl Question { pub fn num_outcomes(&self) -> usize { self.payout_numerators.len() } + + pub fn is_resolved(&self) -> bool { + self.payout_denominator != 0 + } } #[account] diff --git a/sdk/package.json b/sdk/package.json index 75a94bae..9d380254 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -31,6 +31,5 @@ "spl-token-bankrun": "0.2.3", "ts-mocha": "^10.0.0", "typescript": "^4.3.5" - }, - "packageManager": "yarn@3.3.1" + } } diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 44082e61..598d4ff4 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -370,6 +370,79 @@ export class ConditionalVaultClient { return ix; } + redeemTokensIx( + question: PublicKey, + vault: PublicKey, + underlyingTokenMint: PublicKey, + amount: BN, + numOutcomes: number + ) { + let conditionalTokenMintAddrs = []; + for (let i = 0; i < numOutcomes; i++) { + const [conditionalTokenMint] = getConditionalTokenMintAddr( + this.vaultProgram.programId, + vault, + i + ); + conditionalTokenMintAddrs.push(conditionalTokenMint); + } + + let userConditionalAccounts = []; + for (let conditionalTokenMint of conditionalTokenMintAddrs) { + userConditionalAccounts.push( + getAssociatedTokenAddressSync( + conditionalTokenMint, + this.provider.publicKey, + true + ) + ); + } + + let ix = this.vaultProgram.methods + .redeemTokens() + .accounts({ + question, + authority: this.provider.publicKey, + vault, + vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + this.provider.publicKey, + true + ), + }) + .preInstructions( + conditionalTokenMintAddrs.map((conditionalTokenMint) => { + return createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + getAssociatedTokenAddressSync( + conditionalTokenMint, + this.provider.publicKey + ), + this.provider.publicKey, + conditionalTokenMint + ); + }) + ) + .remainingAccounts( + conditionalTokenMintAddrs + .concat(userConditionalAccounts) + .map((conditionalTokenMint) => { + return { + pubkey: conditionalTokenMint, + isWritable: true, + isSigner: false, + }; + }) + ); + + return ix; + } + mintConditionalTokensIx( vault: PublicKey, underlyingTokenMint: PublicKey, diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index ed578174..afa70972 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -181,6 +181,42 @@ export type ConditionalVault = { } ]; }, + { + name: "redeemTokens"; + accounts: [ + { + name: "question"; + isMut: false; + isSigner: false; + }, + { + name: "vault"; + isMut: false; + isSigner: false; + }, + { + name: "vaultUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "authority"; + isMut: false; + isSigner: true; + }, + { + name: "userUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + } + ]; + args: []; + }, { name: "initializeConditionalVault"; accounts: [ @@ -532,10 +568,6 @@ export type ConditionalVault = { name: "oracle"; type: "publicKey"; }, - { - name: "isResolved"; - type: "bool"; - }, { name: "payoutNumerators"; type: { @@ -770,6 +802,11 @@ export type ConditionalVault = { code: 6009; name: "BadConditionalTokenAccount"; msg: "Unable to deserialize a conditional token account"; + }, + { + code: 6010; + name: "PayoutZero"; + msg: "Payouts must sum to 1 or more"; } ]; }; @@ -957,6 +994,42 @@ export const IDL: ConditionalVault = { }, ], }, + { + name: "redeemTokens", + accounts: [ + { + name: "question", + isMut: false, + isSigner: false, + }, + { + name: "vault", + isMut: false, + isSigner: false, + }, + { + name: "vaultUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "authority", + isMut: false, + isSigner: true, + }, + { + name: "userUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, { name: "initializeConditionalVault", accounts: [ @@ -1308,10 +1381,6 @@ export const IDL: ConditionalVault = { name: "oracle", type: "publicKey", }, - { - name: "isResolved", - type: "bool", - }, { name: "payoutNumerators", type: { @@ -1547,5 +1616,10 @@ export const IDL: ConditionalVault = { name: "BadConditionalTokenAccount", msg: "Unable to deserialize a conditional token account", }, + { + code: 6010, + name: "PayoutZero", + msg: "Payouts must sum to 1 or more", + }, ], }; diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index 9f57a96f..51cd1adf 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -164,7 +164,6 @@ describe("conditional_vault", async function () { const storedQuestion = await vaultClient.fetchQuestion(question); assert.deepEqual(storedQuestion.questionId, Array.from(questionId)); assert.ok(storedQuestion.oracle.equals(settlementAuthority.publicKey)); - assert.isFalse(storedQuestion.isResolved); assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); assert.equal(storedQuestion.payoutDenominator, 0); }); @@ -258,7 +257,6 @@ describe("conditional_vault", async function () { it("resolves questions", async function () { let storedQuestion = await vaultClient.fetchQuestion(question); - assert.isFalse(storedQuestion.isResolved); assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); assert.equal(storedQuestion.payoutDenominator, 0); @@ -268,7 +266,6 @@ describe("conditional_vault", async function () { storedQuestion = await vaultClient.fetchQuestion(question); - assert.isTrue(storedQuestion.isResolved); assert.deepEqual(storedQuestion.payoutNumerators, [1, 0]); assert.equal(storedQuestion.payoutDenominator, 1); }); @@ -335,7 +332,7 @@ describe("conditional_vault", async function () { }); }); - describe("#merge_tokens", async function () { + describe("#merge_tokens", async function () { let question: PublicKey; let vault: PublicKey; @@ -353,22 +350,6 @@ describe("conditional_vault", async function () { 2 ); - // let userUnderlyingTokenAccount = await createAssociatedTokenAccount( - // banksClient, - // payer, - // underlyingTokenMint, - // payer.publicKey - // ); - - // await mintTo( - // banksClient, - // payer, - // underlyingTokenMint, - // userUnderlyingTokenAccount, - // underlyingMintAuthority, - // 10_000_000_000n - // ); - await vaultClient .splitTokensIx(question, vault, underlyingTokenMint, new BN(1000), 2) .rpc(); @@ -387,6 +368,66 @@ describe("conditional_vault", async function () { }); }); + describe("#redeem_tokens", async function () { + let question: PublicKey; + let vault: PublicKey; + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([9, 28, 2, 1])); + + question = await vaultClient.initializeQuestion( + questionId, + settlementAuthority.publicKey, + 2 + ); + vault = await vaultClient.initializeNewVault( + question, + underlyingTokenMint, + 2 + ); + + await vaultClient + .splitTokensIx(question, vault, underlyingTokenMint, new BN(1000), 2) + .rpc(); + }); + + // it("can't redeem tokens when question is not resolved", async function () { + // const callbacks = expectError( + // "CantRedeemConditionalTokens", + // "redeemed tokens despite question not being resolved" + // ); + + // await vaultClient + // .redeemTokensIx(question, vault, underlyingTokenMint, new BN(600), 2) + // .rpc() + // .then(callbacks[0], callbacks[1]); + // }); + + it("can redeem tokens when question is resolved", async function () { + await vaultClient + .resolveQuestionIx(question, settlementAuthority, [1, 0]) + .rpc(); + + const underlyingTokenAccount = await token.getAssociatedTokenAddress( + underlyingTokenMint, + payer.publicKey + ); + + const balanceBefore = await getAccount(banksClient, underlyingTokenAccount) + .then(acc => acc.amount); + + await vaultClient + .redeemTokensIx(question, vault, underlyingTokenMint, new BN(600), 2) + .rpc(); + + const balanceAfter = await getAccount(banksClient, underlyingTokenAccount) + .then(acc => acc.amount); + + assert.isTrue(balanceAfter > balanceBefore); + assert.equal(balanceAfter - balanceBefore, 1000n); + }); + }); + describe("#initialize_conditional_vault", async function () { it("initializes vaults", async function () { await vaultClient From 2a37a0acc29546ffa9a1f69ad407106cfc150fd6 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Thu, 22 Aug 2024 00:00:00 +0000 Subject: [PATCH 11/52] Add redeem tests --- tests/conditionalVault.ts | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index 51cd1adf..cf649c6b 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -29,6 +29,7 @@ import { getAccount, getMint, mintTo, + transfer, } from "spl-token-bankrun"; const { PublicKey, Keypair } = web3; @@ -403,11 +404,44 @@ describe("conditional_vault", async function () { // .then(callbacks[0], callbacks[1]); // }); + // it("can redeem tokens when question is resolved", async function () { + // await vaultClient + // .resolveQuestionIx(question, settlementAuthority, [1, 0]) + // .rpc(); + + // const underlyingTokenAccount = await token.getAssociatedTokenAddress( + // underlyingTokenMint, + // payer.publicKey + // ); + + // const balanceBefore = await getAccount(banksClient, underlyingTokenAccount) + // .then(acc => acc.amount); + + // await vaultClient + // .redeemTokensIx(question, vault, underlyingTokenMint, new BN(600), 2) + // .rpc(); + + // const balanceAfter = await getAccount(banksClient, underlyingTokenAccount) + // .then(acc => acc.amount); + + // assert.isTrue(balanceAfter > balanceBefore); + // assert.equal(balanceAfter - balanceBefore, 1000n); + // }); + it("can redeem tokens when question is resolved", async function () { await vaultClient .resolveQuestionIx(question, settlementAuthority, [1, 0]) .rpc(); + const storedVault = await vaultClient.fetchVault(vault); + + const outcome0Tokens = storedVault.conditionalTokenMints[0]; + + let burne = Keypair.generate(); + await createAssociatedTokenAccount(banksClient, payer, outcome0Tokens, burne.publicKey); + + await transfer(banksClient, payer, token.getAssociatedTokenAddressSync(outcome0Tokens, payer.publicKey), token.getAssociatedTokenAddressSync(outcome0Tokens, burne.publicKey), payer, 1000n); + const underlyingTokenAccount = await token.getAssociatedTokenAddress( underlyingTokenMint, payer.publicKey @@ -423,8 +457,7 @@ describe("conditional_vault", async function () { const balanceAfter = await getAccount(banksClient, underlyingTokenAccount) .then(acc => acc.amount); - assert.isTrue(balanceAfter > balanceBefore); - assert.equal(balanceAfter - balanceBefore, 1000n); + assert.isTrue(balanceAfter == balanceBefore); }); }); From 402fff83b14e8b89938f3d3e63b5f75ba4423545 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 12/52] v0.3.0-alpha.12 --- sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/package.json b/sdk/package.json index 9d380254..0d4a9f8d 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@metadaoproject/futarchy", - "version": "0.3.0-alpha.9", + "version": "0.3.0-alpha.12", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ From 0b8b2cc9e8886f32789ab7f92b73b86fd22bad8e Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 13/52] Use updated versions of packages --- package.json | 4 ++-- yarn.lock | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 584dbe98..39b27885 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "anchor-bankrun": "^0.3.0", "arweave": "^1.14.4", "solana-bankrun": "^0.3.0", - "spl-token-bankrun": "0.2.5", - "@metadaoproject/futarchy": "0.3.0-alpha.8" + "spl-token-bankrun": "0.2.6", + "@metadaoproject/futarchy": "0.3.0-alpha.12" }, "devDependencies": { "@mercurial-finance/dynamic-amm-sdk": "^0.4.19", diff --git a/yarn.lock b/yarn.lock index 413d163f..c75a80bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -910,12 +910,13 @@ decimal.js "10.3.1" jsbi "4.3.0" -"@metadaoproject/futarchy@0.3.0-alpha.8": - version "0.3.0-alpha.8" - resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.8.tgz#eaf4d898129df2c8a1408bb1974fa61dacb87ade" - integrity sha512-5gyYsEpAMCC9x5Gij4Zlj7j33GwmVormuigilnhZTlpm0/RsKdPHxPiPoBmjQL5fHR1N8+5B9vHyQvFbc4KaZA== +"@metadaoproject/futarchy@0.3.0-alpha.12": + version "0.3.0-alpha.12" + resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.12.tgz#d615cab0c3836bc0a7fa54371e1fa1973733e704" + integrity sha512-s6MceULSXEFMrB80CPVAOilEj5RDsVm4sh84yjh1z/1ixIqmmJZOqBgI9lb9A6NxuKzCYDonry3F90Ec5ii7PQ== dependencies: "@coral-xyz/anchor" "^0.29.0" + "@noble/hashes" "^1.4.0" "@solana/spl-token" "^0.3.7" "@solana/web3.js" "^1.74.0" bn.js "^5.2.1" @@ -1188,7 +1189,7 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== -"@noble/hashes@1.4.0": +"@noble/hashes@1.4.0", "@noble/hashes@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== @@ -4465,10 +4466,10 @@ source-map@^0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -spl-token-bankrun@0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/spl-token-bankrun/-/spl-token-bankrun-0.2.5.tgz#0a026be083a19300d593c41034c9685968dd50d3" - integrity sha512-1Tb0bksg5Q3r48LInbQh+uBYhekrz1OWP1XVd4zMj7FAwdQzqQ1puJHd6vJhCuIbB8Vr6CQSGf5Ke716EH0khw== +spl-token-bankrun@0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/spl-token-bankrun/-/spl-token-bankrun-0.2.6.tgz#c820c2f8777d64966b58eb37556f0add4eeeb16d" + integrity sha512-tD4qpg7s4bagnjlWiDKaY158TcYq9odecM2rwqlKFjKzeVyjYnyoQq54BxwofghWD9Q+gOR/0BbWYK2KW2VbJQ== dependencies: "@solana/spl-token" "^0.3.8" solana-bankrun "^0.3.0" From 96d58584ca3467b846e1049dd560d8af4f9f373b Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 14/52] Remove `metadata` folder --- token/dark-metadata.json | 5 ----- token/devnet-usdc-metadata.json | 5 ----- token/drift-metadata.json | 5 ----- token/logo_black.png | Bin 18003 -> 0 bytes token/metadata.json | 6 ------ token/ore-metadata.json | 5 ----- 6 files changed, 26 deletions(-) delete mode 100644 token/dark-metadata.json delete mode 100644 token/devnet-usdc-metadata.json delete mode 100644 token/drift-metadata.json delete mode 100644 token/logo_black.png delete mode 100644 token/metadata.json delete mode 100644 token/ore-metadata.json diff --git a/token/dark-metadata.json b/token/dark-metadata.json deleted file mode 100644 index 2970d60f..00000000 --- a/token/dark-metadata.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Dark Protocol", - "symbol": "DARK", - "uri": "https://arweave.net/EtLH9bBZdhhs9OFkntBPvQBW8r_TNBouOom4yJctcbI" -} diff --git a/token/devnet-usdc-metadata.json b/token/devnet-usdc-metadata.json deleted file mode 100644 index b86f2f7c..00000000 --- a/token/devnet-usdc-metadata.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Meta USDC", - "symbol": "mUSDC", - "uri": "https://arweave.net/pmEBiqDAxmLQ6nPTfdG893WZ1PJMD2jhRepG2P79Ea8" -} \ No newline at end of file diff --git a/token/drift-metadata.json b/token/drift-metadata.json deleted file mode 100644 index 822b8602..00000000 --- a/token/drift-metadata.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Drift", - "symbol": "DRIFT", - "uri": "https://arweave.net/-B72mZZumiJmHaL78xT5sOzUS9pXEGtylX9C1DSOV0I" -} diff --git a/token/logo_black.png b/token/logo_black.png deleted file mode 100644 index 320fc73034246226d639ce849da409f5a721276d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18003 zcmeIaXH=72(?D7^+zK#<dI6U7byq`2&nGeRd_@| z0MP-zoMdOf6Epb?UGPEZ^ho(O0lJgr5BSfkXS(;y)zk?1!FMtO5<*%6A{+?#M?iRy zfCT@YfIx+i`JeBP2>JfPKnMteo)ZxNh0zC}xSzY=7mWVNyb*P}1Rk2?>%@&jW0F&oy^IdFJQpV+xL%fMi`iT?RHrPKMj`G04!b^6N| zU{DzMM);c0RpEbdgQ3#6r&13bpFaaL)b0=%S*~Rq2Zvg0_XP+-G~v}HQF5B< zt+oR{M?1&WDzA*uf~x*xCm-w3lXNF-krcbmx?R5kW3OeOonjge`IiKQ5Mr|b_vQar zA$+4)s#pr&;rc86EkXN`dI7`i`h&06%yJSk=Xb#u!(s*2Ai0J(d{%G^-VtP+u6^xGC)t_r^+k>$OKH94c#+u3dzd3 z!#Ph$HYW?^u1f9^MFHC5pcQJpc$3FuLZVT(u&De)f6MKPycbnUbhs6PU?aYyBf}w~ z{nd+kF=7QTc7!^~C7m=!^{gTZ)vN@md-!$JPk;Vf!FfZDx>-BiLoG4G0C91;i8-nvNZcuE3V@p7?2o(2do?}byAkgMlW(0a!7DVapI}dF&j^RC zU-)LwN|_VgaVk7{io5yE&+XMhJN!fy$xn&d2Uw=>qmPAhdWaTMRPr*!_2@L{$akxN#L)Xuu~)uh#;O4Rb8V~Q`{u>cG#3o>gbpAo0}9K%s;P8cfJ5)+Ec>C6+VO& z@oO+rKzCIxcV<+)$sko`Ddpa%nu|A>+TS}^K%!iaM~-$27STwbdd4UdN#8&588wC9 zrg6fW*P39g>lbe*ksj`y+K8TyB}RRXvZ;BHbe5IQp)1W{V9#St&;hkq6Zm_$r&YCH zC{$AJNlSc&NoYf12yQuMiV(JGS;RU~M#DSa7n{l>h_Y6^6j|6EGScK$S>+K^K_mRepr3uS)X?+wZwQN6{`<9`A%Gzeisyqnt_l zpqiP^w4Q_X_Oed$u*_IVlV#s2e6xo|9w?nX%BZsRe@%e+NEz!}^1y_=|EGoC(b2eE z^%@e%q>`xgJheR6Ldi%YsSmk#q4Cz>qh z3zceb!sM{p`^8y@*PC^k2akLdd_5)Q{9ji*E0_t zF3g3gJ1zMM!n!o|fdIPS$p7Iz&RoOef=qSA5$}Z6Wt?AcMhd`Gx}N_Q z>AVri{?gsLDF)_9jbqF{1`G&J;n!aaA+{!r?C>IIVYT)Zn4$!%6>nUylOT0nN)5pp z;gv$~r=kB^*S{>dUv#%agxDiz!Uj5^@M72Bf;ppp4pVlGcbp+=w|nFF2m;P|RSRfD z7FDfMq|K1hd>o`Gu}M8Vrx2U|soi4aD@?82(T$hFV6Ut*ewtKDgnbd-EeH>Cg> ztYzX6Z4WGt?lo1wW;;X{e}#Hb&kU@lGET-*;}A0-q93R%^5{GlZcJ`Jn>yO1{B^-i zVTOhOhH_j-Md6J|Cu|`X8@nl|e5L~cy^?ZCr%kBQ%6r4+&shEXZiKS8WY$crrug{V zIn0G?Eq?r;UGxCn?kxE>-Xygc)K)yaOmcB&>2W>g@Wrc|5BEJ)WZA=tmnff%ome&5 z;M6R7>EQp%#%m`%NgRs;hLJ}d@AWC+EeF2%#Oq6XZSt1b+^?@3>0Ggu1`5Zx#vU8^ff&vY%hC0@fbJ0Ib=xZCXPrMGHx$50E*tyxA(9BF{E$e5g~IBi>plFEQif z1;L5-eKhK696Zd+QRZs$t{cFYP=Zyrq4vJQ_SoZ`Byc-iOnlfG&FfVv+T{IvJUYI8 zEhrc|gzrX0Y=>LB(?rMmUbWXNuy=j?YRqgdEMdi%v+vTIO)Lu3KX%;{#U*R;d zyq;LM>3PmW8Ol-g2FU%>+i28QB;4k@z>f6JcuR&!7~c(@*p2}fJ`qx(h?W&6F2gl8 zAOIhL_%<_;svLR0`KS4L$-^`tDkM%+N8b^#M9QOLlkkjaD!TX5{Q7&fp&_5LX%iPTmQwG15X&HyscpeH^ zj5z%nUU!#!whmy)XmpiP6wf#|<~LW(EZS>ksEx_S~f< zQfYuG!-)X8muBkd=oi7{{bt$Y*uL}JEk-yash-2QP2YgovAqCbY5>gN>;VAdC)XZJ z!^$ZeGHW<~j{~z>T2LQ4NSiF-JPBM8z`erWHvaO%)8EDY?B^PoN_ZJgH#VIJzu>v> zn|*5v=rH1DoMR}CpW$!G?jv4A^i*tesf2&R!*wBQV<$9l#ug8Z?Vavp$2h|RZr!e6 zSs&i4ne#XXu7=&PSCdYV7RGr+S|GRc*7woTsMo}c@jVf8p?o*WaR#aCL|x07Za|I8 z=ZnICdRs5Xpx))(Xsos=Jy`{Gp>Xr{rW0iZ&n24S+|_p|ANGs!vOK`)^E+a64#TjXx@m{=W-p+F zM~ENWt7W`8_q6W#Z-44hH!+)5EL?0U-1z?S*{r;;O=qDZ$QlOK;7O!i)d!`KhTMIL zbot}qI|Vmo!_6$H6N!vE5DCpr8or6mw_nPWId5ZPUOehbuS2HB4>FbO_Z+(a#zY@7qzX$inZ`Dehq{n`GRqTQ{e7qo;wlo_%?vXoaQnN_qxc z$?#^J3Aw5Ye3Z2FdP`Zd;cwzUHyihLe|r23O}gB;F;(&1tVedXjmBwW!@YAO?UhJ+ zWr39mM_~y6c4ke9Lw!AM{%+9p+hJXmqV|r-M-f!NFMj&?9dYQ}%WJ!N_>2z0L!vU~ zzLCZAb!SQQ53zupk5hNLR_Eb;|KDFd`Gx!U^~MdmPAuhIWh@}_@E2cO41y`0_ z&KXWsMn4nQ@DZ+wt!+IL<bGfvx)2T6)}DTJu-D`AZ#Sf`r7>p;DXJB|V~_1?_Lh zLw}oF?^5hOT7ARC?NPM_2qaADgJ?A>< z#-gM8=)s@fi;!jVTHflP+eBV@yQV{;jh=d;icA&_mvl$F{-D{p+C`KerxVfU?>Lgn z48hhW_HK^*E-WB?)nZ*v1YK1Bbh*&wpFr;#UDB?}=hhwV_=9F+ZokGeiwSe$=z_~w z48+7`5LeTl|0bsME91Va>w*KT%Zj2#r-`ekcG%Z^Co1iFhP@CdwrRL;>S8|nQrw-F z@&gbodmFE^x6ssgG^Gw3qL^$(!Ox{B=LFSWQFcXw;)>UqMVI1(dXk>h#vyNGyZuW0 z+G|&F;mP~M^ktHa6M8kcJwmm}R~YtS0KKme*>X4z)eHP>j!-1;H+V82+xY}!Lm9m& z*)vOBgDkps)H6OsBbHmU15^!S{A-o-1H4@EK3T05jclX2icF))3Gm-kw|7^w{JFnH z!Ug3%B%~eem|0Ofi;!-pugDe6I3t3>OXlLze=Hl;VBYp1YA5H5p%owN zM2O1#bo}EiNX{|mQt1^CTPHG63lENTAig?Hl(>gWIbi0LwRD6ghTgT4+IP%mTx_Wm zo5~W4SY0SmEixX_GHu7D<3SLtZCe|Z$xSyWiw9=^?akI>2|GqfY2TwyNH67D5!eaS z&7V`^RCn4?2?Pj5h==*fI@AtX>Dkj+PB&;I?^sr&bkH!zlyI!3FjlmE3+fgU2`2%Y z37nr=p5B40KCtOn5&$Yw!Ikd;XN$U>WU?&n+pt326J5F$bD}P4Lw_6P3Dd zPZfMihHk?z6Snm|QdL{pBJBw=N$jB`?>|eA0+w8qg1IxjTdVdZ$kkv@2jT>F@J`Q@ zhaxj*4v%a+FEtLGY2g8m;uVIDOP1AvkqQz5w$&l87RHYg=Rc=#F@}{9qmGOFi6F%7 zb z@SdZMt50UNRcKOZGv0yXjCrys}9;7?cUS)q^jYFE<>%?TG zSJqz5kH{$*c02NJ+qeMvEts3h!t7jkE1(q%MNzc*)YljkU&=p*U@z`-LG42KTp!Ey zCWQg{Ft3L#Oax?!`LP!-kSu+3zU^NY8tnu4&XV`NcrXbqjHvVtCfKj70F&Fr=nvGw zE*^g&VHR%WN1`kt#CeRB)}_a5Nbfn!ZTMsuH?)s)E!A??Zo6BPT>D&W9@sjoAc7Q8 zU158gaRmeO>LjUE3$ad-pVdS4#~F+L9+cfL^7PlHB82pj&ur^>cm>`^Sy!EL^M0eMDetJYijm_=KK3G#F!2{#BgjoBKxk0Rd>}o!Jawp1 zuAUuCc^4f_$MlgyE@?voYBbhmH5#Z=)F0=>LI^3Km{CjJJd<7crV{q~`ZZ7q@F7Y+ zH&pxb=oM7V$X_h0v6~*`yPWnZmWpG8HyY;p{sJANdS=ubB#IOYxplcUu7c*FRWj-q#F!jR_mN}$FyPTf4hbD?#{>>9o9_ln{#>feNNT!kWPt1Rnf+4>7du3LkefUHxp}-}gG}IvI`SEsnytI=?1sgZWU|7s4D>Lt_?a4NU06 z_S&wAKJ5^{Th#x`={zB%gM38B)A?T0g^q32pdFf4FCM*S0sB=;ecvzph-sUtAA;Xj zhorcl%u`Bbu-mw=|0pQu0(FvVhDzz+8oQ>iql~E1YvhtnVmUp7p9GWk>cyr8Wps27 z5=3}mP;edb#rB7~$+ zjPO$IIXCI0FwX2bP~1f~-w;w(`gF`cmFXaEm#uxTNPNoaDj_6{Oj>H+LBA<3Y)e_f zl`hh7Iq+JR$%*Jtv_zVZ?8|g0hG}WLLmycC!Ho|SHeU2dH(=cXeR=zN{_%tP}? zX!A9wf|@}!CI+*sD6{&;ekYH2?NQ%dv`U+hΨfv?{LYy7a!Bd>c!JYpkKVDq7l zAZ_Z>Q5undXAD$XWVXAMd(0GM^EuZ#0UqY*E*egNfI!@WuXlynTL(faeHboRE!_89 z_1sA58E14D981anr3Q9c_+XOw3Uid5ARJcdOIv4C6cde_JhP3}*zOHN)#oL1>uOOY zG}Cevy~RKsyp7$q*Qvfa?8$?=vFXIQH&)}c4r^EYbo5ZfsCLs{;NSY`TdfN>mbAuB z+8-r>wUbDAu${Zp_C?dkrGPd+FX+KE*pJ~)KpJ}w6P*}QQ?+b1m(WGiXtGbcieB5?^#2q9#eanDh7H4(WQUX!F>QS(wn zr^Y(=M5eIJ^AXmH=W&3DZp;NMR_?2Mh(m&(5?r#9dI*Y}E8n&J0$fvat&|mfoBDqg zycE$L_2dsDKsb=k7zFGh_jW3i--~S$r&cBN_8#psDMZQj`xL931+%Jxn5oa$I=o|e zadHTyqNL~8*CqdZba0%-VbM1)<*6WT{ypj%b-Kh*j8E1FKiWz3GQ-)bnY@f!QrvkS z=5ktU)r{o*?}AL`$VCMs*^&FIO-X9IB~hLC(Cz1D>NUPHa~WD|jY(NY!nddM!PaY^ zsnS9dyK#)tn$82d(fX=Ae)8O*Rk*-6kxQHg?9yM(Jp?XSz}~rZ$p?Bcu;H8m!C?_5H`?LU;nBRQs^ zm6Z)jq&Q2mKK%h6G;4V1$bE_$HBk<1yLQE9Z{x{9#| z=0ZM0ACRE zh-0-BuJ*3Fb z3Dje{)8y;96Pe0Qei}uoEZoKUF?h_X)Zfx27{)7()*~iPP2vY<$K9^U7x?V#4{biAd()zRZa@AYlEZ7sEv9 zj0S$@!!j>evVkd@u`t-cEFjf$=yTj6$0K~#?^jXt$n-nDic_P zDS;TM)+w_Sm0cglK2o|gakd|M=FVeKi{Xp3qw zB$sr#Sm@P&1ajo-#r7;jDs+FL-nk@j0glk=Yn0ycZ8KSH>ZXz{#9Fetnr=jB-#Ve|d4vs8db5!AOmOhX% zSiar&(nL%w@d|UAjDrd*o0&;k)zAygboL;*C!b&}MsLAv4AjXybcvCeXFUVE>bVWs zi+M9vpE$AX4ttX+43qOJfX6uMRCBt-mzcDy2*2wH-|+H~F-Ai_%Z59EkLju1NULI1=ZC?-<0=+ zKF2HTjFbK3w$~EfMIu-G`1fv&Ctsa0&>;`ioEWhjs8bp$&q?{6{I)M>x6uLFWaU!vk#$m>>M^s!5%*PD zgjx2zmasy-<%-r}e^i7+y``-0_7|3Gw+QW^>h|3(NtjmR&Nn-$vL)hT<18TGzjJug zp;bm(?mLu@A@XmOzDvBZks%TKPB7-VQkFqHQ>E6@ei)J{L9QvCxSn_7;uSgs0fLhl zjZ`@o(P|)~>q%?h(P!0tE@G5h8HKfnK`z6I_2sBc>8vLfeW+`PC%15*T%yZU0g7iHvFHk2smS~j|@1{WqI?>D}r z4NB*_6H%|f=^Qd53|?S|Gd4*!mNDPBY6T_7TjPI|7zUKp#(^5=pz6dA$-~x;VEZk8 zVAz6%ajSaw^W%3*fa5Zg3fTUUh!W>cg*C6m1+_m;2<5TE6>I z-UfoMS#W5&r1Q<}z~pE{d&f`>ewMtyuV;p-p#Oyz9mm5c;F4Mz9d8{t4!T8*x?ZV3 z)sf{3Ufoh$!Ft>KDs;Mm3f(gKjN-C~L(Tgl5SYIa$~jk!E-Avf5O2HSqcjD6!%8yt zAmKD)wD-9GAe*a7pL%H2Cc&v>U>0(RSC@UTmQn?r5WR!&MwA+Lh~0NA8;O*CibIh} zhv1~k?{fV?^cw7#l@JFJ#TtU;_W(ey?}=m0uMl|)$Q^E7bj$n;G6n^>GYFjNfeMag z=l9u)zd=LXK9K^?8F;s7qgnt^X+tq0R_!t@UL%e+zK3RvXdz-;eR2-+m3Uvh=ukOp z@F9O9hL1S4Zo%|C*hNRJR=6{H(eUwiTll*e_N4Qr5+L4^)aGno1+#o@Ejj$Ew8JOd zt>gbYd+2w|BP!xR`nBZ#fchD$Yb9?g2V2>W$hM;3GG!U)T%ZgDF&IhU7s9rXTNO-{ zy*~bCjbuP9;)$1ypNsRKSmff(l_9h2+Fe(}oGCJz^9h)W>>EK!duu9<4i2+cUL&=~ z3Qx-Fq6&^EVz_zJYOgGWMu}o%deb}x%4)hHeaqFHKY)wU+S$@+_PBZ^_;+3pG$`hk# z)Uxtu5^CA!1*D&45-mkEC};i}|_l#JZH{=<&) z)Aj*=5_GusoZbrBZtfAc(ia0 zk+1|O^+R@ImXw;H%n4{|C-W8wmdRJ`QE{2I+F@y}0qfm-7@SOim)n#q7me2XbY3@W zIEpdvwEjXBoOT^_kg@crK2Yk1w)HI$Mj3Xv7RTdo4MEpNt6n!h0>IjiV#8ncanoBW zPq;2SKQoj8aBLCT=7r^WSTa#Az{1M$rxPsEk|0DqD-tr2!&L`8&&N946 zIJ!$-+QBZ1R!5_L6_Iur+Xw>c0jWw87<13-CE<6`=I4+zR^=gOxVgH+F@*ncaIUTI zf1&kXu)tHDk_yPJIhErwnY%&J>BAKXuQEJR`OFJA2B>hXE(u!vFH!-D{h$2*8>yrd zV9QP;cVsA}?(Gh>r5v6KT@HJr7j9wekasTPH=V=`miPZr8#tZ-wS(kkr|qS_ep-Bw zjyW;fDu~d8yl|V|y@)Nuu zOSvY>(5L*i&szLf2n7PCC`+)vgz)pr--H-1{zdu1_aAZp?;`xajb38-q+@W}MUxC& z)7P=&!>PML{9nQ@ba@?!v&@~6f8^0Y<_?g?; zJCiWtkb{xy5ty~!X7cLaVbB;MOuQv0j31rnhMw!8ThRN25;I2&$E%#Pj zT<|l;#Ra^XFA#U;p})q7={lAjy*yg{7qq*Lfb>(4!^qh4WQ+$(x*v?bI7OsIHe)?x z3YF088B$^X^Bv5Rz^-+)wNo>waw-9+q=-I$F~77Sh{Tu-{n`+4?u#Rr0b<bRf{ewB9cR7v6D@l|ypOOsIVn!(>czSjH-z%v(WJh5gu zDWNpp-p)M#y>E)J_KItj`?@WD7$GE^vGS@uZ}EaD5~NI=H^Zt^W9o2o!)qbN-#jle zxAi%8XBgT_ctP>g^ia49E2qT~`$>hcFfOOU zF@TvT-e`V{)t@EOmpK~-QYlV9Zr7U@RYuE-c*d1@?OjXnKI!W%x{HnvI-c058Hzcj zS_NaR8}Ws+N}p}G$#ASa9cOD-g$N5xWx|7ItUO-OIe7?p;S_?YB(4qImSh8*v)wG% z2+Z!=_!RUxa87CS7#4X}l zb1IBbl|JjyD2jcG3`aA^RpiToe8NE^r!uzvaLJ_=u!rHpzvvE;_vKx2sQdXYS_Tw& zh62p~$fcSaCAWv;hef^|$U{tkT}|W7j_UN3Y@GNr

M4j`UVFVk$^>Cv^8C?mX!_ zA91MSK;@MC}&tFxCwa(V=|9xU%L`+>%7OTLKEqK!AM0k*n{#(f=MA{{b#?njgULA|5FH zrbTfo!!1UmQMP9q4FX0b`SEF0Khs+L#J+19+aH@oTggq)7si9OxRi|$a)*4zhpCG- z&vAR~3fQr9Fs2;3y0WBxFgx{|ppYYy_2^9>&4$PVNL=Lgocfptq&rP<+LVVsCM>*_ zLgDIOTR~FurS=ZJi)qvGD8t7~cvC2PII%D|-3d()8tJ93BLl?>*3E?MTsK#p)M(u3 zmzYNoyNJ3=+V?fKKgHk*Fgnf#sXtC%-6kxQp?tGH+Bob#>Sge&s)fSmIn8SO->iF52Ocvx;AYT1e>1 zbe<-TuSz|Pbw>wd_I}5(|Fb}|2EHAC>+l0}a}yh1c&(DJ((3v!M?{B}qx14qc(Jc?6T+$7;Iy^2A6j{xy8V;3iQXK% zC6)=KfvM{WS@ROH0>*9l88i?B$_?DwTx{L|J9Vc|_MQlrWP*z}XhkB?(mZv;ihN-#{U2ZoaDyHZq*&k8qg<)!FtG(3bNL(uWh=J-lzUxQP})rI^SCS1E3S z9%+5V9jek;$^(BG&AKprp+dh5=ltd0LlP8fBEl-Oe(XFb`2~jGT*Asl9I_)%_ZL^V*1U>inyfB- zgf35i*E$Jsc&O=N+myCns2_%t@53m}>z@8;7&xWH%=T$fHWb-#wHgC;ZM1pj zRuTWIR`h_$k?uK{kN!BJFVMN0ir#3D>UacdS~_2-^vB9%8h#xnP9|rX;{j#GHUwyfzo<9rL;m6s!CcnK^>A~Z;a-&aofHyJyaBGZW zIuuDtJ$$wdA1m$>A9D{mJZhridLCh(&p9EhW1Z>ZemA|&(>Un&j+Lg|Q|t&%ElRUXtpSvg!EHjQS7b}5bI1(eX^o6xt( zy(N-UD~$J^_^1s`6+nb6$N8^+A5Ty&^O)OFKKHmzAD868;fRxq+YL?=;<@!)KqgZD zOPPs>kLC{dmU~!Z-{S*IuJVMx<$@2)I|wdgR&H}2)c)c>41{?wjos%ONq(#28V7U? zdMf6WC}pp?3y6YtX@Dqu>0KsEM1+ELe#{Sgd{$=0vKaZhnmvw*nXKTUJ!x04SL0h;k4ZS67#huhz?f&OWwp8oC1jG7|1Ja%nf9-He8P` zw>*Q>jca7 zyHo9?>8;)L_Kui*9e$dR7>TS>@0)A))8g}rG{`Hv`f>x!3Y)zJ`e%sN+q`Uw@z!KT z-+EtmpRI(_1 z)`}ZP=nLR>j_XaDZW#0*d;+yU%KmG)35p_{=1#{gk6YZI&53K@wPBfc~&SqF^dS{mt%8SJVt5b4qnOf zIV0IsJ>g9r)+~#WDkPw!F@W(AOg@N1e)!q<0=L`7JS9nImg%DWB}fNieO_l{Cv*3a zdz#vS6&S9nG||!1YoVS~A>|qKa%O=iIM4S6O#k2`vbGHre83FNy!NJ|e+Z#i7xVZx z@z&CRO(c!wYbwf0u}H!k)uku8kNzykk6XJdxe=>hQs;*cS;!%0SR|PsKfMREH(W zv`}Ed9cSQ;NQ}6iuEpE0X|7+gpOcEbayIX{@le%yl4)@@kPVmmcp@HWkVE^L8K@A-ik zE_o}Xs1?3={EE!P?(rg->xgZeR!b%kliP>f6EmnAOC@U_FA%CGu~2ZhtmM?U$A5}^ zg&0Bo^+|T}PUWSG2ccvqUU|=dmc|hv^g(?m@I>KNQ(iyx%1_$?Lk54=qCv{66+0#A z=NR$aWH8%f8c8uJZ24tpO?Dk{dR;K9^=<5f@|}o?N)th23@2@p9Xve3G3%&(1moP}L%ZNN$Fkh2zxyK3omy8K$T z0KA&SnOYZ7>fwXM9vUi^S{Rb$g7BrVBPn5DY3`}^@|ursU~$YVPSj|8i%?vee=i=V zDFE7AT<7C$-P5b~{~($FlRp+|P3-HgKZc%^xCAtizhJPxb)X$3ej+PS9d|PA`6Oku zR{#)A&*X0Ub8Tp-m zrSujJ_EuFe);8rVDg}%Fd0vjIG%#n0i*@%O*ZsHvsBz|9d(FphSGD$jC0O%^Cb74^ zJwI2Sg`9A>+-5;z;d0fG3C=grKoXP=EnhXYJL}#DUXIa#jx_B;-QFbfknJ{pcQL|e z?{U~V*L)aT_8OxP=qVdazQI?>>+bk!kv#drBUXH4>}pf7NArRibL`|p$iZE-s()>z zj2yWW0|jou>2fr*!5VsCb_QmgMW8Be#Z_-keS9$ACc=fb$x)U9OCRx@1pUUy4>l$9 zjs3|3$R-Kc4cB-4EKg-+mF0zXJ;Bod`Ke^$N3N_y-)@a~4$r+MT{=-)|*e z41s!}&X8-Pvhz)db9f^MbblKl3KNY7wkw4JQ6@iJ`eW>%zOqvvp|X1VbLXwZ2q6-b zGan$EQ!WF#qc5f^Adt?x^zt@Et*YoTdIxxbPcJvfzBnUyi`$0gm>-$k?OF zhyh$3g&})c9miA!*T#*u+wg~k!mk20By_O7*~*{;Ov_28mlODK{+IggiJ`@ix98HYEW=kLF$?rQ_q9NJjZr2G`%? zXoy+sfV$N_4>JLD6(!4$83@aX#w4)mf-`!|uU8u99*Be;$ZyRwLfNhGD@Hy8vKrA| zFL(P%--qhze#W)(i6LidLyZZ8V!yGR$u(q9fqVWapbd=)$zGIWjq2iLglOSvs==mK z=qRiLwgefFyqH7{<>-!+A4}YeMar$_$^WLA2X3G*q13aSB9%s9qxaRhk?O{Au!upl z;#^yW$x3*T6KGKYJng2|Sv?6#!p5u)_6lR}npy1N#sM8uYhf|=2R@!0h3ilegLRuZ%~9^IJz;W8z;s z7expm<273MWl8Vms|NCb61Cb{GrF|OE1v7qP&xJ}rV^k*OA`(`!o5$>uFzIyY6CJ+ z&Iy}Fp1|TN^^u#NTY<*5ZgAW;F7;hOGx*Y4D&hh`=u?BE)=#>iO^$ym z3VML+KDN9w5j@{)r!=Ft;GGYU@q$vj)14v1pzsK`I_aq%TzeRq>Vt{8p5G=&NefS- zf$7U29$Tqo8(Elig`3NXPgOz3aaUy@X2=^qh?5}p@djbAe!X{!0oO(V{k{@FF;j5S z_~QD{TLy0(!BsoxUdSL;2UcI&ert#O;U}<`XM0j?hcnoWPNtDJ)f42q$U#+W%5u^V z>3%S8kcR**+4i@*S~7zd6({m@f0rV;xY+0?<;a0`U8O}ZXR&&u&Gf=#Z$yhfKZ83u z;@fU7D1ktD6_bSDNLI=J3ce%{7ekzDUg-*vG%NeEgx@fl(Y2mjnsJNrxduBKxN<&` z=%^DxIpwgMk89xvBE8j{$$)sEsBFyWVAS*lwiAJ4Q5%HK!&Rljr%k-=+V7o#TcSDt zXktYJn*vL%gTtv~Q;6)4ErUjZ-kB(Td1#->T~jk@z}Fq|3{LGVL3(_H?eV$QlgWH= z76!(K$dB>ue&E_@er*iDkVv!$t~R_~eW19k440p1Ugh;xmtR7zLB3&FbbYB) znIe)h!tBFrPv67s5JXSyHp{b;k~DcJ>){OspYp;nYWl>O!lo1O!C8xW5I^y&1GRfoDfKyt^Ni1T1nLR8?(!5l^!O7C zSodZ$YjBn?e3o~tL3c(_I%|}P<3VP>)Cak28c8mFa9MCb+1rI)@CVqU*@8@WRAG_{ zqDUMb@@Uiec2{VnNz_GpDPUwu%9~gEQn)(PR6cCS$cKRta8X$;#hRhjK)WG}Q?FYR{-obW5D<4CxkmGsKX=oO8j7TONKEKwS&< z7pIw5pJ?Ud@3KszcR0b_4So(T+k$4&$54;)k6^2Dw(mXuZmKtkf6rjRUrvMb|Qbo2&6y$at`tN5)6hcgHRj8RN)tT>T{xB*Mw}&4@sny;1Dfg;Xt9AH;ec z`$QmX6j^Alz2+_qsOFeDM6%xWo2`3)J*nMA0)MNwC|X{&5f@sEK*@uAy9b)|Vp*lq z_2_$(fN++aXjb6w4a=wX&l2o@FQ>~d?;lO+`uTtpW3YHY|G8AQCJo{q`4W2__!Am* z1;ZVCNsHHQ8KN>Azl)e+op#6F3Wm2u#8;iP6w=j_x1DxfJgfr>6%84q+Im;y+>X+R t3*P|OgkOw+hyVBG|0V<_-nA2=AGc{W8M%ndaDQ3lo}#(}`ZnDE{{XGS!dL(R diff --git a/token/metadata.json b/token/metadata.json deleted file mode 100644 index 5abe8d79..00000000 --- a/token/metadata.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "META", - "symbol": "META", - "description": "Native token of the Meta-DAO", - "image": "https://6hgaiayuroxlfrn3djpb7mx4ocwielgo2ovpj4ka4v47jsxj2p2a.arweave.net/8cwEAxSLrrLFuxpeH7L8cKyCLM7TqvTxQOV59Mrp0_Q?ext=png" -} \ No newline at end of file diff --git a/token/ore-metadata.json b/token/ore-metadata.json deleted file mode 100644 index 972a88d5..00000000 --- a/token/ore-metadata.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Ore", - "symbol": "ORE", - "uri": "https://ore.supply/metadata.json" -} From 1a87fb5f57b89680c2d31a3def91f8106b18a2a2 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 15/52] Clean up `tests` --- Anchor.toml | 2 +- run.sh | 2 +- tests/amm.ts | 6 +- tests/autocrat.ts | 2 +- tests/conditionalVault.ts | 2110 +++++++++++++++++------------------ tests/{utils => }/utils.ts | 0 tests/utils/bankrunUtils.ts | 226 ---- tests/utils/pdaGenerator.ts | 79 -- 8 files changed, 1060 insertions(+), 1367 deletions(-) rename tests/{utils => }/utils.ts (100%) delete mode 100644 tests/utils/bankrunUtils.ts delete mode 100644 tests/utils/pdaGenerator.ts diff --git a/Anchor.toml b/Anchor.toml index 81054dec..d8c619df 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -1,5 +1,5 @@ [scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/*.ts" +test = "npx mocha --node-option require=ts-node/register --extension ts -t 10000000 tests/*.ts" propose = "yarn run ts-node scripts/initializeProposal.ts" initialize-dao = "yarn run ts-node scripts/initializeDao.ts" finalize = "yarn run ts-node scripts/finalizeProposal.ts" diff --git a/run.sh b/run.sh index efddfb05..9054006e 100755 --- a/run.sh +++ b/run.sh @@ -14,7 +14,7 @@ test_vault() { #sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/conditionalVault.ts"/' Anchor.toml #sleep 10 && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/*.ts"/' Anchor.toml & find programs tests sdk | entr -sc \ - "anchor build -p conditional_vault && (cd sdk && yarn build) && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/conditionalVault.ts\"/' Anchor.toml && (sleep 3 && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/*.ts\"/' Anchor.toml) & RUST_LOG= anchor test --skip-build" + "anchor build -p conditional_vault && (cd sdk && yarn build) && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/conditionalVault.ts\"/' Anchor.toml && (sleep 3 && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/*.ts\"/' Anchor.toml) & RUST_LOG= anchor test --skip-build" } test_no_build() { diff --git a/tests/amm.ts b/tests/amm.ts index 25fe0dd9..e3e5ea1c 100644 --- a/tests/amm.ts +++ b/tests/amm.ts @@ -26,7 +26,7 @@ import { } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { expectError, fastForward } from "./utils/utils"; +import { expectError, fastForward } from "./utils"; const META_DECIMALS = 9; const USDC_DECIMALS = 6; @@ -48,7 +48,7 @@ describe("amm", async function () { banksClient = context.banksClient; provider = new BankrunProvider(context); anchor.setProvider(provider); - ammClient = await AmmClient.createClient({ provider }); + ammClient = await AmmClient.createClient({ provider: provider as any }); payer = provider.wallet.payer; }); @@ -114,7 +114,6 @@ describe("amm", async function () { ammClient.program.programId, META, USDC, - proposal ); const ammAcc = await ammClient.getAmm(amm); @@ -161,7 +160,6 @@ describe("amm", async function () { META, twapFirstObservationScaled, twapMaxObservationChangePerUpdateScaled, - proposal ) .rpc() .then(callbacks[0], callbacks[1]); diff --git a/tests/autocrat.ts b/tests/autocrat.ts index 5f498934..4fd9e00d 100644 --- a/tests/autocrat.ts +++ b/tests/autocrat.ts @@ -25,7 +25,7 @@ import { redeemConditionalTokens, } from "./conditionalVault"; -import { advanceBySlots, expectError } from "./utils/utils"; +import { advanceBySlots, expectError } from "./utils"; import { Autocrat } from "../target/types/autocrat"; import { ConditionalVault } from "../target/types/conditional_vault"; import { AutocratMigrator } from "../target/types/autocrat_migrator"; diff --git a/tests/conditionalVault.ts b/tests/conditionalVault.ts index cf649c6b..60869806 100644 --- a/tests/conditionalVault.ts +++ b/tests/conditionalVault.ts @@ -35,7 +35,7 @@ import { const { PublicKey, Keypair } = web3; import { ConditionalVault } from "../target/types/conditional_vault"; -import { expectError } from "./utils/utils"; +import { expectError } from "./utils"; import { CONDITIONAL_VAULT_PROGRAM_ID, ConditionalVaultClient, @@ -115,7 +115,7 @@ describe("conditional_vault", async function () { provider ); - vaultClient = await ConditionalVaultClient.createClient({ provider }); + vaultClient = await ConditionalVaultClient.createClient({ provider: provider as any }); payer = vaultProgram.provider.wallet.payer; alice = anchor.web3.Keypair.generate(); @@ -136,8 +136,7 @@ describe("conditional_vault", async function () { [vault] = getVaultAddr( vaultProgram.programId, settlementAuthority.publicKey, - underlyingTokenMint, - proposal + underlyingTokenMint ); vaultUnderlyingTokenAccount = await token.getAssociatedTokenAddress( @@ -365,7 +364,7 @@ describe("conditional_vault", async function () { const balanceAfter = await getAccount(banksClient, token.getAssociatedTokenAddressSync(underlyingTokenMint, payer.publicKey)).then(acc => acc.amount); assert.isTrue(balanceAfter > balanceBefore); - assert.equal(balanceAfter - balanceBefore, 600n); + assert.equal(balanceAfter - balanceBefore, 600); }); }); @@ -460,1055 +459,1056 @@ describe("conditional_vault", async function () { assert.isTrue(balanceAfter == balanceBefore); }); }); - - describe("#initialize_conditional_vault", async function () { - it("initializes vaults", async function () { - await vaultClient - .initializeVaultIx( - settlementAuthority.publicKey, - underlyingTokenMint, - proposal - ) - .rpc(); - - conditionalOnFinalizeMint = getVaultFinalizeMintAddr( - vaultProgram.programId, - vault - )[0]; - - conditionalOnRevertMint = getVaultRevertMintAddr( - vaultProgram.programId, - vault - )[0]; - - const storedVault = await vaultProgram.account.conditionalVault.fetch( - vault - ); - assert.exists(storedVault.status.active); - assert.ok( - storedVault.settlementAuthority.equals(settlementAuthority.publicKey) - ); - assert.ok(storedVault.underlyingTokenMint.equals(underlyingTokenMint)); - assert.ok( - storedVault.underlyingTokenAccount.equals(vaultUnderlyingTokenAccount) - ); - assert.ok( - storedVault.conditionalOnFinalizeTokenMint.equals( - conditionalOnFinalizeMint - ) - ); - assert.ok( - storedVault.conditionalOnRevertTokenMint.equals(conditionalOnRevertMint) - ); - }); - }); - - describe("#mint_conditional_tokens", async function () { - // alice is available throughout the tests, bob is just for mint_conditional_tokens - let bob: Keypair; - let amount = 1000; - let bobUnderlyingTokenAccount: PublicKey; - let bobConditionalOnFinalizeTokenAccount: PublicKey; - let bobConditionalOnRevertTokenAccount: PublicKey; - - beforeEach(async function () { - bob = anchor.web3.Keypair.generate(); - - bobUnderlyingTokenAccount = await createAssociatedTokenAccount( - /* bobUnderlyingTokenAccount = await createAccount( */ - banksClient, - payer, - underlyingTokenMint, - bob.publicKey - ); - - bobConditionalOnFinalizeTokenAccount = await createAssociatedTokenAccount( - banksClient, - payer, - conditionalOnFinalizeMint, - bob.publicKey - ); - bobConditionalOnRevertTokenAccount = await createAssociatedTokenAccount( - banksClient, - payer, - conditionalOnRevertMint, - bob.publicKey - ); - - await mintTo( - banksClient, - payer, - underlyingTokenMint, - bobUnderlyingTokenAccount, - underlyingMintAuthority, - amount - ); - }); - - it("mints conditional tokens", async function () { - await mintConditionalTokens( - vaultProgram, - amount, - bob, - vault, - banksClient - ); - }); - - it("mints conditional tokens twice", async function () { - await mintConditionalTokens( - vaultProgram, - amount / 2, - bob, - vault, - banksClient - ); - - await mintConditionalTokens( - vaultProgram, - amount / 2, - bob, - vault, - banksClient - ); - }); - - it("blocks mints when the user doesn't have enough underlying tokens", async function () { - const callbacks = expectError( - "InsufficientUnderlyingTokens", - "mint suceeded despite user not having enough underlying tokens" - ); - await mintConditionalTokens( - vaultProgram, - amount + 10, - bob, - vault, - banksClient - ).then(callbacks[0], callbacks[1]); - }); - - it("checks that `vault_underlying_token_account` and `conditional_vault` match up", async function () { - const maliciousVaultUnderlyingTokenAccount = await createAccount( - banksClient, - payer, - underlyingTokenMint, - anchor.web3.Keypair.generate().publicKey - ); - - const callbacks = expectError( - "InvalidVaultUnderlyingTokenAccount", - "was able to mint conditional tokens while supplying an invalid vault underlying account" - ); - await mintConditionalTokens( - vaultProgram, - amount, - bob, - vault, - banksClient, - undefined, - undefined, - undefined, - maliciousVaultUnderlyingTokenAccount - ).then(callbacks[0], callbacks[1]); - }); - - it("checks that `user_underlying_token_account` is owned by the user", async function () { - const nonOwnedUserUnderlyingAccount = await createAccount( - banksClient, - payer, - underlyingTokenMint, - anchor.web3.Keypair.generate().publicKey - ); - - await mintTo( - banksClient, - payer, - underlyingTokenMint, - nonOwnedUserUnderlyingAccount, - underlyingMintAuthority, - amount - ); - - const callbacks = expectError( - "ConstraintTokenOwner", - "mint suceeded despite `user_underlying_token_account` not being owned by the user" - ); - - await mintConditionalTokens( - vaultProgram, - amount, - bob, - vault, - banksClient, - nonOwnedUserUnderlyingAccount - ).then(callbacks[0], callbacks[1]); - }); - - it("checks that `user_conditional_on_finalize_token_account` is owned by the user", async function () { - const nonOwnedUserConditionalAccount = await createAccount( - banksClient, - payer, - conditionalOnFinalizeMint, - anchor.web3.Keypair.generate().publicKey - ); - - const callbacks = expectError( - "ConstraintTokenOwner", - "mint suceeded despite `user_conditional_token_account` not being owned by the user" - ); - - await mintConditionalTokens( - vaultProgram, - amount, - bob, - vault, - banksClient, - undefined, - nonOwnedUserConditionalAccount - ).then(callbacks[0], callbacks[1]); - }); - - it("checks that `user_conditional_on_revert_token_account` is owned by the user", async function () { - const nonOwnedUserConditionalAccount = await createAccount( - banksClient, - payer, - conditionalOnRevertMint, - anchor.web3.Keypair.generate().publicKey - ); - - const callbacks = expectError( - "ConstraintTokenOwner", - "mint suceeded despite `user_conditional_token_account` not being owned by the user" - ); - - await mintConditionalTokens( - vaultProgram, - amount, - bob, - vault, - banksClient, - undefined, - nonOwnedUserConditionalAccount - ).then(callbacks[0], callbacks[1]); - }); - - it("checks that `user_conditional_token_account` has `conditional_token_mint` as its mint", async function () { - const wrongConditionalTokenMint = await createMint( - banksClient, - payer, - vault, - vault, - 8 - ); - const wrongMintBobConditionalTokenAccount = await createAccount( - banksClient, - payer, - wrongConditionalTokenMint, - bob.publicKey - ); - - const callbacks = expectError( - "ConstraintTokenMint", - "mint suceeded despite `user_conditional_token_account` having a wrong mint" - ); - - await mintConditionalTokens( - vaultProgram, - amount, - bob, - vault, - banksClient, - undefined, - wrongMintBobConditionalTokenAccount - ).then(callbacks[0], callbacks[1]); - }); - - it("checks that `user_underlying_token_account` has the correct mint", async function () { - const mintAuthority = anchor.web3.Keypair.generate(); - const randomMint = await createMint( - banksClient, - payer, - mintAuthority.publicKey, - mintAuthority.publicKey, - 8 - ); - const wrongMintBobUnderlyingAccount = await createAccount( - banksClient, - payer, - randomMint, - bob.publicKey - ); - - await mintTo( - banksClient, - payer, - randomMint, - wrongMintBobUnderlyingAccount, - mintAuthority, - amount - ); - - const callbacks = expectError( - "ConstraintTokenMint", - "mint suceeded despite `user_underlying_token_account` having the wrong mint" - ); - - await mintConditionalTokens( - vaultProgram, - amount, - bob, - vault, - banksClient, - wrongMintBobUnderlyingAccount - ).then(callbacks[0], callbacks[1]); - }); - - it("checks that `conditional_token_mint` is the one stored in the conditional vault", async function () { - const wrongConditionalTokenMint = await createMint( - banksClient, - payer, - vault, - null, - 10 - ); - - const wrongMintBobConditionalTokenAccount = await createAccount( - banksClient, - payer, - wrongConditionalTokenMint, - bob.publicKey - ); - - const callbacks = expectError( - "InvalidConditionalTokenMint", - "mint suceeded despite `conditional_token_mint` not being the one stored in the conditional vault" - ); - - await mintConditionalTokens( - vaultProgram, - amount, - bob, - vault, - banksClient, - undefined, - wrongMintBobConditionalTokenAccount, - undefined, - undefined, - wrongConditionalTokenMint - ).then(callbacks[0], callbacks[1]); - }); - }); - - describe("#settle_conditional_vault", async function () { - it("allows vaults to be finalized", async function () { - let [vault, _, settlementAuthority] = await generateRandomVault( - vaultProgram, - vaultClient, - payer, - banksClient, - umi - ); - - await vaultProgram.methods - .settleConditionalVault({ finalized: {} }) - .accounts({ - settlementAuthority: settlementAuthority.publicKey, - vault, - }) - .signers([settlementAuthority]) - .rpc(); - }); - - it("allows vaults to be reverted", async function () { - let [vault, _, settlementAuthority] = await generateRandomVault( - vaultProgram, - vaultClient, - payer, - banksClient, - umi - ); - - await vaultProgram.methods - .settleConditionalVault({ reverted: {} }) - .accounts({ - settlementAuthority: settlementAuthority.publicKey, - vault, - }) - .signers([settlementAuthority]) - .rpc(); - }); - - it("disallows vaults from being finalized twice", async function () { - let [vault, _, settlementAuthority] = await generateRandomVault( - vaultProgram, - vaultClient, - payer, - banksClient, - umi - ); - - await vaultProgram.methods - .settleConditionalVault({ finalized: {} }) - .accounts({ - settlementAuthority: settlementAuthority.publicKey, - vault, - }) - .signers([settlementAuthority]) - .rpc(); - - const callbacks = expectError( - "VaultAlreadySettled", - "settle suceeded even though this vault had already been settled" - ); - - await vaultProgram.methods - .settleConditionalVault({ reverted: {} }) - .accounts({ - settlementAuthority: settlementAuthority.publicKey, - vault, - }) - .signers([settlementAuthority]) - .rpc() - .then(callbacks[0], callbacks[1]); - }); - }); - - describe("#redeem_and_merge_conditional_tokens_for_underlying_tokens", async function () { - let bob: Keypair; - let amount = 1000; - let mergeAmount = 10; - let bobUnderlyingTokenAccount: PublicKey; - let bobConditionalOnFinalizeTokenAccount: PublicKey; - let bobConditionalOnRevertTokenAccount: PublicKey; - - beforeEach(async function () { - [vault, underlyingMintAuthority, settlementAuthority] = - await generateRandomVault( - vaultProgram, - vaultClient, - payer, - banksClient, - umi - ); - let storedVault = await vaultProgram.account.conditionalVault.fetch( - vault - ); - underlyingTokenMint = storedVault.underlyingTokenMint; - conditionalOnFinalizeMint = storedVault.conditionalOnFinalizeTokenMint; - conditionalOnRevertMint = storedVault.conditionalOnRevertTokenMint; - vaultUnderlyingTokenAccount = storedVault.underlyingTokenAccount; - - bob = anchor.web3.Keypair.generate(); - - bobUnderlyingTokenAccount = await createAssociatedTokenAccount( - banksClient, - payer, - underlyingTokenMint, - bob.publicKey - ); - - bobConditionalOnFinalizeTokenAccount = await createAssociatedTokenAccount( - banksClient, - payer, - conditionalOnFinalizeMint, - bob.publicKey - ); - - bobConditionalOnRevertTokenAccount = await createAccount( - banksClient, - payer, - conditionalOnRevertMint, - bob.publicKey - ); - - await mintTo( - banksClient, - payer, - underlyingTokenMint, - bobUnderlyingTokenAccount, - underlyingMintAuthority, - amount - ); - - await mintConditionalTokens( - vaultProgram, - amount, - bob, - vault, - banksClient - ); - }); - - it("successfully merges 10 tokens before the vault has been finalized", async function () { - // Assuming the vault has not yet been finalized - - await mergeConditionalTokens( - vaultProgram, - mergeAmount, - bob, - bobConditionalOnFinalizeTokenAccount, - bobConditionalOnRevertTokenAccount, - conditionalOnFinalizeMint, - conditionalOnRevertMint, - bobUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - banksClient - ); - }); - - it("prevents users from merging conditional tokens after the vault has been finalized", async function () { - await vaultProgram.methods - .settleConditionalVault({ finalized: {} }) - .accounts({ - settlementAuthority: settlementAuthority.publicKey, - vault, - }) - .signers([settlementAuthority]) - .rpc(); - - const callbacks = expectError( - "VaultAlreadySettled", - "merge suceeded even though this vault was finalized" - ); - await mergeConditionalTokens( - vaultProgram, - mergeAmount, - bob, - bobConditionalOnFinalizeTokenAccount, - bobConditionalOnRevertTokenAccount, - conditionalOnFinalizeMint, - conditionalOnRevertMint, - bobUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - banksClient - ).then(callbacks[0], callbacks[1]); - }); - - it("allows users to redeem conditional-on-finalize tokens for underlying tokens when a vault has been finalized", async function () { - await vaultProgram.methods - .settleConditionalVault({ finalized: {} }) - .accounts({ - settlementAuthority: settlementAuthority.publicKey, - vault, - }) - .signers([settlementAuthority]) - .rpc(); - - await redeemConditionalTokens( - vaultProgram, - bob, - bobConditionalOnFinalizeTokenAccount, - bobConditionalOnRevertTokenAccount, - conditionalOnFinalizeMint, - conditionalOnRevertMint, - bobUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - banksClient - ); - }); - - it("allows user to redeeming conditional-on-revert tokens for underlying tokens when a vault is reverted", async function () { - await vaultProgram.methods - .settleConditionalVault({ reverted: {} }) - .accounts({ - settlementAuthority: settlementAuthority.publicKey, - vault, - }) - .signers([settlementAuthority]) - .rpc(); - - await redeemConditionalTokens( - vaultProgram, - bob, - bobConditionalOnFinalizeTokenAccount, - bobConditionalOnRevertTokenAccount, - conditionalOnFinalizeMint, - conditionalOnRevertMint, - bobUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - banksClient - ); - }); - - it("prevents users from redeeming conditional tokens while a vault is still active", async function () { - const callbacks = expectError( - "CantRedeemConditionalTokens", - "redemption suceeded even though this vault was still active" - ); - await redeemConditionalTokens( - vaultProgram, - bob, - bobConditionalOnFinalizeTokenAccount, - bobConditionalOnRevertTokenAccount, - conditionalOnFinalizeMint, - conditionalOnRevertMint, - bobUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - banksClient - ).then(callbacks[0], callbacks[1]); - }); - - it("checks that the user has provided the correct conditional token mint", async function () { - const wrongConditionalTokenMint = await createMint( - banksClient, - payer, - vault, - null, - 10 - ); - - const wrongMintBobConditionalTokenAccount = await createAccount( - banksClient, - payer, - wrongConditionalTokenMint, - bob.publicKey - ); - - const callbacks = expectError( - "InvalidConditionalTokenMint", - "redemption suceeded despite `conditional_token_mint` not being the one stored in the conditional vault" - ); - await redeemConditionalTokens( - vaultProgram, - bob, - wrongMintBobConditionalTokenAccount, - bobConditionalOnRevertTokenAccount, - wrongConditionalTokenMint, - conditionalOnRevertMint, - bobUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - banksClient - ).then(callbacks[0], callbacks[1]); - }); - }); }); -async function generateRandomVault( - vaultProgram: VaultProgram, - vaultClient: ConditionalVaultClient, - payer: Keypair, - banksClient: BanksClient, - umi: Umi, - settlementAuthority: Keypair = anchor.web3.Keypair.generate() -): Promise<[PublicKey, Keypair, Keypair]> { - const underlyingMintAuthority = anchor.web3.Keypair.generate(); - - const underlyingTokenMint = await createMint( - banksClient, - payer, - underlyingMintAuthority.publicKey, - null, - 8 - ); - - const tokenMint = fromWeb3JsPublicKey(underlyingTokenMint); - let builder = createMetadataAccountV3(umi, { - mint: tokenMint, - mintAuthority: createSignerFromKeypair( - umi, - fromWeb3JsKeypair(underlyingMintAuthority) - ), - data: { - name: "TOKE", - symbol: "TOKE", - uri: "METADATA_URI", - sellerFeeBasisPoints: 0, - creators: none(), - collection: none(), - uses: none(), - }, - isMutable: false, - collectionDetails: none(), - }); - builder = builder.setBlockhash( - (await umi.rpc.getLatestBlockhash()).blockhash - ); - - const createMetadataResult = await vaultProgram.provider.sendAndConfirm( - toWeb3JsLegacyTransaction(builder.build(umi)), - [underlyingMintAuthority], - { - skipPreflight: true, - commitment: "confirmed", - } - ); - // console.log(createMetadataResult); - - const proposal = Keypair.generate().publicKey; - - const [vault] = getVaultAddr( - vaultProgram.programId, - settlementAuthority.publicKey, - underlyingTokenMint, - proposal - ); - - const conditionalOnFinalizeTokenMint = getVaultFinalizeMintAddr( - vaultProgram.programId, - vault - )[0]; - const conditionalOnRevertTokenMint = getVaultRevertMintAddr( - vaultProgram.programId, - vault - )[0]; - - const vaultUnderlyingTokenAccount = await token.getAssociatedTokenAddress( - underlyingTokenMint, - vault, - true - ); - - // when we have a ts lib, we can consolidate this logic there - const [conditionalOnFinalizeTokenMetadata] = - anchor.web3.PublicKey.findProgramAddressSync( - [ - anchor.utils.bytes.utf8.encode("metadata"), - MPL_TOKEN_METADATA_PROGRAM_ID.toBuffer(), - conditionalOnFinalizeTokenMint.toBuffer(), - ], - MPL_TOKEN_METADATA_PROGRAM_ID - ); - - const [conditionalOnRevertTokenMetadata] = - anchor.web3.PublicKey.findProgramAddressSync( - [ - anchor.utils.bytes.utf8.encode("metadata"), - MPL_TOKEN_METADATA_PROGRAM_ID.toBuffer(), - conditionalOnRevertTokenMint.toBuffer(), - ], - MPL_TOKEN_METADATA_PROGRAM_ID - ); - - const [underlyingTokenMetadata] = - anchor.web3.PublicKey.findProgramAddressSync( - [ - anchor.utils.bytes.utf8.encode("metadata"), - MPL_TOKEN_METADATA_PROGRAM_ID.toBuffer(), - underlyingTokenMint.toBuffer(), - ], - MPL_TOKEN_METADATA_PROGRAM_ID - ); - - // const addMetadataToConditionalTokensIx = await vaultProgram.methods - // .addMetadataToConditionalTokens({ - // proposalNumber: new BN(0), // nonce, - // onFinalizeUri: METADATA_URI, - // onRevertUri: METADATA_URI, - // }) - // .accounts({ - // payer: payer.publicKey, - // vault, - // underlyingTokenMint, - // underlyingTokenMetadata, - // conditionalOnFinalizeTokenMint, - // conditionalOnRevertTokenMint, - // conditionalOnFinalizeTokenMetadata, - // conditionalOnRevertTokenMetadata, - // tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, - // systemProgram: anchor.web3.SystemProgram.programId, - // rent: SYSVAR_RENT_PUBKEY, - // }) - // .instruction(); - const addMetadataToConditionalTokensIx = await vaultClient - .addMetadataToConditionalTokensIx( - vault, - underlyingTokenMint, - 1, - METADATA_URI, - METADATA_URI - ) - .instruction(); - - await vaultClient - .initializeVaultIx( - settlementAuthority.publicKey, - underlyingTokenMint, - proposal - ) - .postInstructions([addMetadataToConditionalTokensIx]) - .rpc(); - - return [vault, underlyingMintAuthority, settlementAuthority]; -} - -export async function mintConditionalTokens( - program: VaultProgram, - amount: number | bigint, - user: Signer, - vault: PublicKey, - banksClient: BanksClient, - userUnderlyingTokenAccount?: PublicKey, - userConditionalOnFinalizeTokenAccount?: PublicKey, - userConditionalOnRevertTokenAccount?: PublicKey, - vaultUnderlyingTokenAccount?: PublicKey, - conditionalOnFinalizeTokenMint?: PublicKey -) { - const storedVault = await program.account.conditionalVault.fetch(vault); - if (!userUnderlyingTokenAccount) { - userUnderlyingTokenAccount = await token.getAssociatedTokenAddress( - storedVault.underlyingTokenMint, - user.publicKey, - true - ); - } - if (!userConditionalOnFinalizeTokenAccount) { - userConditionalOnFinalizeTokenAccount = - await token.getAssociatedTokenAddress( - storedVault.conditionalOnFinalizeTokenMint, - user.publicKey, - true - ); - } - if (!userConditionalOnRevertTokenAccount) { - userConditionalOnRevertTokenAccount = await token.getAssociatedTokenAddress( - storedVault.conditionalOnRevertTokenMint, - user.publicKey, - true - ); - } - if (!vaultUnderlyingTokenAccount) { - vaultUnderlyingTokenAccount = storedVault.underlyingTokenAccount; - } - if (!conditionalOnFinalizeTokenMint) { - conditionalOnFinalizeTokenMint = storedVault.conditionalOnFinalizeTokenMint; - } - - const vaultUnderlyingTokenAccountBefore = await getAccount( - banksClient, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountBefore = await getAccount( - banksClient, - userUnderlyingTokenAccount - ); - const userConditionalOnFinalizeTokenAccountBefore = await getAccount( - banksClient, - userConditionalOnFinalizeTokenAccount - ); - const userConditionalOnRevertTokenAccountBefore = await getAccount( - banksClient, - userConditionalOnRevertTokenAccount - ); - - const bnAmount = new anchor.BN(amount.toString()); - /* try { */ - await program.methods - .mintConditionalTokens(bnAmount) - .accounts({ - authority: user.publicKey, - vault, - vaultUnderlyingTokenAccount, - userUnderlyingTokenAccount, - conditionalOnFinalizeTokenMint, - userConditionalOnFinalizeTokenAccount, - conditionalOnRevertTokenMint: storedVault.conditionalOnRevertTokenMint, - userConditionalOnRevertTokenAccount, - }) - .signers([user]) - .rpc(); - - const vaultUnderlyingTokenAccountAfter = await getAccount( - banksClient, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountAfter = await getAccount( - banksClient, - userUnderlyingTokenAccount - ); - const userConditionalOnFinalizeTokenAccountAfter = await getAccount( - banksClient, - userConditionalOnFinalizeTokenAccount - ); - const userConditionalOnRevertTokenAccountAfter = await getAccount( - banksClient, - userConditionalOnRevertTokenAccount - ); - - assert.equal( - vaultUnderlyingTokenAccountAfter.amount, - vaultUnderlyingTokenAccountBefore.amount + BigInt(amount) - ); - assert.equal( - userUnderlyingTokenAccountAfter.amount, - userUnderlyingTokenAccountBefore.amount - BigInt(amount) - ); - assert.equal( - userConditionalOnFinalizeTokenAccountAfter.amount, - userConditionalOnFinalizeTokenAccountBefore.amount + BigInt(amount) - ); - assert.equal( - userConditionalOnRevertTokenAccountAfter.amount, - userConditionalOnRevertTokenAccountBefore.amount + BigInt(amount) - ); -} - -export async function mergeConditionalTokens( - vaultProgram: VaultProgram, - amount: number | bigint, - user: Signer, - userConditionalOnFinalizeTokenAccount: PublicKey, - userConditionalOnRevertTokenAccount: PublicKey, - conditionalOnFinalizeTokenMint: PublicKey, - conditionalOnRevertTokenMint: PublicKey, - userUnderlyingTokenAccount: PublicKey, - vaultUnderlyingTokenAccount: PublicKey, - vault: PublicKey, - banksClient: BanksClient -) { - const vaultUnderlyingTokenAccountBefore = await getAccount( - banksClient, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountBefore = await getAccount( - banksClient, - userUnderlyingTokenAccount - ); - const userConditionalOnFinalizeTokenAccountBefore = await getAccount( - banksClient, - userConditionalOnFinalizeTokenAccount - ); - const userConditionalOnRevertTokenAccountBefore = await getAccount( - banksClient, - userConditionalOnRevertTokenAccount - ); - - const bnAmount = new anchor.BN(amount.toString()); - await vaultProgram.methods - .mergeConditionalTokensForUnderlyingTokens(bnAmount) - .accounts({ - authority: user.publicKey, - userConditionalOnFinalizeTokenAccount, - userConditionalOnRevertTokenAccount, - userUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - conditionalOnFinalizeTokenMint, - conditionalOnRevertTokenMint, - tokenProgram: token.TOKEN_PROGRAM_ID, - }) - .signers([user]) - .rpc(); - - const vaultUnderlyingTokenAccountAfter = await getAccount( - banksClient, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountAfter = await getAccount( - banksClient, - userUnderlyingTokenAccount - ); - const userConditionalOnFinalizeTokenAccountAfter = await getAccount( - banksClient, - userConditionalOnFinalizeTokenAccount - ); - const userConditionalOnRevertTokenAccountAfter = await getAccount( - banksClient, - userConditionalOnRevertTokenAccount - ); - - assert.equal( - vaultUnderlyingTokenAccountAfter.amount, - vaultUnderlyingTokenAccountBefore.amount - BigInt(amount) - ); - assert.equal( - userUnderlyingTokenAccountAfter.amount, - userUnderlyingTokenAccountBefore.amount + BigInt(amount) - ); - assert.equal( - userConditionalOnFinalizeTokenAccountAfter.amount, - userConditionalOnFinalizeTokenAccountBefore.amount - BigInt(amount) - ); - assert.equal( - userConditionalOnRevertTokenAccountAfter.amount, - userConditionalOnRevertTokenAccountBefore.amount - BigInt(amount) - ); -} - -export async function redeemConditionalTokens( - vaultProgram: VaultProgram, - user: Signer, - userConditionalOnFinalizeTokenAccount: PublicKey, - userConditionalOnRevertTokenAccount: PublicKey, - conditionalOnFinalizeTokenMint: PublicKey, - conditionalOnRevertTokenMint: PublicKey, - userUnderlyingTokenAccount: PublicKey, - vaultUnderlyingTokenAccount: PublicKey, - vault: PublicKey, - banksClient: BanksClient -) { - const vaultUnderlyingTokenAccountBefore = await getAccount( - banksClient, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountBefore = await getAccount( - banksClient, - userUnderlyingTokenAccount - ); - const userConditionalOnFinalizeTokenAccountBefore = await getAccount( - banksClient, - userConditionalOnFinalizeTokenAccount - ); - const userConditionalOnRevertTokenAccountBefore = await getAccount( - banksClient, - userConditionalOnRevertTokenAccount - ); - - await vaultProgram.methods - .redeemConditionalTokensForUnderlyingTokens() - .accounts({ - authority: user.publicKey, - userConditionalOnFinalizeTokenAccount, - userConditionalOnRevertTokenAccount, - userUnderlyingTokenAccount, - vaultUnderlyingTokenAccount, - vault, - conditionalOnFinalizeTokenMint, - conditionalOnRevertTokenMint, - tokenProgram: token.TOKEN_PROGRAM_ID, - }) - .signers([user]) - .rpc(); - - const vaultUnderlyingTokenAccountAfter = await getAccount( - banksClient, - vaultUnderlyingTokenAccount - ); - const userUnderlyingTokenAccountAfter = await getAccount( - banksClient, - userUnderlyingTokenAccount - ); - const userConditionalOnFinalizeTokenAccountAfter = await getAccount( - banksClient, - userConditionalOnFinalizeTokenAccount - ); - const userConditionalOnRevertTokenAccountAfter = await getAccount( - banksClient, - userConditionalOnRevertTokenAccount - ); - - let storedVault = await vaultProgram.account.conditionalVault.fetch(vault); - - let amount; - if (storedVault.status.finalized) { - amount = userConditionalOnFinalizeTokenAccountBefore.amount; - } else { - amount = userConditionalOnRevertTokenAccountBefore.amount; - } - - assert.equal( - vaultUnderlyingTokenAccountAfter.amount, - vaultUnderlyingTokenAccountBefore.amount - BigInt(amount) - ); - assert.equal( - userUnderlyingTokenAccountAfter.amount, - userUnderlyingTokenAccountBefore.amount + BigInt(amount) - ); - assert.equal(userConditionalOnFinalizeTokenAccountAfter.amount, BigInt(0)); - assert.equal(userConditionalOnRevertTokenAccountAfter.amount, BigInt(0)); -} +// describe("#initialize_conditional_vault", async function () { +// it("initializes vaults", async function () { +// await vaultClient +// .initializeVaultIx( +// settlementAuthority.publicKey, +// underlyingTokenMint, +// proposal +// ) +// .rpc(); + +// conditionalOnFinalizeMint = getVaultFinalizeMintAddr( +// vaultProgram.programId, +// vault +// )[0]; + +// conditionalOnRevertMint = getVaultRevertMintAddr( +// vaultProgram.programId, +// vault +// )[0]; + +// const storedVault = await vaultProgram.account.conditionalVault.fetch( +// vault +// ); +// assert.exists(storedVault.status.active); +// assert.ok( +// storedVault.settlementAuthority.equals(settlementAuthority.publicKey) +// ); +// assert.ok(storedVault.underlyingTokenMint.equals(underlyingTokenMint)); +// assert.ok( +// storedVault.underlyingTokenAccount.equals(vaultUnderlyingTokenAccount) +// ); +// assert.ok( +// storedVault.conditionalOnFinalizeTokenMint.equals( +// conditionalOnFinalizeMint +// ) +// ); +// assert.ok( +// storedVault.conditionalOnRevertTokenMint.equals(conditionalOnRevertMint) +// ); +// }); +// }); + +// describe("#mint_conditional_tokens", async function () { +// // alice is available throughout the tests, bob is just for mint_conditional_tokens +// let bob: Keypair; +// let amount = 1000; +// let bobUnderlyingTokenAccount: PublicKey; +// let bobConditionalOnFinalizeTokenAccount: PublicKey; +// let bobConditionalOnRevertTokenAccount: PublicKey; + +// beforeEach(async function () { +// bob = anchor.web3.Keypair.generate(); + +// bobUnderlyingTokenAccount = await createAssociatedTokenAccount( +// /* bobUnderlyingTokenAccount = await createAccount( */ +// banksClient, +// payer, +// underlyingTokenMint, +// bob.publicKey +// ); + +// bobConditionalOnFinalizeTokenAccount = await createAssociatedTokenAccount( +// banksClient, +// payer, +// conditionalOnFinalizeMint, +// bob.publicKey +// ); +// bobConditionalOnRevertTokenAccount = await createAssociatedTokenAccount( +// banksClient, +// payer, +// conditionalOnRevertMint, +// bob.publicKey +// ); + +// await mintTo( +// banksClient, +// payer, +// underlyingTokenMint, +// bobUnderlyingTokenAccount, +// underlyingMintAuthority, +// amount +// ); +// }); + +// it("mints conditional tokens", async function () { +// await mintConditionalTokens( +// vaultProgram, +// amount, +// bob, +// vault, +// banksClient +// ); +// }); + +// it("mints conditional tokens twice", async function () { +// await mintConditionalTokens( +// vaultProgram, +// amount / 2, +// bob, +// vault, +// banksClient +// ); + +// await mintConditionalTokens( +// vaultProgram, +// amount / 2, +// bob, +// vault, +// banksClient +// ); +// }); + +// it("blocks mints when the user doesn't have enough underlying tokens", async function () { +// const callbacks = expectError( +// "InsufficientUnderlyingTokens", +// "mint suceeded despite user not having enough underlying tokens" +// ); +// await mintConditionalTokens( +// vaultProgram, +// amount + 10, +// bob, +// vault, +// banksClient +// ).then(callbacks[0], callbacks[1]); +// }); + +// it("checks that `vault_underlying_token_account` and `conditional_vault` match up", async function () { +// const maliciousVaultUnderlyingTokenAccount = await createAccount( +// banksClient, +// payer, +// underlyingTokenMint, +// anchor.web3.Keypair.generate().publicKey +// ); + +// const callbacks = expectError( +// "InvalidVaultUnderlyingTokenAccount", +// "was able to mint conditional tokens while supplying an invalid vault underlying account" +// ); +// await mintConditionalTokens( +// vaultProgram, +// amount, +// bob, +// vault, +// banksClient, +// undefined, +// undefined, +// undefined, +// maliciousVaultUnderlyingTokenAccount +// ).then(callbacks[0], callbacks[1]); +// }); + +// it("checks that `user_underlying_token_account` is owned by the user", async function () { +// const nonOwnedUserUnderlyingAccount = await createAccount( +// banksClient, +// payer, +// underlyingTokenMint, +// anchor.web3.Keypair.generate().publicKey +// ); + +// await mintTo( +// banksClient, +// payer, +// underlyingTokenMint, +// nonOwnedUserUnderlyingAccount, +// underlyingMintAuthority, +// amount +// ); + +// const callbacks = expectError( +// "ConstraintTokenOwner", +// "mint suceeded despite `user_underlying_token_account` not being owned by the user" +// ); + +// await mintConditionalTokens( +// vaultProgram, +// amount, +// bob, +// vault, +// banksClient, +// nonOwnedUserUnderlyingAccount +// ).then(callbacks[0], callbacks[1]); +// }); + +// it("checks that `user_conditional_on_finalize_token_account` is owned by the user", async function () { +// const nonOwnedUserConditionalAccount = await createAccount( +// banksClient, +// payer, +// conditionalOnFinalizeMint, +// anchor.web3.Keypair.generate().publicKey +// ); + +// const callbacks = expectError( +// "ConstraintTokenOwner", +// "mint suceeded despite `user_conditional_token_account` not being owned by the user" +// ); + +// await mintConditionalTokens( +// vaultProgram, +// amount, +// bob, +// vault, +// banksClient, +// undefined, +// nonOwnedUserConditionalAccount +// ).then(callbacks[0], callbacks[1]); +// }); + +// it("checks that `user_conditional_on_revert_token_account` is owned by the user", async function () { +// const nonOwnedUserConditionalAccount = await createAccount( +// banksClient, +// payer, +// conditionalOnRevertMint, +// anchor.web3.Keypair.generate().publicKey +// ); + +// const callbacks = expectError( +// "ConstraintTokenOwner", +// "mint suceeded despite `user_conditional_token_account` not being owned by the user" +// ); + +// await mintConditionalTokens( +// vaultProgram, +// amount, +// bob, +// vault, +// banksClient, +// undefined, +// nonOwnedUserConditionalAccount +// ).then(callbacks[0], callbacks[1]); +// }); + +// it("checks that `user_conditional_token_account` has `conditional_token_mint` as its mint", async function () { +// const wrongConditionalTokenMint = await createMint( +// banksClient, +// payer, +// vault, +// vault, +// 8 +// ); +// const wrongMintBobConditionalTokenAccount = await createAccount( +// banksClient, +// payer, +// wrongConditionalTokenMint, +// bob.publicKey +// ); + +// const callbacks = expectError( +// "ConstraintTokenMint", +// "mint suceeded despite `user_conditional_token_account` having a wrong mint" +// ); + +// await mintConditionalTokens( +// vaultProgram, +// amount, +// bob, +// vault, +// banksClient, +// undefined, +// wrongMintBobConditionalTokenAccount +// ).then(callbacks[0], callbacks[1]); +// }); + +// it("checks that `user_underlying_token_account` has the correct mint", async function () { +// const mintAuthority = anchor.web3.Keypair.generate(); +// const randomMint = await createMint( +// banksClient, +// payer, +// mintAuthority.publicKey, +// mintAuthority.publicKey, +// 8 +// ); +// const wrongMintBobUnderlyingAccount = await createAccount( +// banksClient, +// payer, +// randomMint, +// bob.publicKey +// ); + +// await mintTo( +// banksClient, +// payer, +// randomMint, +// wrongMintBobUnderlyingAccount, +// mintAuthority, +// amount +// ); + +// const callbacks = expectError( +// "ConstraintTokenMint", +// "mint suceeded despite `user_underlying_token_account` having the wrong mint" +// ); + +// await mintConditionalTokens( +// vaultProgram, +// amount, +// bob, +// vault, +// banksClient, +// wrongMintBobUnderlyingAccount +// ).then(callbacks[0], callbacks[1]); +// }); + +// it("checks that `conditional_token_mint` is the one stored in the conditional vault", async function () { +// const wrongConditionalTokenMint = await createMint( +// banksClient, +// payer, +// vault, +// null, +// 10 +// ); + +// const wrongMintBobConditionalTokenAccount = await createAccount( +// banksClient, +// payer, +// wrongConditionalTokenMint, +// bob.publicKey +// ); + +// const callbacks = expectError( +// "InvalidConditionalTokenMint", +// "mint suceeded despite `conditional_token_mint` not being the one stored in the conditional vault" +// ); + +// await mintConditionalTokens( +// vaultProgram, +// amount, +// bob, +// vault, +// banksClient, +// undefined, +// wrongMintBobConditionalTokenAccount, +// undefined, +// undefined, +// wrongConditionalTokenMint +// ).then(callbacks[0], callbacks[1]); +// }); +// }); + +// describe("#settle_conditional_vault", async function () { +// it("allows vaults to be finalized", async function () { +// let [vault, _, settlementAuthority] = await generateRandomVault( +// vaultProgram, +// vaultClient, +// payer, +// banksClient, +// umi +// ); + +// await vaultProgram.methods +// .settleConditionalVault({ finalized: {} }) +// .accounts({ +// settlementAuthority: settlementAuthority.publicKey, +// vault, +// }) +// .signers([settlementAuthority]) +// .rpc(); +// }); + +// it("allows vaults to be reverted", async function () { +// let [vault, _, settlementAuthority] = await generateRandomVault( +// vaultProgram, +// vaultClient, +// payer, +// banksClient, +// umi +// ); + +// await vaultProgram.methods +// .settleConditionalVault({ reverted: {} }) +// .accounts({ +// settlementAuthority: settlementAuthority.publicKey, +// vault, +// }) +// .signers([settlementAuthority]) +// .rpc(); +// }); + +// it("disallows vaults from being finalized twice", async function () { +// let [vault, _, settlementAuthority] = await generateRandomVault( +// vaultProgram, +// vaultClient, +// payer, +// banksClient, +// umi +// ); + +// await vaultProgram.methods +// .settleConditionalVault({ finalized: {} }) +// .accounts({ +// settlementAuthority: settlementAuthority.publicKey, +// vault, +// }) +// .signers([settlementAuthority]) +// .rpc(); + +// const callbacks = expectError( +// "VaultAlreadySettled", +// "settle suceeded even though this vault had already been settled" +// ); + +// await vaultProgram.methods +// .settleConditionalVault({ reverted: {} }) +// .accounts({ +// settlementAuthority: settlementAuthority.publicKey, +// vault, +// }) +// .signers([settlementAuthority]) +// .rpc() +// .then(callbacks[0], callbacks[1]); +// }); +// }); + +// describe("#redeem_and_merge_conditional_tokens_for_underlying_tokens", async function () { +// let bob: Keypair; +// let amount = 1000; +// let mergeAmount = 10; +// let bobUnderlyingTokenAccount: PublicKey; +// let bobConditionalOnFinalizeTokenAccount: PublicKey; +// let bobConditionalOnRevertTokenAccount: PublicKey; + +// beforeEach(async function () { +// [vault, underlyingMintAuthority, settlementAuthority] = +// await generateRandomVault( +// vaultProgram, +// vaultClient, +// payer, +// banksClient, +// umi +// ); +// let storedVault = await vaultProgram.account.conditionalVault.fetch( +// vault +// ); +// underlyingTokenMint = storedVault.underlyingTokenMint; +// conditionalOnFinalizeMint = storedVault.conditionalOnFinalizeTokenMint; +// conditionalOnRevertMint = storedVault.conditionalOnRevertTokenMint; +// vaultUnderlyingTokenAccount = storedVault.underlyingTokenAccount; + +// bob = anchor.web3.Keypair.generate(); + +// bobUnderlyingTokenAccount = await createAssociatedTokenAccount( +// banksClient, +// payer, +// underlyingTokenMint, +// bob.publicKey +// ); + +// bobConditionalOnFinalizeTokenAccount = await createAssociatedTokenAccount( +// banksClient, +// payer, +// conditionalOnFinalizeMint, +// bob.publicKey +// ); + +// bobConditionalOnRevertTokenAccount = await createAccount( +// banksClient, +// payer, +// conditionalOnRevertMint, +// bob.publicKey +// ); + +// await mintTo( +// banksClient, +// payer, +// underlyingTokenMint, +// bobUnderlyingTokenAccount, +// underlyingMintAuthority, +// amount +// ); + +// await mintConditionalTokens( +// vaultProgram, +// amount, +// bob, +// vault, +// banksClient +// ); +// }); + +// it("successfully merges 10 tokens before the vault has been finalized", async function () { +// // Assuming the vault has not yet been finalized + +// await mergeConditionalTokens( +// vaultProgram, +// mergeAmount, +// bob, +// bobConditionalOnFinalizeTokenAccount, +// bobConditionalOnRevertTokenAccount, +// conditionalOnFinalizeMint, +// conditionalOnRevertMint, +// bobUnderlyingTokenAccount, +// vaultUnderlyingTokenAccount, +// vault, +// banksClient +// ); +// }); + +// it("prevents users from merging conditional tokens after the vault has been finalized", async function () { +// await vaultProgram.methods +// .settleConditionalVault({ finalized: {} }) +// .accounts({ +// settlementAuthority: settlementAuthority.publicKey, +// vault, +// }) +// .signers([settlementAuthority]) +// .rpc(); + +// const callbacks = expectError( +// "VaultAlreadySettled", +// "merge suceeded even though this vault was finalized" +// ); +// await mergeConditionalTokens( +// vaultProgram, +// mergeAmount, +// bob, +// bobConditionalOnFinalizeTokenAccount, +// bobConditionalOnRevertTokenAccount, +// conditionalOnFinalizeMint, +// conditionalOnRevertMint, +// bobUnderlyingTokenAccount, +// vaultUnderlyingTokenAccount, +// vault, +// banksClient +// ).then(callbacks[0], callbacks[1]); +// }); + +// it("allows users to redeem conditional-on-finalize tokens for underlying tokens when a vault has been finalized", async function () { +// await vaultProgram.methods +// .settleConditionalVault({ finalized: {} }) +// .accounts({ +// settlementAuthority: settlementAuthority.publicKey, +// vault, +// }) +// .signers([settlementAuthority]) +// .rpc(); + +// await redeemConditionalTokens( +// vaultProgram, +// bob, +// bobConditionalOnFinalizeTokenAccount, +// bobConditionalOnRevertTokenAccount, +// conditionalOnFinalizeMint, +// conditionalOnRevertMint, +// bobUnderlyingTokenAccount, +// vaultUnderlyingTokenAccount, +// vault, +// banksClient +// ); +// }); + +// it("allows user to redeeming conditional-on-revert tokens for underlying tokens when a vault is reverted", async function () { +// await vaultProgram.methods +// .settleConditionalVault({ reverted: {} }) +// .accounts({ +// settlementAuthority: settlementAuthority.publicKey, +// vault, +// }) +// .signers([settlementAuthority]) +// .rpc(); + +// await redeemConditionalTokens( +// vaultProgram, +// bob, +// bobConditionalOnFinalizeTokenAccount, +// bobConditionalOnRevertTokenAccount, +// conditionalOnFinalizeMint, +// conditionalOnRevertMint, +// bobUnderlyingTokenAccount, +// vaultUnderlyingTokenAccount, +// vault, +// banksClient +// ); +// }); + +// it("prevents users from redeeming conditional tokens while a vault is still active", async function () { +// const callbacks = expectError( +// "CantRedeemConditionalTokens", +// "redemption suceeded even though this vault was still active" +// ); +// await redeemConditionalTokens( +// vaultProgram, +// bob, +// bobConditionalOnFinalizeTokenAccount, +// bobConditionalOnRevertTokenAccount, +// conditionalOnFinalizeMint, +// conditionalOnRevertMint, +// bobUnderlyingTokenAccount, +// vaultUnderlyingTokenAccount, +// vault, +// banksClient +// ).then(callbacks[0], callbacks[1]); +// }); + +// it("checks that the user has provided the correct conditional token mint", async function () { +// const wrongConditionalTokenMint = await createMint( +// banksClient, +// payer, +// vault, +// null, +// 10 +// ); + +// const wrongMintBobConditionalTokenAccount = await createAccount( +// banksClient, +// payer, +// wrongConditionalTokenMint, +// bob.publicKey +// ); + +// const callbacks = expectError( +// "InvalidConditionalTokenMint", +// "redemption suceeded despite `conditional_token_mint` not being the one stored in the conditional vault" +// ); +// await redeemConditionalTokens( +// vaultProgram, +// bob, +// wrongMintBobConditionalTokenAccount, +// bobConditionalOnRevertTokenAccount, +// wrongConditionalTokenMint, +// conditionalOnRevertMint, +// bobUnderlyingTokenAccount, +// vaultUnderlyingTokenAccount, +// vault, +// banksClient +// ).then(callbacks[0], callbacks[1]); +// }); +// }); +// }); + +// async function generateRandomVault( +// vaultProgram: VaultProgram, +// vaultClient: ConditionalVaultClient, +// payer: Keypair, +// banksClient: BanksClient, +// umi: Umi, +// settlementAuthority: Keypair = anchor.web3.Keypair.generate() +// ): Promise<[PublicKey, Keypair, Keypair]> { +// const underlyingMintAuthority = anchor.web3.Keypair.generate(); + +// const underlyingTokenMint = await createMint( +// banksClient, +// payer, +// underlyingMintAuthority.publicKey, +// null, +// 8 +// ); + +// const tokenMint = fromWeb3JsPublicKey(underlyingTokenMint); +// let builder = createMetadataAccountV3(umi, { +// mint: tokenMint, +// mintAuthority: createSignerFromKeypair( +// umi, +// fromWeb3JsKeypair(underlyingMintAuthority) +// ), +// data: { +// name: "TOKE", +// symbol: "TOKE", +// uri: "METADATA_URI", +// sellerFeeBasisPoints: 0, +// creators: none(), +// collection: none(), +// uses: none(), +// }, +// isMutable: false, +// collectionDetails: none(), +// }); +// builder = builder.setBlockhash( +// (await umi.rpc.getLatestBlockhash()).blockhash +// ); + +// const createMetadataResult = await vaultProgram.provider.sendAndConfirm( +// toWeb3JsLegacyTransaction(builder.build(umi)), +// [underlyingMintAuthority], +// { +// skipPreflight: true, +// commitment: "confirmed", +// } +// ); +// // console.log(createMetadataResult); + +// const proposal = Keypair.generate().publicKey; + +// const [vault] = getVaultAddr( +// vaultProgram.programId, +// settlementAuthority.publicKey, +// underlyingTokenMint, +// proposal +// ); + +// const conditionalOnFinalizeTokenMint = getVaultFinalizeMintAddr( +// vaultProgram.programId, +// vault +// )[0]; +// const conditionalOnRevertTokenMint = getVaultRevertMintAddr( +// vaultProgram.programId, +// vault +// )[0]; + +// const vaultUnderlyingTokenAccount = await token.getAssociatedTokenAddress( +// underlyingTokenMint, +// vault, +// true +// ); + +// // when we have a ts lib, we can consolidate this logic there +// const [conditionalOnFinalizeTokenMetadata] = +// anchor.web3.PublicKey.findProgramAddressSync( +// [ +// anchor.utils.bytes.utf8.encode("metadata"), +// MPL_TOKEN_METADATA_PROGRAM_ID.toBuffer(), +// conditionalOnFinalizeTokenMint.toBuffer(), +// ], +// MPL_TOKEN_METADATA_PROGRAM_ID +// ); + +// const [conditionalOnRevertTokenMetadata] = +// anchor.web3.PublicKey.findProgramAddressSync( +// [ +// anchor.utils.bytes.utf8.encode("metadata"), +// MPL_TOKEN_METADATA_PROGRAM_ID.toBuffer(), +// conditionalOnRevertTokenMint.toBuffer(), +// ], +// MPL_TOKEN_METADATA_PROGRAM_ID +// ); + +// const [underlyingTokenMetadata] = +// anchor.web3.PublicKey.findProgramAddressSync( +// [ +// anchor.utils.bytes.utf8.encode("metadata"), +// MPL_TOKEN_METADATA_PROGRAM_ID.toBuffer(), +// underlyingTokenMint.toBuffer(), +// ], +// MPL_TOKEN_METADATA_PROGRAM_ID +// ); + +// // const addMetadataToConditionalTokensIx = await vaultProgram.methods +// // .addMetadataToConditionalTokens({ +// // proposalNumber: new BN(0), // nonce, +// // onFinalizeUri: METADATA_URI, +// // onRevertUri: METADATA_URI, +// // }) +// // .accounts({ +// // payer: payer.publicKey, +// // vault, +// // underlyingTokenMint, +// // underlyingTokenMetadata, +// // conditionalOnFinalizeTokenMint, +// // conditionalOnRevertTokenMint, +// // conditionalOnFinalizeTokenMetadata, +// // conditionalOnRevertTokenMetadata, +// // tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, +// // systemProgram: anchor.web3.SystemProgram.programId, +// // rent: SYSVAR_RENT_PUBKEY, +// // }) +// // .instruction(); +// const addMetadataToConditionalTokensIx = await vaultClient +// .addMetadataToConditionalTokensIx( +// vault, +// underlyingTokenMint, +// 1, +// METADATA_URI, +// METADATA_URI +// ) +// .instruction(); + +// await vaultClient +// .initializeVaultIx( +// settlementAuthority.publicKey, +// underlyingTokenMint, +// proposal +// ) +// .postInstructions([addMetadataToConditionalTokensIx]) +// .rpc(); + +// return [vault, underlyingMintAuthority, settlementAuthority]; +// } + +// export async function mintConditionalTokens( +// program: VaultProgram, +// amount: number | bigint, +// user: Signer, +// vault: PublicKey, +// banksClient: BanksClient, +// userUnderlyingTokenAccount?: PublicKey, +// userConditionalOnFinalizeTokenAccount?: PublicKey, +// userConditionalOnRevertTokenAccount?: PublicKey, +// vaultUnderlyingTokenAccount?: PublicKey, +// conditionalOnFinalizeTokenMint?: PublicKey +// ) { +// const storedVault = await program.account.conditionalVault.fetch(vault); +// if (!userUnderlyingTokenAccount) { +// userUnderlyingTokenAccount = await token.getAssociatedTokenAddress( +// storedVault.underlyingTokenMint, +// user.publicKey, +// true +// ); +// } +// if (!userConditionalOnFinalizeTokenAccount) { +// userConditionalOnFinalizeTokenAccount = +// await token.getAssociatedTokenAddress( +// storedVault.conditionalOnFinalizeTokenMint, +// user.publicKey, +// true +// ); +// } +// if (!userConditionalOnRevertTokenAccount) { +// userConditionalOnRevertTokenAccount = await token.getAssociatedTokenAddress( +// storedVault.conditionalOnRevertTokenMint, +// user.publicKey, +// true +// ); +// } +// if (!vaultUnderlyingTokenAccount) { +// vaultUnderlyingTokenAccount = storedVault.underlyingTokenAccount; +// } +// if (!conditionalOnFinalizeTokenMint) { +// conditionalOnFinalizeTokenMint = storedVault.conditionalOnFinalizeTokenMint; +// } + +// const vaultUnderlyingTokenAccountBefore = await getAccount( +// banksClient, +// vaultUnderlyingTokenAccount +// ); +// const userUnderlyingTokenAccountBefore = await getAccount( +// banksClient, +// userUnderlyingTokenAccount +// ); +// const userConditionalOnFinalizeTokenAccountBefore = await getAccount( +// banksClient, +// userConditionalOnFinalizeTokenAccount +// ); +// const userConditionalOnRevertTokenAccountBefore = await getAccount( +// banksClient, +// userConditionalOnRevertTokenAccount +// ); + +// const bnAmount = new anchor.BN(amount.toString()); +// /* try { */ +// await program.methods +// .mintConditionalTokens(bnAmount) +// .accounts({ +// authority: user.publicKey, +// vault, +// vaultUnderlyingTokenAccount, +// userUnderlyingTokenAccount, +// conditionalOnFinalizeTokenMint, +// userConditionalOnFinalizeTokenAccount, +// conditionalOnRevertTokenMint: storedVault.conditionalOnRevertTokenMint, +// userConditionalOnRevertTokenAccount, +// }) +// .signers([user]) +// .rpc(); + +// const vaultUnderlyingTokenAccountAfter = await getAccount( +// banksClient, +// vaultUnderlyingTokenAccount +// ); +// const userUnderlyingTokenAccountAfter = await getAccount( +// banksClient, +// userUnderlyingTokenAccount +// ); +// const userConditionalOnFinalizeTokenAccountAfter = await getAccount( +// banksClient, +// userConditionalOnFinalizeTokenAccount +// ); +// const userConditionalOnRevertTokenAccountAfter = await getAccount( +// banksClient, +// userConditionalOnRevertTokenAccount +// ); + +// assert.equal( +// vaultUnderlyingTokenAccountAfter.amount, +// vaultUnderlyingTokenAccountBefore.amount + BigInt(amount) +// ); +// assert.equal( +// userUnderlyingTokenAccountAfter.amount, +// userUnderlyingTokenAccountBefore.amount - BigInt(amount) +// ); +// assert.equal( +// userConditionalOnFinalizeTokenAccountAfter.amount, +// userConditionalOnFinalizeTokenAccountBefore.amount + BigInt(amount) +// ); +// assert.equal( +// userConditionalOnRevertTokenAccountAfter.amount, +// userConditionalOnRevertTokenAccountBefore.amount + BigInt(amount) +// ); +// } + +// export async function mergeConditionalTokens( +// vaultProgram: VaultProgram, +// amount: number | bigint, +// user: Signer, +// userConditionalOnFinalizeTokenAccount: PublicKey, +// userConditionalOnRevertTokenAccount: PublicKey, +// conditionalOnFinalizeTokenMint: PublicKey, +// conditionalOnRevertTokenMint: PublicKey, +// userUnderlyingTokenAccount: PublicKey, +// vaultUnderlyingTokenAccount: PublicKey, +// vault: PublicKey, +// banksClient: BanksClient +// ) { +// const vaultUnderlyingTokenAccountBefore = await getAccount( +// banksClient, +// vaultUnderlyingTokenAccount +// ); +// const userUnderlyingTokenAccountBefore = await getAccount( +// banksClient, +// userUnderlyingTokenAccount +// ); +// const userConditionalOnFinalizeTokenAccountBefore = await getAccount( +// banksClient, +// userConditionalOnFinalizeTokenAccount +// ); +// const userConditionalOnRevertTokenAccountBefore = await getAccount( +// banksClient, +// userConditionalOnRevertTokenAccount +// ); + +// const bnAmount = new anchor.BN(amount.toString()); +// await vaultProgram.methods +// .mergeConditionalTokensForUnderlyingTokens(bnAmount) +// .accounts({ +// authority: user.publicKey, +// userConditionalOnFinalizeTokenAccount, +// userConditionalOnRevertTokenAccount, +// userUnderlyingTokenAccount, +// vaultUnderlyingTokenAccount, +// vault, +// conditionalOnFinalizeTokenMint, +// conditionalOnRevertTokenMint, +// tokenProgram: token.TOKEN_PROGRAM_ID, +// }) +// .signers([user]) +// .rpc(); + +// const vaultUnderlyingTokenAccountAfter = await getAccount( +// banksClient, +// vaultUnderlyingTokenAccount +// ); +// const userUnderlyingTokenAccountAfter = await getAccount( +// banksClient, +// userUnderlyingTokenAccount +// ); +// const userConditionalOnFinalizeTokenAccountAfter = await getAccount( +// banksClient, +// userConditionalOnFinalizeTokenAccount +// ); +// const userConditionalOnRevertTokenAccountAfter = await getAccount( +// banksClient, +// userConditionalOnRevertTokenAccount +// ); + +// assert.equal( +// vaultUnderlyingTokenAccountAfter.amount, +// vaultUnderlyingTokenAccountBefore.amount - BigInt(amount) +// ); +// assert.equal( +// userUnderlyingTokenAccountAfter.amount, +// userUnderlyingTokenAccountBefore.amount + BigInt(amount) +// ); +// assert.equal( +// userConditionalOnFinalizeTokenAccountAfter.amount, +// userConditionalOnFinalizeTokenAccountBefore.amount - BigInt(amount) +// ); +// assert.equal( +// userConditionalOnRevertTokenAccountAfter.amount, +// userConditionalOnRevertTokenAccountBefore.amount - BigInt(amount) +// ); +// } + +// export async function redeemConditionalTokens( +// vaultProgram: VaultProgram, +// user: Signer, +// userConditionalOnFinalizeTokenAccount: PublicKey, +// userConditionalOnRevertTokenAccount: PublicKey, +// conditionalOnFinalizeTokenMint: PublicKey, +// conditionalOnRevertTokenMint: PublicKey, +// userUnderlyingTokenAccount: PublicKey, +// vaultUnderlyingTokenAccount: PublicKey, +// vault: PublicKey, +// banksClient: BanksClient +// ) { +// const vaultUnderlyingTokenAccountBefore = await getAccount( +// banksClient, +// vaultUnderlyingTokenAccount +// ); +// const userUnderlyingTokenAccountBefore = await getAccount( +// banksClient, +// userUnderlyingTokenAccount +// ); +// const userConditionalOnFinalizeTokenAccountBefore = await getAccount( +// banksClient, +// userConditionalOnFinalizeTokenAccount +// ); +// const userConditionalOnRevertTokenAccountBefore = await getAccount( +// banksClient, +// userConditionalOnRevertTokenAccount +// ); + +// await vaultProgram.methods +// .redeemConditionalTokensForUnderlyingTokens() +// .accounts({ +// authority: user.publicKey, +// userConditionalOnFinalizeTokenAccount, +// userConditionalOnRevertTokenAccount, +// userUnderlyingTokenAccount, +// vaultUnderlyingTokenAccount, +// vault, +// conditionalOnFinalizeTokenMint, +// conditionalOnRevertTokenMint, +// tokenProgram: token.TOKEN_PROGRAM_ID, +// }) +// .signers([user]) +// .rpc(); + +// const vaultUnderlyingTokenAccountAfter = await getAccount( +// banksClient, +// vaultUnderlyingTokenAccount +// ); +// const userUnderlyingTokenAccountAfter = await getAccount( +// banksClient, +// userUnderlyingTokenAccount +// ); +// const userConditionalOnFinalizeTokenAccountAfter = await getAccount( +// banksClient, +// userConditionalOnFinalizeTokenAccount +// ); +// const userConditionalOnRevertTokenAccountAfter = await getAccount( +// banksClient, +// userConditionalOnRevertTokenAccount +// ); + +// let storedVault = await vaultProgram.account.conditionalVault.fetch(vault); + +// let amount; +// if (storedVault.status.finalized) { +// amount = userConditionalOnFinalizeTokenAccountBefore.amount; +// } else { +// amount = userConditionalOnRevertTokenAccountBefore.amount; +// } + +// assert.equal( +// vaultUnderlyingTokenAccountAfter.amount, +// vaultUnderlyingTokenAccountBefore.amount - BigInt(amount) +// ); +// assert.equal( +// userUnderlyingTokenAccountAfter.amount, +// userUnderlyingTokenAccountBefore.amount + BigInt(amount) +// ); +// assert.equal(userConditionalOnFinalizeTokenAccountAfter.amount, BigInt(0)); +// assert.equal(userConditionalOnRevertTokenAccountAfter.amount, BigInt(0)); +// } diff --git a/tests/utils/utils.ts b/tests/utils.ts similarity index 100% rename from tests/utils/utils.ts rename to tests/utils.ts diff --git a/tests/utils/bankrunUtils.ts b/tests/utils/bankrunUtils.ts deleted file mode 100644 index 43f74ebe..00000000 --- a/tests/utils/bankrunUtils.ts +++ /dev/null @@ -1,226 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -const { Signer, PublicKey, Transaction, Commitment, ConfirmOptions } = - anchor.web3; -import * as token from "@solana/spl-token"; -import { - BanksClient, - BanksTransactionMeta, - ProgramTestContext, -} from "solana-bankrun"; - -export async function createMint( - banksClient: BanksClient, - payer: Keypair, - mintAuthority: PublicKey, - freezeAuthority: PublicKey | null, - decimals: number, - keypair = anchor.web3.Keypair.generate(), - programId = token.TOKEN_PROGRAM_ID -): PublicKey { - let rent = await banksClient.getRent(); - - const tx = new anchor.web3.Transaction().add( - anchor.web3.SystemProgram.createAccount({ - fromPubkey: payer.publicKey, - newAccountPubkey: keypair.publicKey, - space: token.MINT_SIZE, - lamports: Number(await rent.minimumBalance(BigInt(token.MINT_SIZE))), - programId: token.TOKEN_PROGRAM_ID, - }), - token.createInitializeMint2Instruction( - keypair.publicKey, - decimals, - mintAuthority, - freezeAuthority, - programId - ) - ); - [tx.recentBlockhash] = await banksClient.getLatestBlockhash(); - tx.sign(payer, keypair); - - await banksClient.processTransaction(tx); - - return keypair.publicKey; -} - -export async function createAccount( - banksClient: BanksClient, - payer: Signer, - mint: PublicKey, - owner: PublicKey, - keypair?: Keypair, - confirmOptions?: ConfirmOptions, - programId = token.TOKEN_PROGRAM_ID -): Promise { - let rent = await banksClient.getRent(); - // If a keypair isn't provided, create the associated token account and return its address - if (!keypair) - return await createAssociatedTokenAccount( - banksClient, - payer, - mint, - owner, - programId - ); - - // Otherwise, create the account with the provided keypair and return its public key - const mintState = await getMint( - banksClient, - mint, - confirmOptions?.commitment, - programId - ); - const space = token.getAccountLenForMint(mintState); - - const tx = new Transaction().add( - SystemProgram.createAccount({ - fromPubkey: payer.publicKey, - newAccountPubkey: keypair.publicKey, - space, - lamports: Number(await rent.minimumBalance(BigInt(space))), - programId, - }), - token.createInitializeAccountInstruction( - keypair.publicKey, - mint, - owner, - programId - ) - ); - [tx.recentBlockhash] = await banksClient.getLatestBlockhash(); - tx.sign(payer, keypair); - - await banksClient.processTransaction(tx); - - return keypair.publicKey; -} - -export async function createAssociatedTokenAccount( - banksClient: BanksClient, - payer: Signer, - mint: PublicKey, - owner: PublicKey, - programId = token.TOKEN_PROGRAM_ID, - associatedTokenProgramId = token.ASSOCIATED_TOKEN_PROGRAM_ID -): Promise { - const associatedToken = token.getAssociatedTokenAddressSync( - mint, - owner, - false, - programId, - associatedTokenProgramId - ); - - const tx = new Transaction().add( - token.createAssociatedTokenAccountInstruction( - payer.publicKey, - associatedToken, - owner, - mint, - programId, - associatedTokenProgramId - ) - ); - - [tx.recentBlockhash] = await banksClient.getLatestBlockhash(); - tx.sign(payer); - - await banksClient.processTransaction(tx); - - return associatedToken; -} - -export async function getMint( - banksClient: BanksClient, - address: PublicKey, - commitment?: Commitment, - programId = token.TOKEN_PROGRAM_ID -): Promise { - const info = await banksClient.getAccountInfo(address, commitment); - return token.unpackMint(address, info, programId); -} - -// `mintTo` without the mintAuthority signer -// uses bankrun's special `setAccount` function -export async function mintToOverride( - context: ProgramTestContext, - destination: PublicKey, - amount: bigint -) { - const banksClient = context.banksClient; - - const existingAccount = await getAccount(banksClient, destination); - const { mint, owner } = existingAccount; - - const accData = Buffer.alloc(token.ACCOUNT_SIZE); - token.AccountLayout.encode( - { - mint, - owner, - amount, - delegateOption: 0, - delegate: PublicKey.default, - delegatedAmount: 0n, - state: 1, - isNativeOption: 0, - isNative: 0n, - closeAuthorityOption: 0, - closeAuthority: PublicKey.default, - }, - accData - ); - - await context.setAccount(destination, { - data: accData, - executable: false, - lamports: 1_000_000_000n, - owner: token.TOKEN_PROGRAM_ID, - }); -} - -export async function mintTo( - banksClient: BanksClient, - payer: Signer, - mint: PublicKey, - destination: PublicKey, - authority: Signer | PublicKey, - amount: number | bigint, - multiSigners: Signer[] = [], - programId = token.TOKEN_PROGRAM_ID -): Promise { - const [authorityPublicKey, signers] = getSigners(authority, multiSigners); - - const tx = new Transaction().add( - token.createMintToInstruction( - mint, - destination, - authorityPublicKey, - amount, - multiSigners, - programId - ) - ); - [tx.recentBlockhash] = await banksClient.getLatestBlockhash(); - tx.sign(payer, ...signers); - - return await banksClient.processTransaction(tx); -} - -export function getSigners( - signerOrMultisig: Signer | PublicKey, - multiSigners: Signer[] -): [PublicKey, Signer[]] { - return signerOrMultisig instanceof PublicKey - ? [signerOrMultisig, multiSigners] - : [signerOrMultisig.publicKey, [signerOrMultisig]]; -} - -export async function getAccount( - banksClient: BanksClient, - address: PublicKey, - commitment?: Commitment, - programId = token.TOKEN_PROGRAM_ID -): Promise { - const info = await banksClient.getAccount(address, commitment); - return token.unpackAccount(address, info, programId); -} diff --git a/tests/utils/pdaGenerator.ts b/tests/utils/pdaGenerator.ts deleted file mode 100644 index d9c16a35..00000000 --- a/tests/utils/pdaGenerator.ts +++ /dev/null @@ -1,79 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; - -import { Program, PublicKey } from "./metaDAO"; - -export type AddressAndBump = [PublicKey, number]; - -export class PDAGenerator { - program: Program; - - constructor(program: Program) { - this.program = program; - } - - generateMetaDAOPDAAddress(): AddressAndBump { - return anchor.web3.PublicKey.findProgramAddressSync( - [anchor.utils.bytes.utf8.encode("WWCACOTMICMIBMHAFTTWYGHMB")], - this.program.programId - ); - } - - generateMemberPDAAddress(name: string): AddressAndBump { - return anchor.web3.PublicKey.findProgramAddressSync( - [ - anchor.utils.bytes.utf8.encode("member"), - anchor.utils.bytes.utf8.encode(name), - ], - this.program.programId - ); - } - - generateTreasuryPDAAddress(memberAddress: PublicKey): AddressAndBump { - return anchor.web3.PublicKey.findProgramAddressSync( - [anchor.utils.bytes.utf8.encode("treasury"), memberAddress.toBuffer()], - this.program.programId - ); - } - - generateConditionalExpressionPDAAddress( - proposal: PublicKey, - redeemableOnPass: boolean - ): AddressAndBump { - return anchor.web3.PublicKey.findProgramAddressSync( - [ - anchor.utils.bytes.utf8.encode("conditional_expression"), - proposal.toBuffer(), - Buffer.from([redeemableOnPass]), - ], - this.program.programId - ); - } - - generateVaultPDAAddress( - conditionalExpressionAddress: PublicKey, - underlyingMint: PublicKey - ): AddressAndBump { - return anchor.web3.PublicKey.findProgramAddressSync( - [ - anchor.utils.bytes.utf8.encode("vault"), - conditionalExpressionAddress.toBuffer(), - underlyingMint.toBuffer(), - ], - this.program.programId - ); - } - - generateDepositSlipPDAAddress( - conditionalVault: PublicKey, - user: PublicKey - ): AddressAndBump { - return anchor.web3.PublicKey.findProgramAddressSync( - [ - anchor.utils.bytes.utf8.encode("deposit_slip"), - conditionalVault.toBuffer(), - user.toBuffer(), - ], - this.program.programId - ); - } -} From b54ab6f0abeee52292a1603498b5885f28f53445 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 16/52] Restructure existing tests into `programs` folder --- Anchor.toml | 2 +- run.sh | 2 +- sdk/src/utils/cu.ts | 2 +- tests/{ => programs}/amm.ts | 8 ++++---- tests/{ => programs}/autocrat.ts | 19 +++++++------------ tests/{ => programs}/conditionalVault.ts | 5 ++--- tests/{ => programs}/migrator.ts | 3 +-- tests/{ => programs}/timelock.ts | 4 ++-- tests/utils.ts | 22 +++------------------- 9 files changed, 22 insertions(+), 45 deletions(-) rename tests/{ => programs}/amm.ts (98%) rename tests/{ => programs}/autocrat.ts (96%) rename tests/{ => programs}/conditionalVault.ts (99%) rename tests/{ => programs}/migrator.ts (97%) rename tests/{ => programs}/timelock.ts (99%) diff --git a/Anchor.toml b/Anchor.toml index d8c619df..4168d8aa 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -1,5 +1,5 @@ [scripts] -test = "npx mocha --node-option require=ts-node/register --extension ts -t 10000000 tests/*.ts" +test = "npx mocha --node-option require=ts-node/register --extension ts -t 10000000 tests/**/*.ts" propose = "yarn run ts-node scripts/initializeProposal.ts" initialize-dao = "yarn run ts-node scripts/initializeDao.ts" finalize = "yarn run ts-node scripts/finalizeProposal.ts" diff --git a/run.sh b/run.sh index 9054006e..3c14b828 100755 --- a/run.sh +++ b/run.sh @@ -14,7 +14,7 @@ test_vault() { #sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/conditionalVault.ts"/' Anchor.toml #sleep 10 && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/*.ts"/' Anchor.toml & find programs tests sdk | entr -sc \ - "anchor build -p conditional_vault && (cd sdk && yarn build) && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/conditionalVault.ts\"/' Anchor.toml && (sleep 3 && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/*.ts\"/' Anchor.toml) & RUST_LOG= anchor test --skip-build" + "anchor build -p conditional_vault && (cd sdk && yarn build) && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/programs\/conditionalVault.ts\"/' Anchor.toml && (sleep 3 && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/**/*.ts\"/' Anchor.toml) & RUST_LOG= anchor test --skip-build" } test_no_build() { diff --git a/sdk/src/utils/cu.ts b/sdk/src/utils/cu.ts index 086df7c2..7264b2bc 100644 --- a/sdk/src/utils/cu.ts +++ b/sdk/src/utils/cu.ts @@ -1,5 +1,5 @@ export const MaxCUs = { - initializeDao: 20_000, + initializeDao: 30_000, createIdempotent: 25_000, initializeConditionalVault: 45_000, mintConditionalTokens: 35_000, diff --git a/tests/amm.ts b/tests/programs/amm.ts similarity index 98% rename from tests/amm.ts rename to tests/programs/amm.ts index e3e5ea1c..fb8f12ad 100644 --- a/tests/amm.ts +++ b/tests/programs/amm.ts @@ -26,7 +26,7 @@ import { } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { expectError, fastForward } from "./utils"; +import { expectError, advanceBySlots } from "../utils"; const META_DECIMALS = 9; const USDC_DECIMALS = 6; @@ -53,7 +53,7 @@ describe("amm", async function () { }); beforeEach(async function () { - await fastForward(context, 1n); + await advanceBySlots(context, 1n); META = await createMint( banksClient, payer, @@ -389,7 +389,7 @@ describe("amm", async function () { ) .rpc(); - await fastForward(context, 1n); + await advanceBySlots(context, 1n); const ammMiddle = await ammClient.getAmm(amm); let quoteReceived = @@ -427,7 +427,7 @@ describe("amm", async function () { ) .rpc(); - await fastForward(context, 1n); + await advanceBySlots(context, 1n); const ammMiddle = await ammClient.getAmm(amm); let baseReceived = diff --git a/tests/autocrat.ts b/tests/programs/autocrat.ts similarity index 96% rename from tests/autocrat.ts rename to tests/programs/autocrat.ts index 4fd9e00d..5a1bfe1b 100644 --- a/tests/autocrat.ts +++ b/tests/programs/autocrat.ts @@ -20,15 +20,10 @@ import { getAccount, } from "spl-token-bankrun"; -import { - mintConditionalTokens, - redeemConditionalTokens, -} from "./conditionalVault"; - -import { advanceBySlots, expectError } from "./utils"; -import { Autocrat } from "../target/types/autocrat"; -import { ConditionalVault } from "../target/types/conditional_vault"; -import { AutocratMigrator } from "../target/types/autocrat_migrator"; +import { advanceBySlots, expectError } from "../utils"; +import { Autocrat, IDL as AutocratIDL } from "../../target/types/autocrat"; +import { ConditionalVault, IDL as ConditionalVaultIDL } from "../../target/types/conditional_vault"; +import { AutocratMigrator, IDL as AutocratMigratorIDL } from "../../target/types/autocrat_migrator"; const { PublicKey, Keypair } = anchor.web3; @@ -53,9 +48,9 @@ import { TransactionInstruction, } from "@solana/web3.js"; -const AutocratIDL: Autocrat = require("../target/idl/autocrat.json"); -const ConditionalVaultIDL: ConditionalVault = require("../target/idl/conditional_vault.json"); -const AutocratMigratorIDL: AutocratMigrator = require("../target/idl/autocrat_migrator.json"); +// const AutocratIDL: Autocrat = require("../target/idl/autocrat.json"); +// const ConditionalVaultIDL: ConditionalVault = require("../target/idl/conditional_vault.json"); +// const AutocratMigratorIDL: AutocratMigrator = require("../target/idl/autocrat_migrator.json"); export type PublicKey = anchor.web3.PublicKey; export type Signer = anchor.web3.Signer; diff --git a/tests/conditionalVault.ts b/tests/programs/conditionalVault.ts similarity index 99% rename from tests/conditionalVault.ts rename to tests/programs/conditionalVault.ts index 60869806..deba0aab 100644 --- a/tests/conditionalVault.ts +++ b/tests/programs/conditionalVault.ts @@ -34,8 +34,8 @@ import { const { PublicKey, Keypair } = web3; -import { ConditionalVault } from "../target/types/conditional_vault"; -import { expectError } from "./utils"; +import { ConditionalVault, IDL as ConditionalVaultIDL } from "../../target/types/conditional_vault"; +import { expectError } from "../utils"; import { CONDITIONAL_VAULT_PROGRAM_ID, ConditionalVaultClient, @@ -47,7 +47,6 @@ import { sha256, } from "@metadaoproject/futarchy"; import { set } from "@metaplex-foundation/umi/serializers"; -const ConditionalVaultIDL: ConditionalVault = require("../target/idl/conditional_vault.json"); export type VaultProgram = anchor.Program; export type PublicKey = anchor.web3.PublicKey; diff --git a/tests/migrator.ts b/tests/programs/migrator.ts similarity index 97% rename from tests/migrator.ts rename to tests/programs/migrator.ts index 98796d46..76cb16f6 100644 --- a/tests/migrator.ts +++ b/tests/programs/migrator.ts @@ -13,8 +13,7 @@ const AUTOCRAT_MIGRATOR_PROGRAM_ID = new PublicKey( "MigRDW6uxyNMDBD8fX2njCRyJC4YZk2Rx9pDUZiAESt" ); -import { AutocratMigrator } from "../target/types/autocrat_migrator"; -const AutocratMigratorIDL: AutocratMigrator = require("../target/idl/autocrat_migrator.json"); +import { AutocratMigrator, IDL as AutocratMigratorIDL } from "../../target/types/autocrat_migrator"; export type PublicKey = anchor.web3.PublicKey; export type Signer = anchor.web3.Signer; diff --git a/tests/timelock.ts b/tests/programs/timelock.ts similarity index 99% rename from tests/timelock.ts rename to tests/programs/timelock.ts index e24f317c..ccf4ecb4 100644 --- a/tests/timelock.ts +++ b/tests/programs/timelock.ts @@ -13,10 +13,10 @@ const TIMELOCK_PROGRAM_ID = new PublicKey( "tiME1hz9F5C5ZecbvE5z6Msjy8PKfTqo1UuRYXfndKF" ); -import { OptimisticTimelock } from "../target/types/optimistic_timelock"; +import { OptimisticTimelock, IDL as OptimisticTimelockIDL } from "../../target/types/optimistic_timelock"; import { ComputeBudgetProgram, Connection } from "@solana/web3.js"; import { printArgs } from "@metaplex-foundation/mpl-token-metadata"; -const OptimisticTimelockIDL: OptimisticTimelock = require("../target/idl/optimistic_timelock.json"); +// const OptimisticTimelockIDL: OptimisticTimelock = require("../target/idl/optimistic_timelock.json"); export type PublicKey = anchor.web3.PublicKey; export type Signer = anchor.web3.Signer; diff --git a/tests/utils.ts b/tests/utils.ts index 2e0395af..7291b9a5 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -1,19 +1,5 @@ -import { expect, assert } from "chai"; -import { Program } from "@coral-xyz/anchor"; - -export const advanceBySlots = async (context: any, slots: BigInt) => { - const currentClock = await context.banksClient.getClock(); - const slot = currentClock.slot + slots; - context.setClock( - new Clock( - slot, - currentClock.epochStartTimestamp, - currentClock.epoch, - currentClock.leaderScheduleEpoch, - currentClock.unixTimestamp - ) - ); -}; +import { assert } from "chai"; +import { Clock, ProgramTestContext } from "solana-bankrun"; export const expectError = ( expectedError: string, @@ -49,9 +35,7 @@ export const expectError = ( ]; }; -import { Clock, ProgramTestContext } from "solana-bankrun"; - -export const fastForward = async ( +export const advanceBySlots = async ( context: ProgramTestContext, slots: bigint ) => { From 67dd51ff4769abb01c62e81633850c6e6313c0d4 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 17/52] Structure the tests under program folders --- run.sh | 2 +- tests/{programs => amm}/amm.ts | 0 tests/{programs => autocrat}/autocrat.ts | 0 tests/{programs => autocratMigrator}/migrator.ts | 0 tests/{programs => conditionalVault}/conditionalVault.ts | 0 tests/{programs => optimisticTimelock}/timelock.ts | 0 6 files changed, 1 insertion(+), 1 deletion(-) rename tests/{programs => amm}/amm.ts (100%) rename tests/{programs => autocrat}/autocrat.ts (100%) rename tests/{programs => autocratMigrator}/migrator.ts (100%) rename tests/{programs => conditionalVault}/conditionalVault.ts (100%) rename tests/{programs => optimisticTimelock}/timelock.ts (100%) diff --git a/run.sh b/run.sh index 3c14b828..62bcbb25 100755 --- a/run.sh +++ b/run.sh @@ -14,7 +14,7 @@ test_vault() { #sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/conditionalVault.ts"/' Anchor.toml #sleep 10 && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/*.ts"/' Anchor.toml & find programs tests sdk | entr -sc \ - "anchor build -p conditional_vault && (cd sdk && yarn build) && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/programs\/conditionalVault.ts\"/' Anchor.toml && (sleep 3 && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/**/*.ts\"/' Anchor.toml) & RUST_LOG= anchor test --skip-build" + "anchor build -p conditional_vault && (cd sdk && yarn build) && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/conditionalVault\/*.ts\"/' Anchor.toml && (sleep 3 && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/\**\/*.ts\"/' Anchor.toml) & RUST_LOG= anchor test --skip-build" } test_no_build() { diff --git a/tests/programs/amm.ts b/tests/amm/amm.ts similarity index 100% rename from tests/programs/amm.ts rename to tests/amm/amm.ts diff --git a/tests/programs/autocrat.ts b/tests/autocrat/autocrat.ts similarity index 100% rename from tests/programs/autocrat.ts rename to tests/autocrat/autocrat.ts diff --git a/tests/programs/migrator.ts b/tests/autocratMigrator/migrator.ts similarity index 100% rename from tests/programs/migrator.ts rename to tests/autocratMigrator/migrator.ts diff --git a/tests/programs/conditionalVault.ts b/tests/conditionalVault/conditionalVault.ts similarity index 100% rename from tests/programs/conditionalVault.ts rename to tests/conditionalVault/conditionalVault.ts diff --git a/tests/programs/timelock.ts b/tests/optimisticTimelock/timelock.ts similarity index 100% rename from tests/programs/timelock.ts rename to tests/optimisticTimelock/timelock.ts From 1d9db4718736223499de61c45007b49befcfc273 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 18/52] Structure tests in a new way --- Anchor.toml | 2 +- tests/autocrat/main.test.ts | 8 ++ tests/autocrat/{ => unit}/autocrat.ts | 8 +- tests/conditionalVault/integration/1.test.ts | 5 + tests/conditionalVault/main.test.ts | 9 ++ .../{conditionalVault.ts => unit.ts} | 2 +- .../unit/initializeConditionalVault.test.ts | 94 +++++++++++++++++++ .../unit/initializeQuestion.test.ts | 33 +++++++ tests/main.test.ts | 41 ++++++++ tsconfig.json | 3 +- 10 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 tests/autocrat/main.test.ts rename tests/autocrat/{ => unit}/autocrat.ts (98%) create mode 100644 tests/conditionalVault/integration/1.test.ts create mode 100644 tests/conditionalVault/main.test.ts rename tests/conditionalVault/{conditionalVault.ts => unit.ts} (99%) create mode 100644 tests/conditionalVault/unit/initializeConditionalVault.test.ts create mode 100644 tests/conditionalVault/unit/initializeQuestion.test.ts create mode 100644 tests/main.test.ts diff --git a/Anchor.toml b/Anchor.toml index 4168d8aa..3468eba8 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -1,5 +1,5 @@ [scripts] -test = "npx mocha --node-option require=ts-node/register --extension ts -t 10000000 tests/**/*.ts" +test = "npx mocha --node-option require=ts-node/register --extension ts -t 10000000 tests/main.test.ts" propose = "yarn run ts-node scripts/initializeProposal.ts" initialize-dao = "yarn run ts-node scripts/initializeDao.ts" finalize = "yarn run ts-node scripts/finalizeProposal.ts" diff --git a/tests/autocrat/main.test.ts b/tests/autocrat/main.test.ts new file mode 100644 index 00000000..ee4bfebc --- /dev/null +++ b/tests/autocrat/main.test.ts @@ -0,0 +1,8 @@ +// import "./unit/autocrat.ts"; +import("./unit/autocrat"); + +describe("autocrat", async function () { + it("should work", async function () { + console.log("should work"); + }); +}); \ No newline at end of file diff --git a/tests/autocrat/autocrat.ts b/tests/autocrat/unit/autocrat.ts similarity index 98% rename from tests/autocrat/autocrat.ts rename to tests/autocrat/unit/autocrat.ts index 5a1bfe1b..e9867b59 100644 --- a/tests/autocrat/autocrat.ts +++ b/tests/autocrat/unit/autocrat.ts @@ -20,10 +20,10 @@ import { getAccount, } from "spl-token-bankrun"; -import { advanceBySlots, expectError } from "../utils"; -import { Autocrat, IDL as AutocratIDL } from "../../target/types/autocrat"; -import { ConditionalVault, IDL as ConditionalVaultIDL } from "../../target/types/conditional_vault"; -import { AutocratMigrator, IDL as AutocratMigratorIDL } from "../../target/types/autocrat_migrator"; +import { advanceBySlots, expectError } from "../../utils"; +import { Autocrat, IDL as AutocratIDL } from "../../../target/types/autocrat"; +import { ConditionalVault, IDL as ConditionalVaultIDL } from "../../../target/types/conditional_vault"; +import { AutocratMigrator, IDL as AutocratMigratorIDL } from "../../../target/types/autocrat_migrator"; const { PublicKey, Keypair } = anchor.web3; diff --git a/tests/conditionalVault/integration/1.test.ts b/tests/conditionalVault/integration/1.test.ts new file mode 100644 index 00000000..879c665d --- /dev/null +++ b/tests/conditionalVault/integration/1.test.ts @@ -0,0 +1,5 @@ +import { assert } from "chai"; + +export default function test() { + assert(true); +} \ No newline at end of file diff --git a/tests/conditionalVault/main.test.ts b/tests/conditionalVault/main.test.ts new file mode 100644 index 00000000..cf3e9402 --- /dev/null +++ b/tests/conditionalVault/main.test.ts @@ -0,0 +1,9 @@ +import initializeQuestion from "./unit/initializeQuestion.test"; +import initializeConditionalVault from "./unit/initializeConditionalVault.test"; +import test1 from "./integration/1.test"; + +export default function suite() { + describe("#initialize_question", initializeQuestion); + describe("#initialize_conditional_vault", initializeConditionalVault); + it("integration test 1", test1); +} \ No newline at end of file diff --git a/tests/conditionalVault/conditionalVault.ts b/tests/conditionalVault/unit.ts similarity index 99% rename from tests/conditionalVault/conditionalVault.ts rename to tests/conditionalVault/unit.ts index deba0aab..45ed0e78 100644 --- a/tests/conditionalVault/conditionalVault.ts +++ b/tests/conditionalVault/unit.ts @@ -114,7 +114,7 @@ describe("conditional_vault", async function () { provider ); - vaultClient = await ConditionalVaultClient.createClient({ provider: provider as any }); + vaultClient = ConditionalVaultClient.createClient({ provider: provider as any }); payer = vaultProgram.provider.wallet.payer; alice = anchor.web3.Keypair.generate(); diff --git a/tests/conditionalVault/unit/initializeConditionalVault.test.ts b/tests/conditionalVault/unit/initializeConditionalVault.test.ts new file mode 100644 index 00000000..b04c9ce9 --- /dev/null +++ b/tests/conditionalVault/unit/initializeConditionalVault.test.ts @@ -0,0 +1,94 @@ +import { sha256, ConditionalVaultClient, getVaultAddr, getConditionalTokenMintAddr} from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createMint, getMint } from "spl-token-bankrun" +import * as anchor from "@coral-xyz/anchor" +import * as token from "@solana/spl-token" + + +export default function suite() { + let vaultClient: ConditionalVaultClient; + let underlyingTokenMint: PublicKey; + + before(async function() { + vaultClient = this.vaultClient; + underlyingTokenMint = await createMint( + this.banksClient, + this.payer as Keypair, + Keypair.generate().publicKey, + null, + 8 + ); + }); + + const testCases = [ + { name: "2-outcome question", idArray: [3, 2, 1], outcomes: 2 }, + { name: "3-outcome question", idArray: [4, 5, 6], outcomes: 3 }, + { name: "4-outcome question", idArray: [7, 8, 9], outcomes: 4 }, + ]; + + testCases.forEach(({ name, idArray, outcomes }) => { + describe(name, function () { + let question: PublicKey; + let oracle: Keypair = Keypair.generate(); + + beforeEach(async function () { + let questionId = sha256(new Uint8Array(idArray)); + question = await vaultClient.initializeQuestion( + questionId, + oracle.publicKey, + outcomes + ); + }); + + it("initializes vaults correctly", async function () { + await vaultClient + .initializeNewVaultIx(question, underlyingTokenMint, outcomes) + .rpc(); + + const [vault, pdaBump] = getVaultAddr( + vaultClient.vaultProgram.programId, + question, + underlyingTokenMint + ); + + const storedVault = await vaultClient.fetchVault(vault); + assert.ok(storedVault.question.equals(question)); + assert.ok( + storedVault.underlyingTokenMint.equals(underlyingTokenMint) + ); + + const vaultUnderlyingTokenAccount = + token.getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ); + assert.ok( + storedVault.underlyingTokenAccount.equals( + vaultUnderlyingTokenAccount + ) + ); + const storedConditionalTokenMints = storedVault.conditionalTokenMints; + storedConditionalTokenMints.forEach((mint, i) => { + const [expectedMint] = getConditionalTokenMintAddr( + vaultClient.vaultProgram.programId, + vault, + i + ); + assert.ok(mint.equals(expectedMint)); + }); + assert.equal(storedVault.pdaBump, pdaBump); + assert.equal(storedVault.decimals, 8); + + for (let mint of storedConditionalTokenMints) { + const storedMint = await getMint(this.banksClient, mint); + assert.ok(storedMint.mintAuthority.equals(vault)); + assert.equal(storedMint.supply.toString(), "0"); + assert.equal(storedMint.decimals, 8); + assert.isNull(storedMint.freezeAuthority); + } + }); + }); + }); +} \ No newline at end of file diff --git a/tests/conditionalVault/unit/initializeQuestion.test.ts b/tests/conditionalVault/unit/initializeQuestion.test.ts new file mode 100644 index 00000000..42273fa8 --- /dev/null +++ b/tests/conditionalVault/unit/initializeQuestion.test.ts @@ -0,0 +1,33 @@ +import { sha256, ConditionalVaultClient, getQuestionAddr } from "@metadaoproject/futarchy"; +import { Keypair } from "@solana/web3.js"; +import { assert } from "chai"; + +export default function suite() { + let vaultClient: ConditionalVaultClient; + before(function () { + vaultClient = this.vaultClient; + }); + + it("initializes 2-outcome questions", async function () { + let questionId = sha256(new Uint8Array([1, 2, 3])); + + let oracle = Keypair.generate(); + + await vaultClient + .initializeQuestionIx(questionId, oracle.publicKey, 2) + .rpc(); + + let [question] = getQuestionAddr( + vaultClient.vaultProgram.programId, + questionId, + oracle.publicKey, + 2 + ); + + const storedQuestion = await vaultClient.fetchQuestion(question); + assert.deepEqual(storedQuestion.questionId, Array.from(questionId)); + assert.ok(storedQuestion.oracle.equals(oracle.publicKey)); + assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); + assert.equal(storedQuestion.payoutDenominator, 0); + }); +} \ No newline at end of file diff --git a/tests/main.test.ts b/tests/main.test.ts new file mode 100644 index 00000000..2e6b3974 --- /dev/null +++ b/tests/main.test.ts @@ -0,0 +1,41 @@ +import { startAnchor } from "solana-bankrun"; +import conditionalVault from "./conditionalVault/main.test"; +import { BankrunProvider } from "anchor-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import { ConditionalVaultClient } from "@metadaoproject/futarchy"; + +before(async function () { + let context = await startAnchor( + "./", + [], + // [ + // // even though the program is loaded into the test validator, we need + // // to tell banks test client to load it as well + // { + // name: "mpl_token_metadata", + // programId: MPL_TOKEN_METADATA_PROGRAM_ID, + // }, + // ], + [] + ); + this.banksClient = context.banksClient; + let provider = new BankrunProvider(context); + anchor.setProvider(provider); + + // umi = createUmi(anchor.AnchorProvider.env().connection); + + // vaultProgram = new Program( + // ConditionalVaultIDL, + // CONDITIONAL_VAULT_PROGRAM_ID, + // provider + // ); + + this.vaultClient = ConditionalVaultClient.createClient({ provider: provider as any }); + this.payer = provider.wallet.payer; + // }); + // describe("1", () => test1(vaultClient)); + // describe("2", () => test2(vaultClient)); + // test2(); +}); + +describe("conditional_vault", conditionalVault); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index ce468706..1bd44cb4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,8 @@ "./node_modules/@types" ], "lib": [ - "es2015" + "es2015", + "dom" ], "module": "commonjs", "target": "ES2020", From d049365b0198c30085e00f6cbcc86f9d3d7bae64 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 19/52] Migrate all conditional vault unit tests over --- tests/conditionalVault/main.test.ts | 8 ++ .../conditionalVault/unit/mergeTokens.test.ts | 75 +++++++++++++ .../unit/redeemTokens.test.ts | 100 ++++++++++++++++++ .../unit/resolveQuestion.test.ts | 39 +++++++ .../conditionalVault/unit/splitTokens.test.ts | 87 +++++++++++++++ 5 files changed, 309 insertions(+) create mode 100644 tests/conditionalVault/unit/mergeTokens.test.ts create mode 100644 tests/conditionalVault/unit/redeemTokens.test.ts create mode 100644 tests/conditionalVault/unit/resolveQuestion.test.ts create mode 100644 tests/conditionalVault/unit/splitTokens.test.ts diff --git a/tests/conditionalVault/main.test.ts b/tests/conditionalVault/main.test.ts index cf3e9402..4323ecaa 100644 --- a/tests/conditionalVault/main.test.ts +++ b/tests/conditionalVault/main.test.ts @@ -1,9 +1,17 @@ import initializeQuestion from "./unit/initializeQuestion.test"; import initializeConditionalVault from "./unit/initializeConditionalVault.test"; +import resolveQuestion from "./unit/resolveQuestion.test"; +import splitTokens from "./unit/splitTokens.test"; +import mergeTokens from "./unit/mergeTokens.test"; +import redeemTokens from "./unit/redeemTokens.test"; import test1 from "./integration/1.test"; export default function suite() { describe("#initialize_question", initializeQuestion); describe("#initialize_conditional_vault", initializeConditionalVault); + describe("#resolve_question", resolveQuestion); + describe("#split_tokens", splitTokens); + describe("#merge_tokens", mergeTokens); + describe("#redeem_tokens", redeemTokens); it("integration test 1", test1); } \ No newline at end of file diff --git a/tests/conditionalVault/unit/mergeTokens.test.ts b/tests/conditionalVault/unit/mergeTokens.test.ts new file mode 100644 index 00000000..471cb639 --- /dev/null +++ b/tests/conditionalVault/unit/mergeTokens.test.ts @@ -0,0 +1,75 @@ +import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createAssociatedTokenAccount, createMint, getAccount, mintTo } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import * as token from "@solana/spl-token"; + +export default function suite() { + let vaultClient: ConditionalVaultClient; + let question: PublicKey; + let vault: PublicKey; + let underlyingTokenMint: PublicKey; + let userUnderlyingTokenAccount: PublicKey; + before(function () { + vaultClient = this.vaultClient; + }); + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([9, 2, 1])); + let oracle = Keypair.generate(); + + question = await vaultClient.initializeQuestion( + questionId, + oracle.publicKey, + 2 + ); + + underlyingTokenMint = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + null, + 8 + ); + + vault = await vaultClient.initializeNewVault( + question, + underlyingTokenMint, + 2 + ); + + userUnderlyingTokenAccount = await createAssociatedTokenAccount( + this.banksClient, + this.payer, + underlyingTokenMint, + this.payer.publicKey + ); + + // Mint some underlying tokens to the user's account + await mintTo( + this.banksClient, + this.payer, + underlyingTokenMint, + userUnderlyingTokenAccount, + this.payer, + 1000 + ); + + + await vaultClient + .splitTokensIx(question, vault, underlyingTokenMint, new anchor.BN(1000), 2) + .rpc(); + }); + + it("merges tokens", async function () { + const balanceBefore = await getAccount(this.banksClient, token.getAssociatedTokenAddressSync(underlyingTokenMint, this.payer.publicKey)).then(acc => acc.amount); + await vaultClient + .mergeTokensIx(question, vault, underlyingTokenMint, new anchor.BN(600), 2) + .rpc(); + const balanceAfter = await getAccount(this.banksClient, token.getAssociatedTokenAddressSync(underlyingTokenMint, this.payer.publicKey)).then(acc => acc.amount); + + assert.isTrue(balanceAfter > balanceBefore); + assert.equal(balanceAfter - balanceBefore, 600); + }); +} \ No newline at end of file diff --git a/tests/conditionalVault/unit/redeemTokens.test.ts b/tests/conditionalVault/unit/redeemTokens.test.ts new file mode 100644 index 00000000..b0776117 --- /dev/null +++ b/tests/conditionalVault/unit/redeemTokens.test.ts @@ -0,0 +1,100 @@ +import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createAssociatedTokenAccount, createMint, getAccount, mintTo } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import * as token from "@solana/spl-token"; + +export default function suite() { + let vaultClient: ConditionalVaultClient; + let question: PublicKey; + let vault: PublicKey; + let underlyingTokenMint: PublicKey; + let settlementAuthority: Keypair; + let userUnderlyingTokenAccount: PublicKey; + + before(function () { + vaultClient = this.vaultClient; + }); + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([9, 28, 2, 1])); + settlementAuthority = Keypair.generate(); + + question = await vaultClient.initializeQuestion( + questionId, + settlementAuthority.publicKey, + 2 + ); + + underlyingTokenMint = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + null, + 8 + ); + + vault = await vaultClient.initializeNewVault( + question, + underlyingTokenMint, + 2 + ); + + userUnderlyingTokenAccount = await createAssociatedTokenAccount( + this.banksClient, + this.payer, + underlyingTokenMint, + this.payer.publicKey + ); + + // Mint some underlying tokens to the user's account + await mintTo( + this.banksClient, + this.payer, + underlyingTokenMint, + userUnderlyingTokenAccount, + this.payer, + 1000 + ); + + await vaultClient + .splitTokensIx(question, vault, underlyingTokenMint, new anchor.BN(1000), 2) + .rpc(); + }); + + it("can't redeem tokens when question is not resolved", async function () { + try { + await vaultClient + .redeemTokensIx(question, vault, underlyingTokenMint, new anchor.BN(600), 2) + .rpc(); + assert.fail("Should have thrown an error"); + } catch (error) { + assert.include(error.message, "CantRedeemConditionalTokens"); + } + }); + + it("can redeem tokens when question is resolved", async function () { + await vaultClient + .resolveQuestionIx(question, settlementAuthority, [1, 0]) + .rpc(); + + const underlyingTokenAccount = await token.getAssociatedTokenAddress( + underlyingTokenMint, + this.payer.publicKey + ); + + const balanceBefore = await getAccount(this.banksClient, underlyingTokenAccount) + .then(acc => acc.amount); + + await vaultClient + .redeemTokensIx(question, vault, underlyingTokenMint, new anchor.BN(600), 2) + .rpc(); + + const balanceAfter = await getAccount(this.banksClient, underlyingTokenAccount) + .then(acc => acc.amount); + + assert.isTrue(balanceAfter > balanceBefore); + assert.equal(balanceAfter - balanceBefore, 1000); + }); +} diff --git a/tests/conditionalVault/unit/resolveQuestion.test.ts b/tests/conditionalVault/unit/resolveQuestion.test.ts new file mode 100644 index 00000000..0f790f14 --- /dev/null +++ b/tests/conditionalVault/unit/resolveQuestion.test.ts @@ -0,0 +1,39 @@ +import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; + +export default function suite() { + let vaultClient: ConditionalVaultClient; + let question: PublicKey; + let settlementAuthority: Keypair; + + before(function () { + vaultClient = this.vaultClient; + }); + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([4, 2, 1])); + settlementAuthority = Keypair.generate(); + question = await vaultClient.initializeQuestion( + questionId, + settlementAuthority.publicKey, + 2 + ); + }); + + it("resolves questions", async function () { + let storedQuestion = await vaultClient.fetchQuestion(question); + + assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); + assert.equal(storedQuestion.payoutDenominator, 0); + + await vaultClient + .resolveQuestionIx(question, settlementAuthority, [1, 0]) + .rpc(); + + storedQuestion = await vaultClient.fetchQuestion(question); + + assert.deepEqual(storedQuestion.payoutNumerators, [1, 0]); + assert.equal(storedQuestion.payoutDenominator, 1); + }); +} \ No newline at end of file diff --git a/tests/conditionalVault/unit/splitTokens.test.ts b/tests/conditionalVault/unit/splitTokens.test.ts new file mode 100644 index 00000000..6b85cb4c --- /dev/null +++ b/tests/conditionalVault/unit/splitTokens.test.ts @@ -0,0 +1,87 @@ +import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createAssociatedTokenAccount, createMint, getAccount, getMint, mintTo } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import * as token from "@solana/spl-token"; + +export default function suite() { + let vaultClient: ConditionalVaultClient; + let question: PublicKey; + let vault: PublicKey; + let underlyingTokenMint: PublicKey; + + before(function () { + vaultClient = this.vaultClient; + }); + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([5, 2, 1])); + let oracle = Keypair.generate(); + + question = await vaultClient.initializeQuestion( + questionId, + oracle.publicKey, + 2 + ); + + underlyingTokenMint = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + null, + 8 + ); + + vault = await vaultClient.initializeNewVault( + question, + underlyingTokenMint, + 2 + ); + + let userUnderlyingTokenAccount = await createAssociatedTokenAccount( + this.banksClient, + this.payer, + underlyingTokenMint, + this.payer.publicKey + ); + + await mintTo( + this.banksClient, + this.payer, + underlyingTokenMint, + userUnderlyingTokenAccount, + this.payer, + 10_000_000_000n + ); + }); + + it("splits tokens", async function () { + await vaultClient + .splitTokensIx(question, vault, underlyingTokenMint, new anchor.BN(1000), 2) + .rpc(); + + const storedVault = await vaultClient.fetchVault(vault); + + let storedVaultUnderlyingAcc = await getAccount( + this.banksClient, + storedVault.underlyingTokenAccount + ); + assert.equal(storedVaultUnderlyingAcc.amount.toString(), "1000"); + + const storedConditionalTokenMints = storedVault.conditionalTokenMints; + for (let mint of storedConditionalTokenMints) { + let storedMint = await getMint( + this.banksClient, + mint + ); + assert.equal(storedMint.supply.toString(), "1000"); + let storedTokenAcc = await getAccount( + this.banksClient, + token.getAssociatedTokenAddressSync(mint, this.payer.publicKey) + // await createAssociatedTokenAccount(this.banksClient, this.payer, mint, this.payer.publicKey) + ); + assert.equal(storedTokenAcc.amount.toString(), "1000"); + } + }); +} \ No newline at end of file From 70bb8c8b6d3d919e17280fd0ab29f89bd17c5670 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 20/52] Start on conditional vault integration test --- sdk/src/ConditionalVaultClient.ts | 36 +++++---- tests/conditionalVault/integration/1.test.ts | 79 +++++++++++++++++++- 2 files changed, 99 insertions(+), 16 deletions(-) diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 598d4ff4..89f02637 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -229,8 +229,19 @@ export class ConditionalVaultClient { vault: PublicKey, underlyingTokenMint: PublicKey, amount: BN, - numOutcomes: number + numOutcomes: number, + user?: PublicKey | Keypair ) { + console.log(typeof user); + console.log(user); + console.log(user instanceof Keypair); + const userPubkey = + user instanceof Keypair + ? user.publicKey + : user || this.provider.publicKey; + + console.log("userPubkey", userPubkey); + let conditionalTokenMintAddrs = []; for (let i = 0; i < numOutcomes; i++) { const [conditionalTokenMint] = getConditionalTokenMintAddr( @@ -273,11 +284,8 @@ export class ConditionalVaultClient { conditionalTokenMintAddrs.map((conditionalTokenMint) => { return createAssociatedTokenAccountIdempotentInstruction( this.provider.publicKey, - getAssociatedTokenAddressSync( - conditionalTokenMint, - this.provider.publicKey - ), - this.provider.publicKey, + getAssociatedTokenAddressSync(conditionalTokenMint, userPubkey), + userPubkey, conditionalTokenMint ); }) @@ -294,6 +302,10 @@ export class ConditionalVaultClient { }) ); + if (user instanceof Keypair) { + ix = ix.signers([user]); + } + return ix; } @@ -449,14 +461,10 @@ export class ConditionalVaultClient { amount: BN, user?: PublicKey | Keypair ) { - let userPubkey; - if (!user) { - userPubkey = this.provider.publicKey; - } else if (user instanceof Keypair) { - userPubkey = user.publicKey; - } else { - userPubkey = user; - } + const userPubkey = + user instanceof Keypair + ? user.publicKey + : user || this.provider.publicKey; const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( this.vaultProgram.programId, diff --git a/tests/conditionalVault/integration/1.test.ts b/tests/conditionalVault/integration/1.test.ts index 879c665d..acfc1816 100644 --- a/tests/conditionalVault/integration/1.test.ts +++ b/tests/conditionalVault/integration/1.test.ts @@ -1,5 +1,80 @@ +import { ConditionalVaultClient, sha256 } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import BN from "bn.js"; import { assert } from "chai"; +import { createMint, getMint, mintTo, createAssociatedTokenAccount } from "spl-token-bankrun" -export default function test() { - assert(true); +export default async function test() { + // A binary prediction market test. Alice, Bob, and Charlie are betting on + // who's going to win the next election: Trump or Harris. Alice and Bob each + // split 100 USDC into TRUMP and HARRIS tokens. Alice is a trump supporter + // and buys 30 TRUMP tokens in exchange for 40 HARRIS tokens from Bob. Charlie + // buys an additional 30 HARRIS tokens from Alice. + + // Alice should have 130 TRUMP and 30 HARRIS tokens. Bob should have 70 TRUMP + // and 140 HARRIS tokens. Charlie should have 30 HARRIS tokens. + + // Alice faces a cash crunch, and merges 15 TRUMP and 15 HARRIS tokens into 15 + // USDC. She should now have 15 USDC, 15 TRUMP, and 15 HARRIS tokens. + + // Bob becomes a trump supporter, and as a display of his newfound + // allegiance, he sends all of his HARRIS tokens to the vault (to burn them). + // Bob should now have 70 TRUMP tokens. + + // The market resolves in favor of Harris. When redeeming, Alice should get 15 + // USDC, Bob should get nothing, and Charlie should get 30 USDC. + + let vaultClient: ConditionalVaultClient = this.vaultClient; + + let alice: Keypair = Keypair.generate(); + let bob: Keypair = Keypair.generate(); + let charlie: Keypair = Keypair.generate(); + let operator: Keypair = Keypair.generate(); + + let question: PublicKey = await vaultClient.initializeQuestion(sha256(new TextEncoder().encode("Who's going to win the next election?")), operator.publicKey, 2); + + let USDC: PublicKey = await createMint(this.banksClient, this.payer, operator.publicKey, null, 6); + + let createUSDCAccount = async (owner: PublicKey) => { + return await createAssociatedTokenAccount( + this.banksClient, + this.payer, + USDC, + owner + ); + } + + let aliceUSDC = await createUSDCAccount(alice.publicKey); + let bobUSDC = await createUSDCAccount(bob.publicKey); + let charlieUSDC = await createUSDCAccount(charlie.publicKey); + + await mintTo( + this.banksClient, + this.payer, + USDC, + aliceUSDC, + operator, + 100 * 10 ** 6 + ); + + await mintTo( + this.banksClient, + this.payer, + USDC, + bobUSDC, + operator, + 100 * 10 ** 6 + ); + + const vault = await vaultClient.initializeNewVault(question, USDC, 2); + + await vaultClient.splitTokensIx(question, vault, USDC, new BN(100 * 10 ** 6), 2, alice).rpc(); + + + + // let question: PublicKey = PublicKey.default; + // let vault: PublicKey = PublicKey.default; + // let underlyingTokenMint: PublicKey = PublicKey.default; + + // await vaultClient.initializeQuestion(question, underlyingTokenMint, [alice.publicKey, bob.publicKey], 2); } \ No newline at end of file From 2eae26144e1555aafab137ada141c5e3ec5b0271 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 21/52] Get start of integration test working --- package-lock.json | 5615 ++++++++++++++++++ sdk/src/ConditionalVaultClient.ts | 27 +- tests/conditionalVault/integration/1.test.ts | 5 +- 3 files changed, 5627 insertions(+), 20 deletions(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..6aa2c8b6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5615 @@ +{ + "name": "futarchy", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@coral-xyz/anchor": "0.28.0", + "@metadaoproject/futarchy": "0.3.0-alpha.12", + "@metaplex-foundation/mpl-token-metadata": "^3.2.0", + "@metaplex-foundation/umi": "^0.9.1", + "@metaplex-foundation/umi-bundle-defaults": "^0.9.1", + "@metaplex-foundation/umi-uploader-bundlr": "^0.9.1", + "@solana/spl-token": "^0.3.7", + "@solana/web3.js": "^1.90.0", + "anchor-bankrun": "^0.3.0", + "solana-bankrun": "^0.2.0", + "spl-token-bankrun": "0.2.6" + }, + "devDependencies": { + "@solana/spl-memo": "^0.2.3", + "@solana/spl-token-registry": "^0.2.4574", + "@types/bn.js": "^5.1.0", + "@types/chai": "^4.3.0", + "@types/mocha": "^9.0.0", + "@types/node": "^20.8.6", + "chai": "^4.3.4", + "mocha": "^9.0.3", + "prettier": "^2.6.2", + "ts-mocha": "^10.0.0", + "tsx": "^4.7.1", + "typescript": "^4.3.5" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", + "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bundlr-network/client": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@bundlr-network/client/-/client-0.8.9.tgz", + "integrity": "sha512-SJ7BAt/KhONeFQ0+nbqrw2DUWrsev6y6cmlXt+3x7fPCkw7OJwudtxV/h2nBteZd65NXjqw8yzkmLiLfZ7CCRA==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-adapter-base": "^0.9.2", + "@solana/web3.js": "^1.36.0", + "@supercharge/promise-pool": "^2.1.0", + "algosdk": "^1.13.1", + "arbundles": "^0.6.21", + "arweave": "^1.11.4", + "async-retry": "^1.3.3", + "axios": "^0.25.0", + "base64url": "^3.0.1", + "bignumber.js": "^9.0.1", + "bs58": "^4.0.1", + "commander": "^8.2.0", + "csv": "^6.0.5", + "ethers": "^5.5.1", + "inquirer": "^8.2.0", + "js-sha256": "^0.9.0", + "mime-types": "^2.1.34", + "near-api-js": "^0.44.2", + "near-seed-phrase": "^0.2.0" + }, + "bin": { + "bundlr": "build/node/cli.js" + } + }, + "node_modules/@coral-xyz/anchor": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.28.0.tgz", + "integrity": "sha512-kQ02Hv2ZqxtWP30WN1d4xxT4QqlOXYDxmEd3k/bbneqhV3X5QMO4LAtoUFs7otxyivOgoqam5Il5qx81FuI4vw==", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@coral-xyz/borsh": "^0.28.0", + "@solana/web3.js": "^1.68.0", + "base64-js": "^1.5.1", + "bn.js": "^5.1.2", + "bs58": "^4.0.1", + "buffer-layout": "^1.2.2", + "camelcase": "^6.3.0", + "cross-fetch": "^3.1.5", + "crypto-hash": "^1.3.0", + "eventemitter3": "^4.0.7", + "js-sha256": "^0.9.0", + "pako": "^2.0.3", + "snake-case": "^3.0.4", + "superstruct": "^0.15.4", + "toml": "^3.0.0" + }, + "engines": { + "node": ">=11" + } + }, + "node_modules/@coral-xyz/borsh": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.28.0.tgz", + "integrity": "sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ==", + "license": "Apache-2.0", + "dependencies": { + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@solana/web3.js": "^1.68.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT" + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@metadaoproject/futarchy": { + "resolved": "sdk", + "link": true + }, + "node_modules/@metaplex-foundation/mpl-token-metadata": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/mpl-token-metadata/-/mpl-token-metadata-3.2.1.tgz", + "integrity": "sha512-26W1NhQwDWmLOg/pBRYut7x/vEs/5kFS2sWVEY5/X0f2jJOLhnd4NaZQcq+5u+XZsXvm1jq2AtrRGPNK43oqWQ==", + "license": "Apache-2.0", + "dependencies": { + "@metaplex-foundation/mpl-toolbox": "^0.9.4" + }, + "peerDependencies": { + "@metaplex-foundation/umi": ">= 0.8.2 < 1" + } + }, + "node_modules/@metaplex-foundation/mpl-toolbox": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/mpl-toolbox/-/mpl-toolbox-0.9.4.tgz", + "integrity": "sha512-fd6JxfoLbj/MM8FG2x91KYVy1U6AjBQw4qjt7+Da3trzQaWnSaYHDcYRG/53xqfvZ9qofY1T2t53GXPlD87lnQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@metaplex-foundation/umi": ">= 0.8.2 < 1" + } + }, + "node_modules/@metaplex-foundation/umi": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi/-/umi-0.9.1.tgz", + "integrity": "sha512-IhHoOvp4vfO/++YL+78+iVuLM53+FDwUOZDYgH6lx0jYXyQ27BeaieeR5i+q3A9dz4KxQo5Nzc5aCA1109QGCQ==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-options": "^0.8.9", + "@metaplex-foundation/umi-public-keys": "^0.8.9", + "@metaplex-foundation/umi-serializers": "^0.9.0" + } + }, + "node_modules/@metaplex-foundation/umi-bundle-defaults": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-bundle-defaults/-/umi-bundle-defaults-0.9.1.tgz", + "integrity": "sha512-QBaCLrb2D5uhY6pbWdxGPdD3LNKOAZ/Wfp7gEzhAipWmEV75KO7ya3AzaU4JZPHaf9juwdU4wO50WEPRb7YyQg==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-downloader-http": "^0.9.1", + "@metaplex-foundation/umi-eddsa-web3js": "^0.9.1", + "@metaplex-foundation/umi-http-fetch": "^0.9.1", + "@metaplex-foundation/umi-program-repository": "^0.9.1", + "@metaplex-foundation/umi-rpc-chunk-get-accounts": "^0.9.1", + "@metaplex-foundation/umi-rpc-web3js": "^0.9.1", + "@metaplex-foundation/umi-serializer-data-view": "^0.9.1", + "@metaplex-foundation/umi-transaction-factory-web3js": "^0.9.1" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1", + "@solana/web3.js": "^1.72.0" + } + }, + "node_modules/@metaplex-foundation/umi-downloader-http": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-downloader-http/-/umi-downloader-http-0.9.1.tgz", + "integrity": "sha512-T/t9YtkDxovIz5hG0SEBolzet0nTd77hZJSSGCNfrhhgJJtNeIHz+/0K+o7U+ubLddFmtPNxF4KBfmh1jCYCQQ==", + "license": "MIT", + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1" + } + }, + "node_modules/@metaplex-foundation/umi-eddsa-web3js": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-eddsa-web3js/-/umi-eddsa-web3js-0.9.1.tgz", + "integrity": "sha512-D+ZP8jOEzfr1ncF18zRdxfE820xjTf6AIBZd926TRj8dlOFIDfu1J0FGS7pC+52CAC9BRNrRvYQyc1TPORkfTQ==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-web3js-adapters": "^0.9.1", + "@noble/curves": "^1.0.0" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1", + "@solana/web3.js": "^1.72.0" + } + }, + "node_modules/@metaplex-foundation/umi-http-fetch": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-http-fetch/-/umi-http-fetch-0.9.1.tgz", + "integrity": "sha512-Flh5wSbiYmeDg4V6IE9BNX1BH3eewcIzHxZ1RT1sagU0PlDwy37dm0gcU+svYM/usDvnbk4hwOMGcZkhQLN1QQ==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.7" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1" + } + }, + "node_modules/@metaplex-foundation/umi-options": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-options/-/umi-options-0.8.9.tgz", + "integrity": "sha512-jSQ61sZMPSAk/TXn8v8fPqtz3x8d0/blVZXLLbpVbo2/T5XobiI6/MfmlUosAjAUaQl6bHRF8aIIqZEFkJiy4A==", + "license": "MIT" + }, + "node_modules/@metaplex-foundation/umi-program-repository": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-program-repository/-/umi-program-repository-0.9.1.tgz", + "integrity": "sha512-6SawFMO4IZdk4y+D/+o8CyYnfmy8kcOqhQsX3fUMqIXSzz0vzMT2/dDTMfLsuTVyULnaW/VYm26cmYBjVqZTlw==", + "license": "MIT", + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1" + } + }, + "node_modules/@metaplex-foundation/umi-public-keys": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-public-keys/-/umi-public-keys-0.8.9.tgz", + "integrity": "sha512-CxMzN7dgVGOq9OcNCJe2casKUpJ3RmTVoOvDFyeoTQuK+vkZ1YSSahbqC1iGuHEtKTLSjtWjKvUU6O7zWFTw3Q==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-serializers-encodings": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-rpc-chunk-get-accounts": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-rpc-chunk-get-accounts/-/umi-rpc-chunk-get-accounts-0.9.1.tgz", + "integrity": "sha512-WxF4DxSBJXzrGfmJ+X4DjF4rk9as/0EnkpGo0DdtHTZNqIfRY9mqi8OPRe/JhSjYzWFCC0ngjanqShhcEetB4A==", + "license": "MIT", + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1" + } + }, + "node_modules/@metaplex-foundation/umi-rpc-web3js": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-rpc-web3js/-/umi-rpc-web3js-0.9.1.tgz", + "integrity": "sha512-kOJEc9IWMX+H7dI5zZZimww1w0A6yd2V/fsQHKB/kHddja7JoPK4Au68n45Pi0vb3HY7riCQN9XMqOOPD5tcxA==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-web3js-adapters": "^0.9.1" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1", + "@solana/web3.js": "^1.72.0" + } + }, + "node_modules/@metaplex-foundation/umi-serializer-data-view": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializer-data-view/-/umi-serializer-data-view-0.9.1.tgz", + "integrity": "sha512-teilMc3abBrdLtgQ0PqnNXvmdsjNFPk4sVbM/flxoh9edyRQCAJmyK7DEA7cXCYfhBVX0jwSJIEcqTDa+r+jdw==", + "license": "MIT", + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1" + } + }, + "node_modules/@metaplex-foundation/umi-serializers": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers/-/umi-serializers-0.9.0.tgz", + "integrity": "sha512-hAOW9Djl4w4ioKeR4erDZl5IG4iJdP0xA19ZomdaCbMhYAAmG/FEs5khh0uT2mq53/MnzWcXSUPoO8WBN4Q+Vg==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-options": "^0.8.9", + "@metaplex-foundation/umi-public-keys": "^0.8.9", + "@metaplex-foundation/umi-serializers-core": "^0.8.9", + "@metaplex-foundation/umi-serializers-encodings": "^0.8.9", + "@metaplex-foundation/umi-serializers-numbers": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-serializers-core": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers-core/-/umi-serializers-core-0.8.9.tgz", + "integrity": "sha512-WT82tkiYJ0Qmscp7uTj1Hz6aWQPETwaKLAENAUN5DeWghkuBKtuxyBKVvEOuoXerJSdhiAk0e8DWA4cxcTTQ/w==", + "license": "MIT" + }, + "node_modules/@metaplex-foundation/umi-serializers-encodings": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers-encodings/-/umi-serializers-encodings-0.8.9.tgz", + "integrity": "sha512-N3VWLDTJ0bzzMKcJDL08U3FaqRmwlN79FyE4BHj6bbAaJ9LEHjDQ9RJijZyWqTm0jE7I750fU7Ow5EZL38Xi6Q==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-serializers-core": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-serializers-numbers": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-serializers-numbers/-/umi-serializers-numbers-0.8.9.tgz", + "integrity": "sha512-NtBf1fnVNQJHFQjLFzRu2i9GGnigb9hOm/Gfrk628d0q0tRJB7BOM3bs5C61VAs7kJs4yd+pDNVAERJkknQ7Lg==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-serializers-core": "^0.8.9" + } + }, + "node_modules/@metaplex-foundation/umi-transaction-factory-web3js": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-transaction-factory-web3js/-/umi-transaction-factory-web3js-0.9.1.tgz", + "integrity": "sha512-DBBvaMpR6pR3ZpyaRD/0QSTjS+3lxHIUZYAqZi0JYsTyYqNTNsdKVbeu6uLjbeyoJbmqgKVZ0nZgcokEKx49eg==", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/umi-web3js-adapters": "^0.9.1" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1", + "@solana/web3.js": "^1.72.0" + } + }, + "node_modules/@metaplex-foundation/umi-uploader-bundlr": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-uploader-bundlr/-/umi-uploader-bundlr-0.9.1.tgz", + "integrity": "sha512-R2apPj0lWSem1zI0Tqk1MAi+nPK3JVcnppwzvczoUs2n+vuQdk6cQSLE+sbHiba7Gq1bx/Y7DwLLayhLwY8GcA==", + "license": "MIT", + "dependencies": { + "@bundlr-network/client": "^0.8.8", + "@metaplex-foundation/umi-web3js-adapters": "^0.9.1", + "bignumber.js": "^9.0.2", + "buffer": "^6.0.3" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1", + "@solana/web3.js": "^1.72.0" + } + }, + "node_modules/@metaplex-foundation/umi-web3js-adapters": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@metaplex-foundation/umi-web3js-adapters/-/umi-web3js-adapters-0.9.1.tgz", + "integrity": "sha512-O6lQGJFebRM8P67ajvUpuctJ/J39Lylp4wyg8E1tHmFxUsdBC7M9qBixi/WmCiNKgSfVrq6MmiYaba3OSrtqwg==", + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3" + }, + "peerDependencies": { + "@metaplex-foundation/umi": "^0.9.1", + "@solana/web3.js": "^1.72.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.5.0.tgz", + "integrity": "sha512-J5EKamIHnKPyClwVrzmaf5wSdQXgdHcPZIZLu3bwnbeCx8/7NPK5q2ZBWF+5FvYGByjiQQsJYX6jfgB2wDPn3A==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/ed25519": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", + "integrity": "sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@randlabs/communication-bridge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@randlabs/communication-bridge/-/communication-bridge-1.0.1.tgz", + "integrity": "sha512-CzS0U8IFfXNK7QaJFE4pjbxDGfPjbXBEsEaCn9FN15F+ouSAEUQkva3Gl66hrkBZOGexKFEWMwUHIDKpZ2hfVg==", + "license": "Apache-2.0" + }, + "node_modules/@randlabs/myalgo-connect": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@randlabs/myalgo-connect/-/myalgo-connect-1.4.2.tgz", + "integrity": "sha512-K9hEyUi7G8tqOp7kWIALJLVbGCByhilcy6123WfcorxWwiE1sbQupPyIU5f3YdQK6wMjBsyTWiLW52ZBMp7sXA==", + "license": "Apache-2.0", + "dependencies": { + "@randlabs/communication-bridge": "1.0.1" + } + }, + "node_modules/@solana/buffer-layout": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", + "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==", + "license": "MIT", + "dependencies": { + "buffer": "~6.0.3" + }, + "engines": { + "node": ">=5.10" + } + }, + "node_modules/@solana/buffer-layout-utils": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz", + "integrity": "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==", + "license": "Apache-2.0", + "dependencies": { + "@solana/buffer-layout": "^4.0.0", + "@solana/web3.js": "^1.32.0", + "bigint-buffer": "^1.1.5", + "bignumber.js": "^9.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@solana/codecs": { + "version": "2.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@solana/codecs/-/codecs-2.0.0-preview.2.tgz", + "integrity": "sha512-4HHzCD5+pOSmSB71X6w9ptweV48Zj1Vqhe732+pcAQ2cMNnN0gMPMdDq7j3YwaZDZ7yrILVV/3+HTnfT77t2yA==", + "dependencies": { + "@solana/codecs-core": "2.0.0-preview.2", + "@solana/codecs-data-structures": "2.0.0-preview.2", + "@solana/codecs-numbers": "2.0.0-preview.2", + "@solana/codecs-strings": "2.0.0-preview.2", + "@solana/options": "2.0.0-preview.2" + } + }, + "node_modules/@solana/codecs-core": { + "version": "2.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-2.0.0-preview.2.tgz", + "integrity": "sha512-gLhCJXieSCrAU7acUJjbXl+IbGnqovvxQLlimztPoGgfLQ1wFYu+XJswrEVQqknZYK1pgxpxH3rZ+OKFs0ndQg==", + "dependencies": { + "@solana/errors": "2.0.0-preview.2" + } + }, + "node_modules/@solana/codecs-data-structures": { + "version": "2.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-preview.2.tgz", + "integrity": "sha512-Xf5vIfromOZo94Q8HbR04TbgTwzigqrKII0GjYr21K7rb3nba4hUW2ir8kguY7HWFBcjHGlU5x3MevKBOLp3Zg==", + "dependencies": { + "@solana/codecs-core": "2.0.0-preview.2", + "@solana/codecs-numbers": "2.0.0-preview.2", + "@solana/errors": "2.0.0-preview.2" + } + }, + "node_modules/@solana/codecs-numbers": { + "version": "2.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.0.0-preview.2.tgz", + "integrity": "sha512-aLZnDTf43z4qOnpTcDsUVy1Ci9im1Md8thWipSWbE+WM9ojZAx528oAql+Cv8M8N+6ALKwgVRhPZkto6E59ARw==", + "dependencies": { + "@solana/codecs-core": "2.0.0-preview.2", + "@solana/errors": "2.0.0-preview.2" + } + }, + "node_modules/@solana/codecs-strings": { + "version": "2.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-2.0.0-preview.2.tgz", + "integrity": "sha512-EgBwY+lIaHHgMJIqVOGHfIfpdmmUDNoNO/GAUGeFPf+q0dF+DtwhJPEMShhzh64X2MeCZcmSO6Kinx0Bvmmz2g==", + "dependencies": { + "@solana/codecs-core": "2.0.0-preview.2", + "@solana/codecs-numbers": "2.0.0-preview.2", + "@solana/errors": "2.0.0-preview.2" + }, + "peerDependencies": { + "fastestsmallesttextencoderdecoder": "^1.0.22" + } + }, + "node_modules/@solana/errors": { + "version": "2.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-2.0.0-preview.2.tgz", + "integrity": "sha512-H2DZ1l3iYF5Rp5pPbJpmmtCauWeQXRJapkDg8epQ8BJ7cA2Ut/QEtC3CMmw/iMTcuS6uemFNLcWvlOfoQhvQuA==", + "dependencies": { + "chalk": "^5.3.0", + "commander": "^12.0.0" + }, + "bin": { + "errors": "bin/cli.js" + } + }, + "node_modules/@solana/errors/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@solana/errors/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@solana/options": { + "version": "2.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@solana/options/-/options-2.0.0-preview.2.tgz", + "integrity": "sha512-FAHqEeH0cVsUOTzjl5OfUBw2cyT8d5Oekx4xcn5hn+NyPAfQJgM3CEThzgRD6Q/4mM5pVUnND3oK/Mt1RzSE/w==", + "dependencies": { + "@solana/codecs-core": "2.0.0-preview.2", + "@solana/codecs-numbers": "2.0.0-preview.2" + } + }, + "node_modules/@solana/spl-memo": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@solana/spl-memo/-/spl-memo-0.2.3.tgz", + "integrity": "sha512-CNsKSsl85ebuVoeGq1LDYi5M/PMs1Pxv2/UsyTgS6b30qrYqZOXha5ouZzgGKtJtZ3C3dxfOAEw6caJPN1N63w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "^6.0.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/web3.js": "^1.20.0" + } + }, + "node_modules/@solana/spl-token": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.11.tgz", + "integrity": "sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==", + "license": "Apache-2.0", + "dependencies": { + "@solana/buffer-layout": "^4.0.0", + "@solana/buffer-layout-utils": "^0.2.0", + "@solana/spl-token-metadata": "^0.1.2", + "buffer": "^6.0.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/web3.js": "^1.88.0" + } + }, + "node_modules/@solana/spl-token-metadata": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@solana/spl-token-metadata/-/spl-token-metadata-0.1.4.tgz", + "integrity": "sha512-N3gZ8DlW6NWDV28+vCCDJoTqaCZiF/jDUnk3o8GRkAFzHObiR60Bs1gXHBa8zCPdvOwiG6Z3dg5pg7+RW6XNsQ==", + "dependencies": { + "@solana/codecs": "2.0.0-preview.2", + "@solana/spl-type-length-value": "0.1.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/web3.js": "^1.91.6" + } + }, + "node_modules/@solana/spl-token-registry": { + "version": "0.2.4574", + "resolved": "https://registry.npmjs.org/@solana/spl-token-registry/-/spl-token-registry-0.2.4574.tgz", + "integrity": "sha512-JzlfZmke8Rxug20VT/VpI2XsXlsqMlcORIUivF+Yucj7tFi7A0dXG7h+2UnD0WaZJw8BrUz2ABNkUnv89vbv1A==", + "dev": true, + "license": "Apache", + "dependencies": { + "cross-fetch": "3.0.6" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@solana/spl-token-registry/node_modules/cross-fetch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", + "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "2.6.1" + } + }, + "node_modules/@solana/spl-token-registry/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@solana/spl-type-length-value": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@solana/spl-type-length-value/-/spl-type-length-value-0.1.0.tgz", + "integrity": "sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA==", + "license": "Apache-2.0", + "dependencies": { + "buffer": "^6.0.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-adapter-base": { + "version": "0.9.23", + "resolved": "https://registry.npmjs.org/@solana/wallet-adapter-base/-/wallet-adapter-base-0.9.23.tgz", + "integrity": "sha512-apqMuYwFp1jFi55NxDfvXUX2x1T0Zh07MxhZ/nCCTGys5raSfYUh82zen2BLv8BSDj/JxZ2P/s7jrQZGrX8uAw==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-standard-features": "^1.1.0", + "@wallet-standard/base": "^1.0.1", + "@wallet-standard/features": "^1.0.3", + "eventemitter3": "^4.0.7" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/web3.js": "^1.77.3" + } + }, + "node_modules/@solana/wallet-standard-features": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-features/-/wallet-standard-features-1.2.0.tgz", + "integrity": "sha512-tUd9srDLkRpe1BYg7we+c4UhRQkq+XQWswsr/L1xfGmoRDF47BPSXf4zE7ZU2GRBGvxtGt7lwJVAufQyQYhxTQ==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "^1.0.1", + "@wallet-standard/features": "^1.0.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/web3.js": { + "version": "1.95.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.95.3.tgz", + "integrity": "sha512-O6rPUN0w2fkNqx/Z3QJMB9L225Ex10PRDH8bTaIUPZXMPV0QP8ZpPvjQnXK+upUczlRgzHzd6SjKIha1p+I6og==", + "dependencies": { + "@babel/runtime": "^7.25.0", + "@noble/curves": "^1.4.2", + "@noble/hashes": "^1.4.0", + "@solana/buffer-layout": "^4.0.1", + "agentkeepalive": "^4.5.0", + "bigint-buffer": "^1.1.5", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^4.1.1", + "node-fetch": "^2.7.0", + "rpc-websockets": "^9.0.2", + "superstruct": "^2.0.2" + } + }, + "node_modules/@solana/web3.js/node_modules/superstruct": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", + "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@supercharge/promise-pool": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@supercharge/promise-pool/-/promise-pool-2.4.0.tgz", + "integrity": "sha512-O9CMipBlq5OObdt1uKJGIzm9cdjpPWfj+a+Zw9EgWKxaMNHKC7EU7X9taj3H0EGQNLOSq2jAcOa3EzxlfHsD6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.12.tgz", + "integrity": "sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.17", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.17.tgz", + "integrity": "sha512-zmZ21EWzR71B4Sscphjief5djsLre50M6lI622OSySTmn9DB3j+C3kWroHfBQWXbOBwbgg/M8CG/hUxDLIloow==", + "dev": true + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.16.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz", + "integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" + }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/@wallet-standard/base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@wallet-standard/base/-/base-1.0.1.tgz", + "integrity": "sha512-1To3ekMfzhYxe0Yhkpri+Fedq0SYcfrOfJi3vbLjMwF2qiKPjTGLwZkf2C9ftdQmxES+hmxhBzTwF4KgcOwf8w==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@wallet-standard/features": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wallet-standard/features/-/features-1.0.3.tgz", + "integrity": "sha512-m8475I6W5LTatTZuUz5JJNK42wFRgkJTB0I9tkruMwfqBF2UN2eomkYNVf9RbrsROelCRzSFmugqjKZBFaubsA==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "^1.0.1" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "license": "MIT" + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/algo-msgpack-with-bigint": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz", + "integrity": "sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ==", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/algosdk": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.24.1.tgz", + "integrity": "sha512-9moZxdqeJ6GdE4N6fA/GlUP4LrbLZMYcYkt141J4Ss68OfEgH9qW0wBuZ3ZOKEx/xjc5bg7mLP2Gjg7nwrkmww==", + "license": "MIT", + "dependencies": { + "algo-msgpack-with-bigint": "^2.1.1", + "buffer": "^6.0.2", + "cross-fetch": "^3.1.5", + "hi-base32": "^0.5.1", + "js-sha256": "^0.9.0", + "js-sha3": "^0.8.0", + "js-sha512": "^0.8.0", + "json-bigint": "^1.0.0", + "tweetnacl": "^1.0.3", + "vlq": "^2.0.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/anchor-bankrun": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/anchor-bankrun/-/anchor-bankrun-0.3.0.tgz", + "integrity": "sha512-PYBW5fWX+iGicIS5MIM/omhk1tQPUc0ELAnI/IkLKQJ6d75De/CQRh8MF2bU/TgGyFi6zEel80wUe3uRol9RrQ==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "@coral-xyz/anchor": "^0.28.0", + "@solana/web3.js": "^1.78.4", + "solana-bankrun": "^0.2.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arbundles": { + "version": "0.6.23", + "resolved": "https://registry.npmjs.org/arbundles/-/arbundles-0.6.23.tgz", + "integrity": "sha512-+gr93F3fivN+6dhiImT6BQNaXz4oECPn2GYjCZjS2yEoq7hM78FRvVp6kQyjEdhnuBFQr/q4oS/nkjnQlHdj9Q==", + "license": "Apache-2.0", + "dependencies": { + "@noble/ed25519": "^1.6.1", + "@randlabs/myalgo-connect": "^1.1.2", + "@solana/wallet-adapter-base": "^0.9.2", + "algosdk": "^1.13.1", + "arweave": "^1.11.4", + "arweave-stream-tx": "^1.1.0", + "avsc": "https://github.com/Irys-xyz/avsc#csp-fixes", + "axios": "^0.21.3", + "base64url": "^3.0.1", + "bs58": "^4.0.1", + "ethers": "^5.5.1", + "keccak": "^3.0.2", + "multistream": "^4.1.0", + "process": "^0.11.10", + "secp256k1": "^4.0.2", + "tmp-promise": "^3.0.2" + } + }, + "node_modules/arbundles/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/arconnect": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/arconnect/-/arconnect-0.4.2.tgz", + "integrity": "sha512-Jkpd4QL3TVqnd3U683gzXmZUVqBUy17DdJDuL/3D9rkysLgX6ymJ2e+sR+xyZF5Rh42CBqDXWNMmCjBXeP7Gbw==", + "license": "MIT", + "dependencies": { + "arweave": "^1.10.13" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arweave": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/arweave/-/arweave-1.14.4.tgz", + "integrity": "sha512-tmqU9fug8XAmFETYwgUhLaD3WKav5DaM4p1vgJpEj/Px2ORPPMikwnSySlFymmL2qgRh2ZBcZsg11+RXPPGLsA==", + "license": "MIT", + "dependencies": { + "arconnect": "^0.4.2", + "asn1.js": "^5.4.1", + "base64-js": "^1.5.1", + "bignumber.js": "^9.0.2" + }, + "engines": { + "node": ">=16.15.0" + } + }, + "node_modules/arweave-stream-tx": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/arweave-stream-tx/-/arweave-stream-tx-1.2.2.tgz", + "integrity": "sha512-bNt9rj0hbAEzoUZEF2s6WJbIz8nasZlZpxIw03Xm8fzb9gRiiZlZGW3lxQLjfc9Z0VRUWDzwtqoYeEoB/JDToQ==", + "dependencies": { + "exponential-backoff": "^3.1.0" + }, + "peerDependencies": { + "arweave": "^1.10.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "license": "MIT" + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "license": "MIT", + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/avsc": { + "version": "5.4.7", + "resolved": "git+ssh://git@github.com/Irys-xyz/avsc.git#a730cc8018b79e114b6a3381bbb57760a24c6cef", + "license": "MIT", + "engines": { + "node": ">=0.11" + } + }, + "node_modules/axios": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.7" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base-x": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", + "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "license": "MIT" + }, + "node_modules/bigint-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", + "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "bindings": "^1.3.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bip39": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz", + "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==", + "license": "ISC", + "dependencies": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" + } + }, + "node_modules/bip39-light": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/bip39-light/-/bip39-light-1.0.7.tgz", + "integrity": "sha512-WDTmLRQUsiioBdTs9BmSEmkJza+8xfJmptsNJjxnoq3EydSa/ZBXT6rm66KoT3PJIRYMnhSKNR7S9YL1l7R40Q==", + "license": "ISC", + "dependencies": { + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9" + } + }, + "node_modules/bip39/node_modules/@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==", + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "license": "MIT" + }, + "node_modules/borsh": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", + "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==", + "license": "Apache-2.0", + "dependencies": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-layout": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz", + "integrity": "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==", + "license": "MIT", + "engines": { + "node": ">=4.5" + } + }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/capability": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/capability/-/capability-0.2.5.tgz", + "integrity": "sha512-rsJZYVCgXd08sPqwmaIqjAd5SUTfonV0z/gDJ8D6cN8wQphky1kkAYEqQ+hmDxTw7UihvBfjUVUSY+DBEe44jg==", + "license": "MIT" + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/crypto-hash": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz", + "integrity": "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/csv": { + "version": "6.3.8", + "resolved": "https://registry.npmjs.org/csv/-/csv-6.3.8.tgz", + "integrity": "sha512-gRh3yiT9bHBA5ka2yOpyFqAVu/ZpwWzajMUR/es0ljevAE88WyHBuMUy7jzd2o5j6LYQesEO/AyhbQ9BhbDXUA==", + "license": "MIT", + "dependencies": { + "csv-generate": "^4.4.0", + "csv-parse": "^5.5.5", + "csv-stringify": "^6.4.6", + "stream-transform": "^3.3.1" + }, + "engines": { + "node": ">= 0.1.90" + } + }, + "node_modules/csv-generate": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-4.4.0.tgz", + "integrity": "sha512-geM01acNPZ0wr4/9sKev5fCzFG/tsc/NbuFWrhLc47M1zQyUdEJH65+cxTLIVafEwhBjIYwQ7fdOL9roBqVltQ==", + "license": "MIT" + }, + "node_modules/csv-parse": { + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.5.tgz", + "integrity": "sha512-erCk7tyU3yLWAhk6wvKxnyPtftuy/6Ak622gOO7BCJ05+TYffnPCJF905wmOQm+BpkX54OdAl8pveJwUdpnCXQ==", + "license": "MIT" + }, + "node_modules/csv-stringify": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.4.6.tgz", + "integrity": "sha512-h2V2XZ3uOTLilF5dPIptgUfN/o2ia/80Ie0Lly18LAnw5s8Eb7kt8rfxSUy24AztJZas9f6DPZpVlzDUtFt/ag==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/error-polyfill": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/error-polyfill/-/error-polyfill-0.1.3.tgz", + "integrity": "sha512-XHJk60ufE+TG/ydwp4lilOog549iiQF2OAPhkk9DdiYWMrltz5yhDz/xnKuenNwP7gy3dsibssO5QpVhkrSzzg==", + "license": "MIT", + "dependencies": { + "capability": "^0.2.5", + "o3": "^1.0.3", + "u3": "^0.1.1" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "license": "Apache-2.0" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "engines": { + "node": "> 0.1.90" + } + }, + "node_modules/fast-stable-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", + "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==", + "license": "MIT" + }, + "node_modules/fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==", + "peer": true + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", + "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hi-base32": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", + "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==", + "license": "MIT" + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jayson": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.2.tgz", + "integrity": "sha512-5nzMWDHy6f+koZOuYsArh2AXs73NfWYVlFyJJuCedr93GpY+Ku8qq10ropSXVfHK+H0T6paA88ww+/dV+1fBNA==", + "dependencies": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "JSONStream": "^1.3.5", + "uuid": "^8.3.2", + "ws": "^7.5.10" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jayson/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + }, + "node_modules/jayson/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/jayson/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==", + "license": "MIT" + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "license": "MIT" + }, + "node_modules/js-sha512": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", + "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multistream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.1.0.tgz", + "integrity": "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "once": "^1.4.0", + "readable-stream": "^3.6.0" + } + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, + "node_modules/nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true, + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/near-api-js": { + "version": "0.44.2", + "resolved": "https://registry.npmjs.org/near-api-js/-/near-api-js-0.44.2.tgz", + "integrity": "sha512-eMnc4V+geggapEUa3nU2p8HSHn/njtloI4P2mceHQWO8vDE1NGpnAw8FuTBrLmXSgIv9m6oocgFc9t3VNf5zwg==", + "license": "(MIT AND Apache-2.0)", + "dependencies": { + "bn.js": "5.2.0", + "borsh": "^0.6.0", + "bs58": "^4.0.0", + "depd": "^2.0.0", + "error-polyfill": "^0.1.3", + "http-errors": "^1.7.2", + "js-sha256": "^0.9.0", + "mustache": "^4.0.0", + "node-fetch": "^2.6.1", + "text-encoding-utf-8": "^1.0.2", + "tweetnacl": "^1.0.1" + } + }, + "node_modules/near-api-js/node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", + "license": "MIT" + }, + "node_modules/near-api-js/node_modules/borsh": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.6.0.tgz", + "integrity": "sha512-sl5k89ViqsThXQpYa9XDtz1sBl3l1lI313cFUY1HKr+wvMILnb+58xpkqTNrYbelh99dY7K8usxoCusQmqix9Q==", + "license": "Apache-2.0", + "dependencies": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "node_modules/near-hd-key": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/near-hd-key/-/near-hd-key-1.2.1.tgz", + "integrity": "sha512-SIrthcL5Wc0sps+2e1xGj3zceEa68TgNZDLuCx0daxmfTP7sFTB3/mtE2pYhlFsCxWoMn+JfID5E1NlzvvbRJg==", + "license": "MIT", + "dependencies": { + "bip39": "3.0.2", + "create-hmac": "1.1.7", + "tweetnacl": "1.0.3" + } + }, + "node_modules/near-seed-phrase": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/near-seed-phrase/-/near-seed-phrase-0.2.0.tgz", + "integrity": "sha512-NpmrnejpY1AdlRpDZ0schJQJtfBaoUheRfiYtQpcq9TkwPgqKZCRULV5L3hHmLc0ep7KRtikbPQ9R2ztN/3cyQ==", + "license": "MIT", + "dependencies": { + "bip39-light": "^1.0.7", + "bs58": "^4.0.1", + "near-hd-key": "^1.2.1", + "tweetnacl": "^1.0.2" + } + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/o3": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/o3/-/o3-1.0.3.tgz", + "integrity": "sha512-f+4n+vC6s4ysy7YO7O2gslWZBUu8Qj2i2OUJOvjRxQva7jVjYjB29jrr9NCjmxZQR0gzrOcv1RnqoYOeMs5VRQ==", + "license": "MIT", + "dependencies": { + "capability": "^0.2.5" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "license": "MIT", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rpc-websockets": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-9.0.2.tgz", + "integrity": "sha512-YzggvfItxMY3Lwuax5rC18inhbjJv9Py7JXRHxTIi94JOLrqBsSsUUc5bbl5W6c11tXhdfpDPK0KzBhoGe8jjw==", + "dependencies": { + "@swc/helpers": "^0.5.11", + "@types/uuid": "^8.3.4", + "@types/ws": "^8.2.2", + "buffer": "^6.0.3", + "eventemitter3": "^5.0.1", + "uuid": "^8.3.2", + "ws": "^8.5.0" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + } + }, + "node_modules/rpc-websockets/node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/rpc-websockets/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/rpc-websockets/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "license": "MIT" + }, + "node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/solana-bankrun": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/solana-bankrun/-/solana-bankrun-0.2.0.tgz", + "integrity": "sha512-TS6vYoO/9YJZng7oiLOVyuz8V7yLow5Hp4SLYWW71XM3702v+z9f1fvUBKudRfa4dfpta4tRNufApSiBIALxJQ==", + "license": "MIT", + "dependencies": { + "@solana/web3.js": "^1.68.0", + "bs58": "^4.0.1" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "solana-bankrun-darwin-arm64": "0.2.0", + "solana-bankrun-darwin-universal": "0.2.0", + "solana-bankrun-darwin-x64": "0.2.0", + "solana-bankrun-linux-x64-gnu": "0.2.0", + "solana-bankrun-linux-x64-musl": "0.2.0" + } + }, + "node_modules/solana-bankrun-darwin-arm64": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-darwin-arm64/-/solana-bankrun-darwin-arm64-0.2.0.tgz", + "integrity": "sha512-ENQ5Z/CYeY8ZVWIc2VutY/gMlBaHi93/kDw9w0iVwewoV+/YpQmP2irwrshIKu6ggRPTF3Ehlh2V6fGVIYWcXw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solana-bankrun-darwin-universal": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-darwin-universal/-/solana-bankrun-darwin-universal-0.2.0.tgz", + "integrity": "sha512-HE45TvZXzBipm1fMn87+fkHeIuQ/KFAi5G/S29y/TLuBYt4RDI935RkWiT0rEQ7KwnwO6Y1aTsOaQXldY5R7uQ==", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solana-bankrun-darwin-x64": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-darwin-x64/-/solana-bankrun-darwin-x64-0.2.0.tgz", + "integrity": "sha512-42UsVrnac2Oo4UaIDo60zfI3Xn1i8W6fmcc9ixJQZNTtdO8o2/sY4mFxcJx9lhLMhda5FPHrQbGYgYdIs0kK0g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solana-bankrun-linux-x64-gnu": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-linux-x64-gnu/-/solana-bankrun-linux-x64-gnu-0.2.0.tgz", + "integrity": "sha512-WnqQjfBBdcI0ZLysjvRStI8gX7vm1c3CI6CC03lgkUztH+Chcq9C4LI9m2M8mXza8Xkn9ryeKAmX36Bx/yoVzg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solana-bankrun-linux-x64-musl": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-linux-x64-musl/-/solana-bankrun-linux-x64-musl-0.2.0.tgz", + "integrity": "sha512-8mtf14ZBoah30+MIJBUwb5BlGLRZyK5cZhCkYnC/ROqaIDN8RxMM44NL63gTUIaNHsFwWGA9xR0KSeljeh3PKQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spl-token-bankrun": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/spl-token-bankrun/-/spl-token-bankrun-0.2.6.tgz", + "integrity": "sha512-tD4qpg7s4bagnjlWiDKaY158TcYq9odecM2rwqlKFjKzeVyjYnyoQq54BxwofghWD9Q+gOR/0BbWYK2KW2VbJQ==", + "license": "GPL-3.0-or-later", + "dependencies": { + "@solana/spl-token": "^0.3.8", + "solana-bankrun": "^0.3.0" + } + }, + "node_modules/spl-token-bankrun/node_modules/solana-bankrun": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/solana-bankrun/-/solana-bankrun-0.3.0.tgz", + "integrity": "sha512-YkH7sa8TB/AoRPzG17CXJtYsRIQHEkEqGLz1Vwc13taXhDBkjO7z6NI5JYw7n0ybRymDHwMYTc7sd+5J40TyVQ==", + "license": "MIT", + "dependencies": { + "@solana/web3.js": "^1.68.0", + "bs58": "^4.0.1" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "solana-bankrun-darwin-arm64": "0.3.0", + "solana-bankrun-darwin-universal": "0.3.0", + "solana-bankrun-darwin-x64": "0.3.0", + "solana-bankrun-linux-x64-gnu": "0.3.0", + "solana-bankrun-linux-x64-musl": "0.3.0" + } + }, + "node_modules/spl-token-bankrun/node_modules/solana-bankrun-darwin-arm64": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-darwin-arm64/-/solana-bankrun-darwin-arm64-0.3.0.tgz", + "integrity": "sha512-+NbDncf0U6l3knuacRBiqpjZ2DSp+5lZaAU518gH7/x6qubbui/d000STaIBK+uNTPBS/AL/bCN+7PkXqmA3lA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/spl-token-bankrun/node_modules/solana-bankrun-darwin-universal": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-darwin-universal/-/solana-bankrun-darwin-universal-0.3.0.tgz", + "integrity": "sha512-1/F0xdMa4qvc5o6z16FCCbZ5jbdvKvxpx5kyPcMWRiRPwyvi+zltMxciPAYMlg3wslQqGz88uFhrBEzq2eTumQ==", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/spl-token-bankrun/node_modules/solana-bankrun-darwin-x64": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-darwin-x64/-/solana-bankrun-darwin-x64-0.3.0.tgz", + "integrity": "sha512-U6CANjkmMl+lgNA7UH0GKs5V7LtVIUDzJBZefGGqLfqUNv3EjA/PrrToM0hAOWJgkxSwdz6zW+p5sw5FmnbXtg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/spl-token-bankrun/node_modules/solana-bankrun-linux-x64-gnu": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-linux-x64-gnu/-/solana-bankrun-linux-x64-gnu-0.3.0.tgz", + "integrity": "sha512-qJSkCFs0k2n4XtTnyxGMiZsuqO2TiqTYgWjQ+3mZhGNUAMys/Vq8bd7/SyBm6RR7EfVuRXRxZvh+F8oKZ77V4w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/spl-token-bankrun/node_modules/solana-bankrun-linux-x64-musl": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/solana-bankrun-linux-x64-musl/-/solana-bankrun-linux-x64-musl-0.3.0.tgz", + "integrity": "sha512-xsS2CS2xb1Sw4ivNXM0gPz/qpW9BX0neSvt/pnok5L330Nu9xlTnKAY8FhzzqOP9P9sJlGRM787Y6d0yYwt6xQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-transform": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-3.3.1.tgz", + "integrity": "sha512-BL8pv9QL8Ikd11oZwlRDp1qYMhGR0i50zI9ltoijKGc4ubQWal/Rc4p6SYJp1TBOGpE0uAGchwbxOZ1ycwTuqQ==", + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superstruct": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.15.5.tgz", + "integrity": "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==" + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/text-encoding-utf-8": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", + "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/tmp-promise/node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "license": "MIT" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-mocha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz", + "integrity": "sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ts-node": "7.0.1" + }, + "bin": { + "ts-mocha": "bin/ts-mocha" + }, + "engines": { + "node": ">= 6.X.X" + }, + "optionalDependencies": { + "tsconfig-paths": "^3.5.0" + }, + "peerDependencies": { + "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X" + } + }, + "node_modules/ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/tsx": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz", + "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.19.10", + "get-tsconfig": "^4.7.2" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/u3": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/u3/-/u3-0.1.1.tgz", + "integrity": "sha512-+J5D5ir763y+Am/QY6hXNRlwljIeRMZMGs0cT6qqZVVzzT3X3nFPXVyPOFRMOR4kupB0T8JnCdpWdp6Q/iXn3w==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vlq": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-2.0.4.tgz", + "integrity": "sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==", + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "sdk": { + "name": "@metadaoproject/futarchy", + "version": "0.3.0-alpha.12", + "dependencies": { + "@coral-xyz/anchor": "^0.29.0", + "@noble/hashes": "^1.4.0", + "@solana/spl-token": "^0.3.7", + "@solana/web3.js": "^1.74.0", + "bn.js": "^5.2.1", + "decimal.js": "^10.4.3", + "esbuild": "^0.17.15" + }, + "devDependencies": { + "@types/bn.js": "^5.1.0", + "@types/chai": "^4.3.0", + "@types/mocha": "^9.0.0", + "chai": "^4.3.4", + "mocha": "^9.0.3", + "prettier": "^2.6.2", + "solana-bankrun": "^0.2.0", + "spl-token-bankrun": "0.2.3", + "ts-mocha": "^10.0.0", + "typescript": "^4.3.5" + } + }, + "sdk/node_modules/@coral-xyz/anchor": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.29.0.tgz", + "integrity": "sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA==", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@coral-xyz/borsh": "^0.29.0", + "@noble/hashes": "^1.3.1", + "@solana/web3.js": "^1.68.0", + "bn.js": "^5.1.2", + "bs58": "^4.0.1", + "buffer-layout": "^1.2.2", + "camelcase": "^6.3.0", + "cross-fetch": "^3.1.5", + "crypto-hash": "^1.3.0", + "eventemitter3": "^4.0.7", + "pako": "^2.0.3", + "snake-case": "^3.0.4", + "superstruct": "^0.15.4", + "toml": "^3.0.0" + }, + "engines": { + "node": ">=11" + } + }, + "sdk/node_modules/@coral-xyz/borsh": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.29.0.tgz", + "integrity": "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==", + "license": "Apache-2.0", + "dependencies": { + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@solana/web3.js": "^1.68.0" + } + }, + "sdk/node_modules/spl-token-bankrun": { + "version": "0.2.3", + "dev": true, + "license": "GPL-3.0-or-later", + "dependencies": { + "@solana/spl-token": "^0.3.8", + "solana-bankrun": "^0.2.0" + } + } + } +} diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 89f02637..a7d9bb4f 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -230,15 +230,10 @@ export class ConditionalVaultClient { underlyingTokenMint: PublicKey, amount: BN, numOutcomes: number, - user?: PublicKey | Keypair + user: PublicKey | Keypair = this.provider.publicKey ) { - console.log(typeof user); - console.log(user); - console.log(user instanceof Keypair); - const userPubkey = - user instanceof Keypair - ? user.publicKey - : user || this.provider.publicKey; + const userPubkey: PublicKey = + "publicKey" in user ? user.publicKey : (user as PublicKey); console.log("userPubkey", userPubkey); @@ -255,11 +250,7 @@ export class ConditionalVaultClient { let userConditionalAccounts = []; for (let conditionalTokenMint of conditionalTokenMintAddrs) { userConditionalAccounts.push( - getAssociatedTokenAddressSync( - conditionalTokenMint, - this.provider.publicKey, - true - ) + getAssociatedTokenAddressSync(conditionalTokenMint, userPubkey, true) ); } @@ -267,7 +258,7 @@ export class ConditionalVaultClient { .splitTokens(amount) .accounts({ question, - authority: this.provider.publicKey, + authority: userPubkey, vault, vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, @@ -276,7 +267,7 @@ export class ConditionalVaultClient { ), userUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, - this.provider.publicKey, + userPubkey, true ), }) @@ -302,9 +293,9 @@ export class ConditionalVaultClient { }) ); - if (user instanceof Keypair) { - ix = ix.signers([user]); - } + // if (user instanceof Keypair) { + // ix = ix.signers([user]); + // } return ix; } diff --git a/tests/conditionalVault/integration/1.test.ts b/tests/conditionalVault/integration/1.test.ts index acfc1816..168ae82e 100644 --- a/tests/conditionalVault/integration/1.test.ts +++ b/tests/conditionalVault/integration/1.test.ts @@ -2,7 +2,8 @@ import { ConditionalVaultClient, sha256 } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import BN from "bn.js"; import { assert } from "chai"; -import { createMint, getMint, mintTo, createAssociatedTokenAccount } from "spl-token-bankrun" +import { createMint, getMint, mintTo, createAssociatedTokenAccount } from "spl-token-bankrun"; +import * as token from "@solana/spl-token"; export default async function test() { // A binary prediction market test. Alice, Bob, and Charlie are betting on @@ -68,7 +69,7 @@ export default async function test() { const vault = await vaultClient.initializeNewVault(question, USDC, 2); - await vaultClient.splitTokensIx(question, vault, USDC, new BN(100 * 10 ** 6), 2, alice).rpc(); + await vaultClient.splitTokensIx(question, vault, USDC, new BN(100 * 10 ** 6), 2, alice).signers([alice]).rpc(); From ab7838fee695b3fefb64662a473d2e671164aaed Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 22/52] Add merging to the integration test --- sdk/src/ConditionalVaultClient.ts | 25 ++++++------- tests/conditionalVault/integration/1.test.ts | 38 +++++++++++++++++++- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index a7d9bb4f..6520c1eb 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -235,8 +235,6 @@ export class ConditionalVaultClient { const userPubkey: PublicKey = "publicKey" in user ? user.publicKey : (user as PublicKey); - console.log("userPubkey", userPubkey); - let conditionalTokenMintAddrs = []; for (let i = 0; i < numOutcomes; i++) { const [conditionalTokenMint] = getConditionalTokenMintAddr( @@ -305,8 +303,12 @@ export class ConditionalVaultClient { vault: PublicKey, underlyingTokenMint: PublicKey, amount: BN, - numOutcomes: number + numOutcomes: number, + user: PublicKey | Keypair = this.provider.publicKey ) { + const userPubkey: PublicKey = + "publicKey" in user ? user.publicKey : (user as PublicKey); + let conditionalTokenMintAddrs = []; for (let i = 0; i < numOutcomes; i++) { const [conditionalTokenMint] = getConditionalTokenMintAddr( @@ -320,11 +322,7 @@ export class ConditionalVaultClient { let userConditionalAccounts = []; for (let conditionalTokenMint of conditionalTokenMintAddrs) { userConditionalAccounts.push( - getAssociatedTokenAddressSync( - conditionalTokenMint, - this.provider.publicKey, - true - ) + getAssociatedTokenAddressSync(conditionalTokenMint, userPubkey, true) ); } @@ -332,7 +330,7 @@ export class ConditionalVaultClient { .mergeTokens(amount) .accounts({ question, - authority: this.provider.publicKey, + authority: userPubkey, vault, vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, @@ -341,7 +339,7 @@ export class ConditionalVaultClient { ), userUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, - this.provider.publicKey, + userPubkey, true ), }) @@ -349,11 +347,8 @@ export class ConditionalVaultClient { conditionalTokenMintAddrs.map((conditionalTokenMint) => { return createAssociatedTokenAccountIdempotentInstruction( this.provider.publicKey, - getAssociatedTokenAddressSync( - conditionalTokenMint, - this.provider.publicKey - ), - this.provider.publicKey, + getAssociatedTokenAddressSync(conditionalTokenMint, userPubkey), + userPubkey, conditionalTokenMint ); }) diff --git a/tests/conditionalVault/integration/1.test.ts b/tests/conditionalVault/integration/1.test.ts index 168ae82e..bf127657 100644 --- a/tests/conditionalVault/integration/1.test.ts +++ b/tests/conditionalVault/integration/1.test.ts @@ -2,7 +2,7 @@ import { ConditionalVaultClient, sha256 } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import BN from "bn.js"; import { assert } from "chai"; -import { createMint, getMint, mintTo, createAssociatedTokenAccount } from "spl-token-bankrun"; +import { createMint, getMint, mintTo, createAssociatedTokenAccount, transfer } from "spl-token-bankrun"; import * as token from "@solana/spl-token"; export default async function test() { @@ -68,9 +68,45 @@ export default async function test() { ); const vault = await vaultClient.initializeNewVault(question, USDC, 2); + const storedVault = await vaultClient.fetchVault(vault); await vaultClient.splitTokensIx(question, vault, USDC, new BN(100 * 10 ** 6), 2, alice).signers([alice]).rpc(); + await vaultClient.splitTokensIx(question, vault, USDC, new BN(100 * 10 ** 6), 2, bob).signers([bob]).rpc(); + + const TRUMP = storedVault.conditionalTokenMints[0]; + const HARRIS = storedVault.conditionalTokenMints[1]; + + const aliceTRUMP = token.getAssociatedTokenAddressSync(TRUMP, alice.publicKey); + const aliceHARRIS = token.getAssociatedTokenAddressSync(HARRIS, alice.publicKey); + + const bobTRUMP = token.getAssociatedTokenAddressSync(TRUMP, bob.publicKey); + const bobHARRIS = token.getAssociatedTokenAddressSync(HARRIS, bob.publicKey); + + await transfer(this.banksClient, this.payer, aliceHARRIS, bobHARRIS, alice, new BN(30 * 10 ** 6)); + await transfer(this.banksClient, this.payer, bobTRUMP, aliceTRUMP, bob, new BN(40 * 10 ** 6)); + + await vaultClient.mergeTokensIx(question, vault, USDC, new BN(15 * 10 ** 6), 2, alice).signers([alice]).rpc(); + + + + // const aliceHARRIS = await createAssociatedTokenAccount(this.banksClient, this.payer, HARRIS, alice.publicKey); + + + + // storedVault. + + + + // banksClient: BanksClient, + // payer: Signer, + // source: PublicKey, + // destination: PublicKey, + // owner: PublicKey | Signer, + // amount: number | bigint, + + + // let question: PublicKey = PublicKey.default; From 79b60e6294fc7d6e60812447f39027e45ac0d15d Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 23/52] Complete integration tests for conditional vault --- sdk/src/ConditionalVaultClient.ts | 90 ++++++-------- tests/conditionalVault/integration/1.test.ts | 117 ------------------ .../binaryPredictionMarket.test.ts | 81 ++++++++++++ .../integration/scalarGrantMarket.test.ts | 52 ++++++++ tests/conditionalVault/main.test.ts | 6 +- .../unit/redeemTokens.test.ts | 4 +- tests/main.test.ts | 37 +++++- 7 files changed, 210 insertions(+), 177 deletions(-) delete mode 100644 tests/conditionalVault/integration/1.test.ts create mode 100644 tests/conditionalVault/integration/binaryPredictionMarket.test.ts create mode 100644 tests/conditionalVault/integration/scalarGrantMarket.test.ts diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 6520c1eb..aead4ae4 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -224,17 +224,7 @@ export class ConditionalVaultClient { ); } - splitTokensIx( - question: PublicKey, - vault: PublicKey, - underlyingTokenMint: PublicKey, - amount: BN, - numOutcomes: number, - user: PublicKey | Keypair = this.provider.publicKey - ) { - const userPubkey: PublicKey = - "publicKey" in user ? user.publicKey : (user as PublicKey); - + getConditionalTokenMints(vault: PublicKey, numOutcomes: number): PublicKey[] { let conditionalTokenMintAddrs = []; for (let i = 0; i < numOutcomes; i++) { const [conditionalTokenMint] = getConditionalTokenMintAddr( @@ -244,11 +234,26 @@ export class ConditionalVaultClient { ); conditionalTokenMintAddrs.push(conditionalTokenMint); } + return conditionalTokenMintAddrs; + } + + splitTokensIx( + question: PublicKey, + vault: PublicKey, + underlyingTokenMint: PublicKey, + amount: BN, + numOutcomes: number, + user: PublicKey = this.provider.publicKey + ) { + let conditionalTokenMintAddrs = this.getConditionalTokenMints( + vault, + numOutcomes + ); let userConditionalAccounts = []; for (let conditionalTokenMint of conditionalTokenMintAddrs) { userConditionalAccounts.push( - getAssociatedTokenAddressSync(conditionalTokenMint, userPubkey, true) + getAssociatedTokenAddressSync(conditionalTokenMint, user, true) ); } @@ -256,7 +261,7 @@ export class ConditionalVaultClient { .splitTokens(amount) .accounts({ question, - authority: userPubkey, + authority: user, vault, vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, @@ -265,7 +270,7 @@ export class ConditionalVaultClient { ), userUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, - userPubkey, + user, true ), }) @@ -273,8 +278,8 @@ export class ConditionalVaultClient { conditionalTokenMintAddrs.map((conditionalTokenMint) => { return createAssociatedTokenAccountIdempotentInstruction( this.provider.publicKey, - getAssociatedTokenAddressSync(conditionalTokenMint, userPubkey), - userPubkey, + getAssociatedTokenAddressSync(conditionalTokenMint, user), + user, conditionalTokenMint ); }) @@ -291,10 +296,6 @@ export class ConditionalVaultClient { }) ); - // if (user instanceof Keypair) { - // ix = ix.signers([user]); - // } - return ix; } @@ -304,25 +305,17 @@ export class ConditionalVaultClient { underlyingTokenMint: PublicKey, amount: BN, numOutcomes: number, - user: PublicKey | Keypair = this.provider.publicKey + user: PublicKey = this.provider.publicKey ) { - const userPubkey: PublicKey = - "publicKey" in user ? user.publicKey : (user as PublicKey); - - let conditionalTokenMintAddrs = []; - for (let i = 0; i < numOutcomes; i++) { - const [conditionalTokenMint] = getConditionalTokenMintAddr( - this.vaultProgram.programId, - vault, - i - ); - conditionalTokenMintAddrs.push(conditionalTokenMint); - } + let conditionalTokenMintAddrs = this.getConditionalTokenMints( + vault, + numOutcomes + ); let userConditionalAccounts = []; for (let conditionalTokenMint of conditionalTokenMintAddrs) { userConditionalAccounts.push( - getAssociatedTokenAddressSync(conditionalTokenMint, userPubkey, true) + getAssociatedTokenAddressSync(conditionalTokenMint, user, true) ); } @@ -330,7 +323,7 @@ export class ConditionalVaultClient { .mergeTokens(amount) .accounts({ question, - authority: userPubkey, + authority: user, vault, vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, @@ -339,7 +332,7 @@ export class ConditionalVaultClient { ), userUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, - userPubkey, + user, true ), }) @@ -347,8 +340,8 @@ export class ConditionalVaultClient { conditionalTokenMintAddrs.map((conditionalTokenMint) => { return createAssociatedTokenAccountIdempotentInstruction( this.provider.publicKey, - getAssociatedTokenAddressSync(conditionalTokenMint, userPubkey), - userPubkey, + getAssociatedTokenAddressSync(conditionalTokenMint, user), + user, conditionalTokenMint ); }) @@ -372,8 +365,8 @@ export class ConditionalVaultClient { question: PublicKey, vault: PublicKey, underlyingTokenMint: PublicKey, - amount: BN, - numOutcomes: number + numOutcomes: number, + user: PublicKey = this.provider.publicKey ) { let conditionalTokenMintAddrs = []; for (let i = 0; i < numOutcomes; i++) { @@ -388,11 +381,7 @@ export class ConditionalVaultClient { let userConditionalAccounts = []; for (let conditionalTokenMint of conditionalTokenMintAddrs) { userConditionalAccounts.push( - getAssociatedTokenAddressSync( - conditionalTokenMint, - this.provider.publicKey, - true - ) + getAssociatedTokenAddressSync(conditionalTokenMint, user, true) ); } @@ -400,7 +389,7 @@ export class ConditionalVaultClient { .redeemTokens() .accounts({ question, - authority: this.provider.publicKey, + authority: user, vault, vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, @@ -409,7 +398,7 @@ export class ConditionalVaultClient { ), userUnderlyingTokenAccount: getAssociatedTokenAddressSync( underlyingTokenMint, - this.provider.publicKey, + user, true ), }) @@ -417,11 +406,8 @@ export class ConditionalVaultClient { conditionalTokenMintAddrs.map((conditionalTokenMint) => { return createAssociatedTokenAccountIdempotentInstruction( this.provider.publicKey, - getAssociatedTokenAddressSync( - conditionalTokenMint, - this.provider.publicKey - ), - this.provider.publicKey, + getAssociatedTokenAddressSync(conditionalTokenMint, user), + user, conditionalTokenMint ); }) diff --git a/tests/conditionalVault/integration/1.test.ts b/tests/conditionalVault/integration/1.test.ts deleted file mode 100644 index bf127657..00000000 --- a/tests/conditionalVault/integration/1.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { ConditionalVaultClient, sha256 } from "@metadaoproject/futarchy"; -import { Keypair, PublicKey } from "@solana/web3.js"; -import BN from "bn.js"; -import { assert } from "chai"; -import { createMint, getMint, mintTo, createAssociatedTokenAccount, transfer } from "spl-token-bankrun"; -import * as token from "@solana/spl-token"; - -export default async function test() { - // A binary prediction market test. Alice, Bob, and Charlie are betting on - // who's going to win the next election: Trump or Harris. Alice and Bob each - // split 100 USDC into TRUMP and HARRIS tokens. Alice is a trump supporter - // and buys 30 TRUMP tokens in exchange for 40 HARRIS tokens from Bob. Charlie - // buys an additional 30 HARRIS tokens from Alice. - - // Alice should have 130 TRUMP and 30 HARRIS tokens. Bob should have 70 TRUMP - // and 140 HARRIS tokens. Charlie should have 30 HARRIS tokens. - - // Alice faces a cash crunch, and merges 15 TRUMP and 15 HARRIS tokens into 15 - // USDC. She should now have 15 USDC, 15 TRUMP, and 15 HARRIS tokens. - - // Bob becomes a trump supporter, and as a display of his newfound - // allegiance, he sends all of his HARRIS tokens to the vault (to burn them). - // Bob should now have 70 TRUMP tokens. - - // The market resolves in favor of Harris. When redeeming, Alice should get 15 - // USDC, Bob should get nothing, and Charlie should get 30 USDC. - - let vaultClient: ConditionalVaultClient = this.vaultClient; - - let alice: Keypair = Keypair.generate(); - let bob: Keypair = Keypair.generate(); - let charlie: Keypair = Keypair.generate(); - let operator: Keypair = Keypair.generate(); - - let question: PublicKey = await vaultClient.initializeQuestion(sha256(new TextEncoder().encode("Who's going to win the next election?")), operator.publicKey, 2); - - let USDC: PublicKey = await createMint(this.banksClient, this.payer, operator.publicKey, null, 6); - - let createUSDCAccount = async (owner: PublicKey) => { - return await createAssociatedTokenAccount( - this.banksClient, - this.payer, - USDC, - owner - ); - } - - let aliceUSDC = await createUSDCAccount(alice.publicKey); - let bobUSDC = await createUSDCAccount(bob.publicKey); - let charlieUSDC = await createUSDCAccount(charlie.publicKey); - - await mintTo( - this.banksClient, - this.payer, - USDC, - aliceUSDC, - operator, - 100 * 10 ** 6 - ); - - await mintTo( - this.banksClient, - this.payer, - USDC, - bobUSDC, - operator, - 100 * 10 ** 6 - ); - - const vault = await vaultClient.initializeNewVault(question, USDC, 2); - const storedVault = await vaultClient.fetchVault(vault); - - await vaultClient.splitTokensIx(question, vault, USDC, new BN(100 * 10 ** 6), 2, alice).signers([alice]).rpc(); - - await vaultClient.splitTokensIx(question, vault, USDC, new BN(100 * 10 ** 6), 2, bob).signers([bob]).rpc(); - - const TRUMP = storedVault.conditionalTokenMints[0]; - const HARRIS = storedVault.conditionalTokenMints[1]; - - const aliceTRUMP = token.getAssociatedTokenAddressSync(TRUMP, alice.publicKey); - const aliceHARRIS = token.getAssociatedTokenAddressSync(HARRIS, alice.publicKey); - - const bobTRUMP = token.getAssociatedTokenAddressSync(TRUMP, bob.publicKey); - const bobHARRIS = token.getAssociatedTokenAddressSync(HARRIS, bob.publicKey); - - await transfer(this.banksClient, this.payer, aliceHARRIS, bobHARRIS, alice, new BN(30 * 10 ** 6)); - await transfer(this.banksClient, this.payer, bobTRUMP, aliceTRUMP, bob, new BN(40 * 10 ** 6)); - - await vaultClient.mergeTokensIx(question, vault, USDC, new BN(15 * 10 ** 6), 2, alice).signers([alice]).rpc(); - - - - // const aliceHARRIS = await createAssociatedTokenAccount(this.banksClient, this.payer, HARRIS, alice.publicKey); - - - - // storedVault. - - - - // banksClient: BanksClient, - // payer: Signer, - // source: PublicKey, - // destination: PublicKey, - // owner: PublicKey | Signer, - // amount: number | bigint, - - - - - - // let question: PublicKey = PublicKey.default; - // let vault: PublicKey = PublicKey.default; - // let underlyingTokenMint: PublicKey = PublicKey.default; - - // await vaultClient.initializeQuestion(question, underlyingTokenMint, [alice.publicKey, bob.publicKey], 2); -} \ No newline at end of file diff --git a/tests/conditionalVault/integration/binaryPredictionMarket.test.ts b/tests/conditionalVault/integration/binaryPredictionMarket.test.ts new file mode 100644 index 00000000..29e46843 --- /dev/null +++ b/tests/conditionalVault/integration/binaryPredictionMarket.test.ts @@ -0,0 +1,81 @@ +import { ConditionalVaultClient, sha256 } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import BN from "bn.js"; +import { assert } from "chai"; +import { createMint, getMint, mintTo, createAssociatedTokenAccount, transfer, getAccount } from "spl-token-bankrun"; +import * as token from "@solana/spl-token"; + +export default async function test() { + // A binary prediction market test. Alice, Bob, and Charlie are betting on + // who's going to win the next election: Trump or Harris. Alice and Bob each + // split 100 USDC into TRUMP and HARRIS tokens. Alice is a trump supporter + // and buys 30 TRUMP tokens in exchange for 40 HARRIS tokens from Bob. Charlie + // buys an additional 30 HARRIS tokens from Alice. + + // Alice should have 130 TRUMP and 30 HARRIS tokens. Bob should have 70 TRUMP + // and 140 HARRIS tokens. Charlie should have 30 HARRIS tokens. + + // Alice faces a cash crunch, and merges 20 TRUMP and 20 HARRIS tokens into 20 + // USDC. She should now have 20 USDC, 110 TRUMP, and 10 HARRIS tokens. + + // Bob becomes a trump supporter, and as a display of his newfound + // allegiance, he sends all of his HARRIS tokens to the vault (to burn them). + // Bob should now have 70 TRUMP tokens. + + // The market resolves in favor of Harris. When redeeming, Alice should get 15 + // USDC, Bob should get nothing, and Charlie should get 30 USDC. + + let vaultClient: ConditionalVaultClient = this.vaultClient; + + let alice: Keypair = Keypair.generate(); + let bob: Keypair = Keypair.generate(); + let charlie: Keypair = Keypair.generate(); + let operator: Keypair = Keypair.generate(); + + let question: PublicKey = await vaultClient.initializeQuestion(sha256(new TextEncoder().encode("Who's going to win the next election?/TRUMP/HARRIS")), operator.publicKey, 2); + + let USDC: PublicKey = await this.createMint(operator.publicKey, 6); + + await this.createTokenAccount(USDC, alice.publicKey); + await this.createTokenAccount(USDC, bob.publicKey); + await this.createTokenAccount(USDC, charlie.publicKey); + + + await this.mintTo(USDC, alice.publicKey, operator, 100); + await this.mintTo(USDC, bob.publicKey, operator, 100); + + const vault = await vaultClient.initializeNewVault(question, USDC, 2); + const storedVault = await vaultClient.fetchVault(vault); + + await vaultClient.splitTokensIx(question, vault, USDC, new BN(100), 2, alice.publicKey).signers([alice]).rpc(); + + await vaultClient.splitTokensIx(question, vault, USDC, new BN(100), 2, bob.publicKey).signers([bob]).rpc(); + + const TRUMP = storedVault.conditionalTokenMints[0]; + const HARRIS = storedVault.conditionalTokenMints[1]; + + await this.createTokenAccount(HARRIS, charlie.publicKey); + + await this.transfer(HARRIS, alice, bob.publicKey, 40); + await this.transfer(TRUMP, bob, alice.publicKey, 30); + await this.transfer(HARRIS, alice, charlie.publicKey, 30); + + await vaultClient.mergeTokensIx(question, vault, USDC, new BN(20), 2, alice.publicKey).signers([alice]).rpc(); + + await this.assertBalance(USDC, alice.publicKey, 20); + await this.assertBalance(TRUMP, alice.publicKey, 110); + await this.assertBalance(HARRIS, alice.publicKey, 10); + + await this.createTokenAccount(HARRIS, vault); + await this.transfer(HARRIS, bob, vault, 140); + + await vaultClient.resolveQuestionIx(question, operator, [0, 1]).rpc(); + + await vaultClient.redeemTokensIx(question, vault, USDC, 2, alice.publicKey).signers([alice]).rpc(); + await vaultClient.redeemTokensIx(question, vault, USDC, 2, bob.publicKey).signers([bob]).rpc(); + await vaultClient.redeemTokensIx(question, vault, USDC, 2, charlie.publicKey).signers([charlie]).rpc(); + + await this.assertBalance(USDC, alice.publicKey, 30); + await this.assertBalance(USDC, bob.publicKey, 0); + await this.assertBalance(USDC, charlie.publicKey, 30); +} \ No newline at end of file diff --git a/tests/conditionalVault/integration/scalarGrantMarket.test.ts b/tests/conditionalVault/integration/scalarGrantMarket.test.ts new file mode 100644 index 00000000..2fb48a0b --- /dev/null +++ b/tests/conditionalVault/integration/scalarGrantMarket.test.ts @@ -0,0 +1,52 @@ +import { ConditionalVaultClient, sha256 } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import BN from "bn.js"; +import { assert } from "chai"; +import { createMint, getMint, mintTo, createAssociatedTokenAccount, transfer, getAccount } from "spl-token-bankrun"; +import * as token from "@solana/spl-token"; + +export default async function test() { + // A scalar grant market test. Alice splits 100 USDC into E-UP and E-DOWN tokens. + // She sends 30 E-UPs to Bob. The grant committee resolves the question with 60% effectiveness. + // Alice and Bob redeem their tokens. + + let vaultClient: ConditionalVaultClient = this.vaultClient; + + let alice: Keypair = Keypair.generate(); + let bob: Keypair = Keypair.generate(); + let grantCommittee: Keypair = Keypair.generate(); + + let question: PublicKey = await vaultClient.initializeQuestion( + sha256(new TextEncoder().encode("What is the effectiveness of the grant?/E-UP/E-DOWN")), + grantCommittee.publicKey, + 2 + ); + + let USDC: PublicKey = await this.createMint(this.payer.publicKey, 6); + + await this.createTokenAccount(USDC, alice.publicKey); + await this.createTokenAccount(USDC, bob.publicKey); + + await this.mintTo(USDC, alice.publicKey, this.payer, 100); + + const vault = await vaultClient.initializeNewVault(question, USDC, 2); + const storedVault = await vaultClient.fetchVault(vault); + + await vaultClient.splitTokensIx(question, vault, USDC, new BN(100), 2, alice.publicKey).signers([alice]).rpc(); + + const E_UP = storedVault.conditionalTokenMints[0]; + const E_DOWN = storedVault.conditionalTokenMints[1]; + + await this.createTokenAccount(E_UP, bob.publicKey); + + await this.transfer(E_UP, alice, bob.publicKey, 30); + + // Grant committee resolves the question with 60% effectiveness + await vaultClient.resolveQuestionIx(question, grantCommittee, [6, 4]).rpc(); + + await vaultClient.redeemTokensIx(question, vault, USDC, 2, alice.publicKey).signers([alice]).rpc(); + await vaultClient.redeemTokensIx(question, vault, USDC, 2, bob.publicKey).signers([bob]).rpc(); + + await this.assertBalance(USDC, bob.publicKey, 18); + await this.assertBalance(USDC, alice.publicKey, 82); +} diff --git a/tests/conditionalVault/main.test.ts b/tests/conditionalVault/main.test.ts index 4323ecaa..487e813f 100644 --- a/tests/conditionalVault/main.test.ts +++ b/tests/conditionalVault/main.test.ts @@ -4,14 +4,16 @@ import resolveQuestion from "./unit/resolveQuestion.test"; import splitTokens from "./unit/splitTokens.test"; import mergeTokens from "./unit/mergeTokens.test"; import redeemTokens from "./unit/redeemTokens.test"; -import test1 from "./integration/1.test"; +import binaryPredictionMarket from "./integration/binaryPredictionMarket.test"; +import scalarGrantMarket from "./integration/scalarGrantMarket.test"; export default function suite() { + it("binary prediction market", binaryPredictionMarket); + it("scalar grant market", scalarGrantMarket); describe("#initialize_question", initializeQuestion); describe("#initialize_conditional_vault", initializeConditionalVault); describe("#resolve_question", resolveQuestion); describe("#split_tokens", splitTokens); describe("#merge_tokens", mergeTokens); describe("#redeem_tokens", redeemTokens); - it("integration test 1", test1); } \ No newline at end of file diff --git a/tests/conditionalVault/unit/redeemTokens.test.ts b/tests/conditionalVault/unit/redeemTokens.test.ts index b0776117..4b32b512 100644 --- a/tests/conditionalVault/unit/redeemTokens.test.ts +++ b/tests/conditionalVault/unit/redeemTokens.test.ts @@ -66,7 +66,7 @@ export default function suite() { it("can't redeem tokens when question is not resolved", async function () { try { await vaultClient - .redeemTokensIx(question, vault, underlyingTokenMint, new anchor.BN(600), 2) + .redeemTokensIx(question, vault, underlyingTokenMint, 2) .rpc(); assert.fail("Should have thrown an error"); } catch (error) { @@ -88,7 +88,7 @@ export default function suite() { .then(acc => acc.amount); await vaultClient - .redeemTokensIx(question, vault, underlyingTokenMint, new anchor.BN(600), 2) + .redeemTokensIx(question, vault, underlyingTokenMint, 2) .rpc(); const balanceAfter = await getAccount(this.banksClient, underlyingTokenAccount) diff --git a/tests/main.test.ts b/tests/main.test.ts index 2e6b3974..8140e4cf 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -3,6 +3,10 @@ import conditionalVault from "./conditionalVault/main.test"; import { BankrunProvider } from "anchor-bankrun"; import * as anchor from "@coral-xyz/anchor"; import { ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { PublicKey, Keypair} from "@solana/web3.js"; +import { createAssociatedTokenAccount, createMint, mintTo, getAccount, transfer } from "spl-token-bankrun"; +import * as token from "@solana/spl-token"; +import { assert } from "chai"; before(async function () { let context = await startAnchor( @@ -32,10 +36,35 @@ before(async function () { this.vaultClient = ConditionalVaultClient.createClient({ provider: provider as any }); this.payer = provider.wallet.payer; - // }); - // describe("1", () => test1(vaultClient)); - // describe("2", () => test2(vaultClient)); - // test2(); + + + this.createTokenAccount = async (mint: PublicKey, owner: PublicKey) => { + return await createAssociatedTokenAccount( + this.banksClient, + this.payer, + mint, + owner + ); + } + + this.createMint = async (mintAuthority: PublicKey, decimals: number) => { + return await createMint(this.banksClient, this.payer, mintAuthority, null, decimals); + } + + this.mintTo = async (mint: PublicKey, to: PublicKey, mintAuthority: Keypair, amount: number) => { + const tokenAccount = token.getAssociatedTokenAddressSync(mint, to, true); + return await mintTo(this.banksClient, this.payer, mint, tokenAccount, mintAuthority, amount); + } + + this.assertBalance = async (mint: PublicKey, owner: PublicKey, amount: number) => { + const tokenAccount = token.getAssociatedTokenAddressSync(mint, owner, true); + const storedTokenAccount = await getAccount(this.banksClient, tokenAccount); + assert.equal(storedTokenAccount.amount.toString(), amount.toString()); + } + + this.transfer = async (mint: PublicKey, from: Keypair, to: PublicKey, amount: number) => { + return await transfer(this.banksClient, this.payer, token.getAssociatedTokenAddressSync(mint, from.publicKey, true), token.getAssociatedTokenAddressSync(mint, to, true), from, amount); + } }); describe("conditional_vault", conditionalVault); \ No newline at end of file From 180bd0745b28f86830c23876c805a11ff8dcace2 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 24/52] Put `Question` in its own file --- programs/conditional_vault/src/lib.rs | 2 +- .../src/state/conditional_vault.rs | 38 ---- programs/conditional_vault/src/state/mod.rs | 2 + .../conditional_vault/src/state/question.rs | 39 ++++ sdk/src/types/conditional_vault.ts | 200 +++++++++--------- 5 files changed, 142 insertions(+), 139 deletions(-) create mode 100644 programs/conditional_vault/src/state/question.rs diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index 1c634237..2bf3c565 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -28,7 +28,7 @@ security_txt! { policy: "The market will decide whether we pay a bug bounty.", source_code: "https://github.com/metaDAOproject/futarchy", source_release: "v0.4", - auditors: "Neodyme", + auditors: "Neodyme (v0.3)", acknowledgements: "DCF = (CF1 / (1 + r)^1) + (CF2 / (1 + r)^2) + ... (CFn / (1 + r)^n)" } diff --git a/programs/conditional_vault/src/state/conditional_vault.rs b/programs/conditional_vault/src/state/conditional_vault.rs index 78e408b2..34fe9872 100644 --- a/programs/conditional_vault/src/state/conditional_vault.rs +++ b/programs/conditional_vault/src/state/conditional_vault.rs @@ -7,44 +7,6 @@ pub enum VaultStatus { Reverted, } -/// Questions represent statements about future events. -/// -/// These statements include: -/// - "Will this proposal pass?" -/// - "Who, if anyone, will be hired?" -/// - "How effective will the grant committee deem this grant?" -/// -/// Questions have 2 or more possible outcomes. For a question like "will this -/// proposal pass," the outcomes are "yes" and "no." For a question like "who -/// will be hired," the outcomes could be "Alice," "Bob," and "neither." -/// -/// Outcomes resolve to a number between 0 and 1. Binary questions like "will -/// this proposal pass" have outcomes that resolve to exactly 0 or 1. You can -/// also have questions with scalar outcomes. For example, the question "how -/// effective will the grant committee deem this grant" could have two outcomes: -/// "ineffective" and "effective." If the grant committee deems the grant 70% -/// effective, the "effective" outcome would resolve to 0.7 and the "ineffective" -/// outcome would resolve to 0.3. -/// -/// Once resolved, the sum of all outcome resolutions is exactly 1. -#[account] -pub struct Question { - pub question_id: [u8; 32], - pub oracle: Pubkey, - pub payout_numerators: Vec, - pub payout_denominator: u32, -} - -impl Question { - pub fn num_outcomes(&self) -> usize { - self.payout_numerators.len() - } - - pub fn is_resolved(&self) -> bool { - self.payout_denominator != 0 - } -} - #[account] pub struct NewConditionalVault { pub question: Pubkey, diff --git a/programs/conditional_vault/src/state/mod.rs b/programs/conditional_vault/src/state/mod.rs index 49d3a06b..33af445d 100644 --- a/programs/conditional_vault/src/state/mod.rs +++ b/programs/conditional_vault/src/state/mod.rs @@ -1,5 +1,7 @@ use super::*; pub mod conditional_vault; +pub mod question; pub use conditional_vault::*; +pub use question::*; diff --git a/programs/conditional_vault/src/state/question.rs b/programs/conditional_vault/src/state/question.rs new file mode 100644 index 00000000..724ee7ad --- /dev/null +++ b/programs/conditional_vault/src/state/question.rs @@ -0,0 +1,39 @@ +use super::*; + +/// Questions represent statements about future events. +/// +/// These statements include: +/// - "Will this proposal pass?" +/// - "Who, if anyone, will be hired?" +/// - "How effective will the grant committee deem this grant?" +/// +/// Questions have 2 or more possible outcomes. For a question like "will this +/// proposal pass," the outcomes are "yes" and "no." For a question like "who +/// will be hired," the outcomes could be "Alice," "Bob," and "neither." +/// +/// Outcomes resolve to a number between 0 and 1. Binary questions like "will +/// this proposal pass" have outcomes that resolve to exactly 0 or 1. You can +/// also have questions with scalar outcomes. For example, the question "how +/// effective will the grant committee deem this grant" could have two outcomes: +/// "ineffective" and "effective." If the grant committee deems the grant 70% +/// effective, the "effective" outcome would resolve to 0.7 and the "ineffective" +/// outcome would resolve to 0.3. +/// +/// Once resolved, the sum of all outcome resolutions is exactly 1. +#[account] +pub struct Question { + pub question_id: [u8; 32], + pub oracle: Pubkey, + pub payout_numerators: Vec, + pub payout_denominator: u32, +} + +impl Question { + pub fn num_outcomes(&self) -> usize { + self.payout_numerators.len() + } + + pub fn is_resolved(&self) -> bool { + self.payout_denominator != 0 + } +} \ No newline at end of file diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index afa70972..589e80b2 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -531,56 +531,6 @@ export type ConditionalVault = { } ]; accounts: [ - { - name: "question"; - docs: [ - "Questions represent statements about future events.", - "", - "These statements include:", - '- "Will this proposal pass?"', - '- "Who, if anyone, will be hired?"', - '- "How effective will the grant committee deem this grant?"', - "", - 'Questions have 2 or more possible outcomes. For a question like "will this', - 'proposal pass," the outcomes are "yes" and "no." For a question like "who', - 'will be hired," the outcomes could be "Alice," "Bob," and "neither."', - "", - 'Outcomes resolve to a number between 0 and 1. Binary questions like "will', - 'this proposal pass" have outcomes that resolve to exactly 0 or 1. You can', - 'also have questions with scalar outcomes. For example, the question "how', - 'effective will the grant committee deem this grant" could have two outcomes:', - '"ineffective" and "effective." If the grant committee deems the grant 70%', - 'effective, the "effective" outcome would resolve to 0.7 and the "ineffective"', - "outcome would resolve to 0.3.", - "", - "Once resolved, the sum of all outcome resolutions is exactly 1." - ]; - type: { - kind: "struct"; - fields: [ - { - name: "questionId"; - type: { - array: ["u8", 32]; - }; - }, - { - name: "oracle"; - type: "publicKey"; - }, - { - name: "payoutNumerators"; - type: { - vec: "u32"; - }; - }, - { - name: "payoutDenominator"; - type: "u32"; - } - ]; - }; - }, { name: "newConditionalVault"; type: { @@ -663,6 +613,56 @@ export type ConditionalVault = { } ]; }; + }, + { + name: "question"; + docs: [ + "Questions represent statements about future events.", + "", + "These statements include:", + '- "Will this proposal pass?"', + '- "Who, if anyone, will be hired?"', + '- "How effective will the grant committee deem this grant?"', + "", + 'Questions have 2 or more possible outcomes. For a question like "will this', + 'proposal pass," the outcomes are "yes" and "no." For a question like "who', + 'will be hired," the outcomes could be "Alice," "Bob," and "neither."', + "", + 'Outcomes resolve to a number between 0 and 1. Binary questions like "will', + 'this proposal pass" have outcomes that resolve to exactly 0 or 1. You can', + 'also have questions with scalar outcomes. For example, the question "how', + 'effective will the grant committee deem this grant" could have two outcomes:', + '"ineffective" and "effective." If the grant committee deems the grant 70%', + 'effective, the "effective" outcome would resolve to 0.7 and the "ineffective"', + "outcome would resolve to 0.3.", + "", + "Once resolved, the sum of all outcome resolutions is exactly 1." + ]; + type: { + kind: "struct"; + fields: [ + { + name: "questionId"; + type: { + array: ["u8", 32]; + }; + }, + { + name: "oracle"; + type: "publicKey"; + }, + { + name: "payoutNumerators"; + type: { + vec: "u32"; + }; + }, + { + name: "payoutDenominator"; + type: "u32"; + } + ]; + }; } ]; types: [ @@ -1344,56 +1344,6 @@ export const IDL: ConditionalVault = { }, ], accounts: [ - { - name: "question", - docs: [ - "Questions represent statements about future events.", - "", - "These statements include:", - '- "Will this proposal pass?"', - '- "Who, if anyone, will be hired?"', - '- "How effective will the grant committee deem this grant?"', - "", - 'Questions have 2 or more possible outcomes. For a question like "will this', - 'proposal pass," the outcomes are "yes" and "no." For a question like "who', - 'will be hired," the outcomes could be "Alice," "Bob," and "neither."', - "", - 'Outcomes resolve to a number between 0 and 1. Binary questions like "will', - 'this proposal pass" have outcomes that resolve to exactly 0 or 1. You can', - 'also have questions with scalar outcomes. For example, the question "how', - 'effective will the grant committee deem this grant" could have two outcomes:', - '"ineffective" and "effective." If the grant committee deems the grant 70%', - 'effective, the "effective" outcome would resolve to 0.7 and the "ineffective"', - "outcome would resolve to 0.3.", - "", - "Once resolved, the sum of all outcome resolutions is exactly 1.", - ], - type: { - kind: "struct", - fields: [ - { - name: "questionId", - type: { - array: ["u8", 32], - }, - }, - { - name: "oracle", - type: "publicKey", - }, - { - name: "payoutNumerators", - type: { - vec: "u32", - }, - }, - { - name: "payoutDenominator", - type: "u32", - }, - ], - }, - }, { name: "newConditionalVault", type: { @@ -1477,6 +1427,56 @@ export const IDL: ConditionalVault = { ], }, }, + { + name: "question", + docs: [ + "Questions represent statements about future events.", + "", + "These statements include:", + '- "Will this proposal pass?"', + '- "Who, if anyone, will be hired?"', + '- "How effective will the grant committee deem this grant?"', + "", + 'Questions have 2 or more possible outcomes. For a question like "will this', + 'proposal pass," the outcomes are "yes" and "no." For a question like "who', + 'will be hired," the outcomes could be "Alice," "Bob," and "neither."', + "", + 'Outcomes resolve to a number between 0 and 1. Binary questions like "will', + 'this proposal pass" have outcomes that resolve to exactly 0 or 1. You can', + 'also have questions with scalar outcomes. For example, the question "how', + 'effective will the grant committee deem this grant" could have two outcomes:', + '"ineffective" and "effective." If the grant committee deems the grant 70%', + 'effective, the "effective" outcome would resolve to 0.7 and the "ineffective"', + "outcome would resolve to 0.3.", + "", + "Once resolved, the sum of all outcome resolutions is exactly 1.", + ], + type: { + kind: "struct", + fields: [ + { + name: "questionId", + type: { + array: ["u8", 32], + }, + }, + { + name: "oracle", + type: "publicKey", + }, + { + name: "payoutNumerators", + type: { + vec: "u32", + }, + }, + { + name: "payoutDenominator", + type: "u32", + }, + ], + }, + }, ], types: [ { From da260de2ce12d4727f1fc9488cee1d615dbfa6f9 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Fri, 23 Aug 2024 00:00:00 +0000 Subject: [PATCH 25/52] Add conditional mint checks --- package.json | 6 +- programs/conditional_vault/src/error.rs | 4 + .../src/instructions/new_common.rs | 31 +++-- .../src/instructions/redeem_tokens.rs | 11 -- run.sh | 10 +- sdk/src/ConditionalVaultClient.ts | 84 +++++++------ sdk/src/types/conditional_vault.ts | 28 ++++- .../conditionalVault/unit/splitTokens.test.ts | 112 ++++++++++++++---- yarn.lock | 8 +- 9 files changed, 203 insertions(+), 91 deletions(-) diff --git a/package.json b/package.json index 39b27885..0f6f5293 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ }, "dependencies": { "@coral-xyz/anchor": "0.29.0", + "@metadaoproject/futarchy": "0.3.0-alpha.12", "@metaplex-foundation/mpl-token-metadata": "^3.2.0", "@metaplex-foundation/umi": "^0.9.1", "@metaplex-foundation/umi-bundle-defaults": "^0.9.1", @@ -18,8 +19,7 @@ "anchor-bankrun": "^0.3.0", "arweave": "^1.14.4", "solana-bankrun": "^0.3.0", - "spl-token-bankrun": "0.2.6", - "@metadaoproject/futarchy": "0.3.0-alpha.12" + "spl-token-bankrun": "0.2.6" }, "devDependencies": { "@mercurial-finance/dynamic-amm-sdk": "^0.4.19", @@ -27,7 +27,7 @@ "@solana/spl-token-registry": "^0.2.4574", "@types/bn.js": "^5.1.0", "@types/chai": "^4.3.0", - "@types/mocha": "^9.0.0", + "@types/mocha": "^10.0.7", "@types/node": "^20.8.6", "chai": "^4.3.4", "mocha": "^9.0.3", diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index cc78af9b..38dfe600 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -18,10 +18,14 @@ pub enum VaultError { InvalidNumPayoutNumerators, #[msg("Client needs to pass in the list of conditional mints for a vault followed by the user's token accounts for those tokens")] InvalidConditionals, + #[msg("Conditional mint not in vault")] + ConditionalMintMismatch, #[msg("Unable to deserialize a conditional token mint")] BadConditionalMint, #[msg("Unable to deserialize a conditional token account")] BadConditionalTokenAccount, + #[msg("User conditional token account mint does not match conditional mint")] + ConditionalTokenMintMismatch, #[msg("Payouts must sum to 1 or more")] PayoutZero, } diff --git a/programs/conditional_vault/src/instructions/new_common.rs b/programs/conditional_vault/src/instructions/new_common.rs index 91d2af91..93e37d2d 100644 --- a/programs/conditional_vault/src/instructions/new_common.rs +++ b/programs/conditional_vault/src/instructions/new_common.rs @@ -26,7 +26,7 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { ) -> Result<(Vec>, Vec>)> { msg!("HelloWorld"); let remaining_accs = &mut ctx.remaining_accounts.iter(); - msg!("{:?}", remaining_accs); + // msg!("{:?}", remaining_accs); let expected_num_conditional_tokens = ctx.accounts.question.num_outcomes(); require_eq!( @@ -38,20 +38,31 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { let mut conditional_token_mints = vec![]; let mut user_conditional_token_accounts = vec![]; - for _ in 0..expected_num_conditional_tokens { + for i in 0..expected_num_conditional_tokens { let conditional_token_mint = next_account_info(remaining_accs)?; - conditional_token_mints.push( - Account::::try_from(conditional_token_mint) - .or(Err(VaultError::BadConditionalMint))?, + require_eq!( + ctx.accounts.vault.conditional_token_mints[i], + conditional_token_mint.key(), + VaultError::ConditionalMintMismatch ); + + conditional_token_mints.push(Account::::try_from(conditional_token_mint) + .or(Err(VaultError::BadConditionalMint))?); } - - for _ in 0..expected_num_conditional_tokens { + + for i in 0..expected_num_conditional_tokens { let user_conditional_token_account = next_account_info(remaining_accs)?; - user_conditional_token_accounts.push( - Account::::try_from(user_conditional_token_account) - .or(Err(VaultError::BadConditionalTokenAccount))?, + + let user_conditional_token_account = Account::::try_from(user_conditional_token_account) + .or(Err(VaultError::BadConditionalTokenAccount))?; + + require_eq!( + user_conditional_token_account.mint, + conditional_token_mints[i].key(), + VaultError::ConditionalTokenMintMismatch ); + + user_conditional_token_accounts.push(user_conditional_token_account); } Ok((conditional_token_mints, user_conditional_token_accounts)) diff --git a/programs/conditional_vault/src/instructions/redeem_tokens.rs b/programs/conditional_vault/src/instructions/redeem_tokens.rs index be99a1c8..c2e7542a 100644 --- a/programs/conditional_vault/src/instructions/redeem_tokens.rs +++ b/programs/conditional_vault/src/instructions/redeem_tokens.rs @@ -31,17 +31,6 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { let seeds = generate_new_vault_seeds!(vault); let signer = &[&seeds[..]]; - // burn from both accounts even though we technically only need to burn from one - // for (conditional_mint, user_conditional_token_account) in [ - // ( - // &accs.conditional_on_finalize_token_mint, - // &accs.user_conditional_on_finalize_token_account, - // ), - // ( - // &accs.conditional_on_revert_token_mint, - // &accs.user_conditional_on_revert_token_account, - // ), - // ] { for (conditional_mint, user_conditional_token_account) in conditional_token_mints .iter() .zip(user_conditional_token_accounts.iter()) diff --git a/run.sh b/run.sh index 62bcbb25..72506cc5 100755 --- a/run.sh +++ b/run.sh @@ -1,5 +1,9 @@ #!/bin/sh +build() { + find programs | entr -sc 'anchor build' +} + test() { find programs tests sdk | entr -sc '(cd sdk && yarn build) && RUST_LOG= anchor test' } @@ -13,8 +17,9 @@ test_vault() { # modify the Anchor.toml and then put it back #sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/conditionalVault.ts"/' Anchor.toml #sleep 10 && sed -i '2s/\(\(\S\+\s\+\)\{9\}\)\S\+/\1tests\/*.ts"/' Anchor.toml & - find programs tests sdk | entr -sc \ - "anchor build -p conditional_vault && (cd sdk && yarn build) && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/conditionalVault\/*.ts\"/' Anchor.toml && (sleep 3 && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/\**\/*.ts\"/' Anchor.toml) & RUST_LOG= anchor test --skip-build" + # find programs tests sdk | entr -sc \ + # "anchor build -p conditional_vault && (cd sdk && yarn build) && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/conditionalVault\/*.ts\"/' Anchor.toml && (sleep 3 && sed -i '2s/\(\(\S\+\s\+\)\{10\}\)\S\+/\1tests\/\**\/*.ts\"/' Anchor.toml) & RUST_LOG= anchor test --skip-build" + find programs tests sdk | entr -sc 'anchor build -p conditional_vault && (cd sdk && yarn build) && RUST_LOG= anchor test --skip-build' } test_no_build() { @@ -77,6 +82,7 @@ bankrun_logs() { } case "$1" in + build) build ;; test) test ;; vault) test_vault ;; build_vault) build_vault ;; diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index aead4ae4..525e35c3 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -237,6 +237,50 @@ export class ConditionalVaultClient { return conditionalTokenMintAddrs; } + getRemainingAccounts( + conditionalTokenMints: PublicKey[], + userConditionalAccounts: PublicKey[] + ) { + return conditionalTokenMints + .concat(userConditionalAccounts) + .map((account) => ({ + pubkey: account, + isWritable: true, + isSigner: false, + })); + } + + // Helper method to get conditional token accounts and instructions + getConditionalTokenAccountsAndInstructions( + vault: PublicKey, + numOutcomes: number, + user: PublicKey + ) { + const conditionalTokenMintAddrs = this.getConditionalTokenMints( + vault, + numOutcomes + ); + const userConditionalAccounts = conditionalTokenMintAddrs.map((mint) => + getAssociatedTokenAddressSync(mint, user, true) + ); + + const preInstructions = conditionalTokenMintAddrs.map((mint) => + createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + getAssociatedTokenAddressSync(mint, user), + user, + mint + ) + ); + + const remainingAccounts = this.getRemainingAccounts( + conditionalTokenMintAddrs, + userConditionalAccounts + ); + + return { userConditionalAccounts, preInstructions, remainingAccounts }; + } + splitTokensIx( question: PublicKey, vault: PublicKey, @@ -245,19 +289,10 @@ export class ConditionalVaultClient { numOutcomes: number, user: PublicKey = this.provider.publicKey ) { - let conditionalTokenMintAddrs = this.getConditionalTokenMints( - vault, - numOutcomes - ); - - let userConditionalAccounts = []; - for (let conditionalTokenMint of conditionalTokenMintAddrs) { - userConditionalAccounts.push( - getAssociatedTokenAddressSync(conditionalTokenMint, user, true) - ); - } + const { preInstructions, remainingAccounts } = + this.getConditionalTokenAccountsAndInstructions(vault, numOutcomes, user); - let ix = this.vaultProgram.methods + return this.vaultProgram.methods .splitTokens(amount) .accounts({ question, @@ -274,29 +309,8 @@ export class ConditionalVaultClient { true ), }) - .preInstructions( - conditionalTokenMintAddrs.map((conditionalTokenMint) => { - return createAssociatedTokenAccountIdempotentInstruction( - this.provider.publicKey, - getAssociatedTokenAddressSync(conditionalTokenMint, user), - user, - conditionalTokenMint - ); - }) - ) - .remainingAccounts( - conditionalTokenMintAddrs - .concat(userConditionalAccounts) - .map((conditionalTokenMint) => { - return { - pubkey: conditionalTokenMint, - isWritable: true, - isSigner: false, - }; - }) - ); - - return ix; + .preInstructions(preInstructions) + .remainingAccounts(remainingAccounts); } mergeTokensIx( diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index 589e80b2..e1aa091d 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -795,16 +795,26 @@ export type ConditionalVault = { }, { code: 6008; + name: "ConditionalMintMismatch"; + msg: "Conditional mint not in vault"; + }, + { + code: 6009; name: "BadConditionalMint"; msg: "Unable to deserialize a conditional token mint"; }, { - code: 6009; + code: 6010; name: "BadConditionalTokenAccount"; msg: "Unable to deserialize a conditional token account"; }, { - code: 6010; + code: 6011; + name: "ConditionalTokenMintMismatch"; + msg: "User conditional token account mint does not match conditional mint"; + }, + { + code: 6012; name: "PayoutZero"; msg: "Payouts must sum to 1 or more"; } @@ -1608,16 +1618,26 @@ export const IDL: ConditionalVault = { }, { code: 6008, + name: "ConditionalMintMismatch", + msg: "Conditional mint not in vault", + }, + { + code: 6009, name: "BadConditionalMint", msg: "Unable to deserialize a conditional token mint", }, { - code: 6009, + code: 6010, name: "BadConditionalTokenAccount", msg: "Unable to deserialize a conditional token account", }, { - code: 6010, + code: 6011, + name: "ConditionalTokenMintMismatch", + msg: "User conditional token account mint does not match conditional mint", + }, + { + code: 6012, name: "PayoutZero", msg: "Payouts must sum to 1 or more", }, diff --git a/tests/conditionalVault/unit/splitTokens.test.ts b/tests/conditionalVault/unit/splitTokens.test.ts index 6b85cb4c..43deb5d5 100644 --- a/tests/conditionalVault/unit/splitTokens.test.ts +++ b/tests/conditionalVault/unit/splitTokens.test.ts @@ -4,6 +4,7 @@ import { assert } from "chai"; import { createAssociatedTokenAccount, createMint, getAccount, getMint, mintTo } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; +import { expectError } from "../../utils"; export default function suite() { let vaultClient: ConditionalVaultClient; @@ -25,11 +26,8 @@ export default function suite() { 2 ); - underlyingTokenMint = await createMint( - this.banksClient, - this.payer, + underlyingTokenMint = await this.createMint( this.payer.publicKey, - null, 8 ); @@ -39,18 +37,14 @@ export default function suite() { 2 ); - let userUnderlyingTokenAccount = await createAssociatedTokenAccount( - this.banksClient, - this.payer, + await this.createTokenAccount( underlyingTokenMint, this.payer.publicKey ); - await mintTo( - this.banksClient, - this.payer, + await this.mintTo( underlyingTokenMint, - userUnderlyingTokenAccount, + this.payer.publicKey, this.payer, 10_000_000_000n ); @@ -63,11 +57,7 @@ export default function suite() { const storedVault = await vaultClient.fetchVault(vault); - let storedVaultUnderlyingAcc = await getAccount( - this.banksClient, - storedVault.underlyingTokenAccount - ); - assert.equal(storedVaultUnderlyingAcc.amount.toString(), "1000"); + this.assertBalance(underlyingTokenMint, vault, 1000); const storedConditionalTokenMints = storedVault.conditionalTokenMints; for (let mint of storedConditionalTokenMints) { @@ -76,12 +66,90 @@ export default function suite() { mint ); assert.equal(storedMint.supply.toString(), "1000"); - let storedTokenAcc = await getAccount( - this.banksClient, - token.getAssociatedTokenAddressSync(mint, this.payer.publicKey) - // await createAssociatedTokenAccount(this.banksClient, this.payer, mint, this.payer.publicKey) - ); - assert.equal(storedTokenAcc.amount.toString(), "1000"); + await this.assertBalance(mint, this.payer.publicKey, 1000); } }); + + it("throws error if conditional token accounts don't exist", async function () { + let { remainingAccounts } = vaultClient.getConditionalTokenAccountsAndInstructions( + vault, + 2, + this.payer.publicKey + ); + + const callbacks = expectError( + "BadConditionalTokenAccount", + "split succeeded despite conditional token account not existing" + ); + + await vaultClient.vaultProgram.methods + .splitTokens(new anchor.BN(1000)) + .accounts({ + question, + authority: this.payer.publicKey, + vault, + vaultUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + this.payer.publicKey, + true + ), + }) + .remainingAccounts(remainingAccounts) + .rpc() + .then(callbacks[0], callbacks[1]); + }); + + it("throws error if the conditional mints are wrong", async function () { + const callbacks = expectError( + "ConditionalMintMismatch", + "split succeeded despite conditional mint not being in vault" + ); + + const fakeConditionalMint1 = await this.createMint( + vault, + 8 + ); + + const fakeConditionalMint2 = await this.createMint( + vault, + 8 + ); + + const fakeConditionalTokenAccount1 = await this.createTokenAccount( + fakeConditionalMint1, + this.payer.publicKey + ); + + const fakeConditionalTokenAccount2 = await this.createTokenAccount( + fakeConditionalMint2, + this.payer.publicKey + ); + + // Attempt to split tokens using the original vault but with the malicious vault's conditional token accounts + await vaultClient.vaultProgram.methods + .splitTokens(new anchor.BN(1000)) + .accounts({ + question, + authority: this.payer.publicKey, + vault, + vaultUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + this.payer.publicKey, + true + ), + }) + .remainingAccounts(vaultClient.getRemainingAccounts([fakeConditionalMint1, fakeConditionalMint2], [fakeConditionalTokenAccount1, fakeConditionalTokenAccount2])) + .rpc() + .then(callbacks[0], callbacks[1]); + }); } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index c75a80bf..5662b6f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1989,10 +1989,10 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== -"@types/mocha@^9.0.0": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" - integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== +"@types/mocha@^10.0.7": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f" + integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw== "@types/node@*", "@types/node@>=13.7.0", "@types/node@^20.8.6": version "20.11.25" From 94112f46cc029e05e62b4b18c4fff67d4ca07e2d Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 26/52] Add back invariant checks to `split_tokens` --- .../src/instructions/split_tokens.rs | 59 +++++++------------ 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/programs/conditional_vault/src/instructions/split_tokens.rs b/programs/conditional_vault/src/instructions/split_tokens.rs index 2033ce6c..031fb56b 100644 --- a/programs/conditional_vault/src/instructions/split_tokens.rs +++ b/programs/conditional_vault/src/instructions/split_tokens.rs @@ -4,16 +4,18 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { pub fn handle_split_tokens(ctx: Context<'_, '_, 'c, 'info, Self>, amount: u64) -> Result<()> { let accs = &ctx.accounts; - let (conditional_token_mints, user_conditional_token_accounts) = + let (mut conditional_token_mints, mut user_conditional_token_accounts) = Self::get_mints_and_user_token_accounts(&ctx)?; let pre_vault_underlying_balance = accs.vault_underlying_token_account.amount; - // let pre_user_conditional_on_finalize_balance = - // accs.user_conditional_on_finalize_token_account.amount; - // let pre_user_conditional_on_revert_balance = - // accs.user_conditional_on_revert_token_account.amount; - // let pre_finalize_mint_supply = accs.conditional_on_finalize_token_mint.supply; - // let pre_revert_mint_supply = accs.conditional_on_revert_token_mint.supply; + let pre_conditional_user_balances = user_conditional_token_accounts + .iter() + .map(|acc| acc.amount) + .collect::>(); + let pre_conditional_mint_supplies = conditional_token_mints + .iter() + .map(|mint| mint.supply) + .collect::>(); require!( accs.user_underlying_token_account.amount >= amount, @@ -55,38 +57,21 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { )?; } - // ctx.accounts - // .user_conditional_on_finalize_token_account - // .reload()?; - // ctx.accounts - // .user_conditional_on_revert_token_account - // .reload()?; - // ctx.accounts.vault_underlying_token_account.reload()?; - // ctx.accounts.conditional_on_finalize_token_mint.reload()?; - // ctx.accounts.conditional_on_revert_token_mint.reload()?; + ctx.accounts.vault_underlying_token_account.reload()?; + assert!( + ctx.accounts.vault_underlying_token_account.amount + == pre_vault_underlying_balance + amount + ); - // let post_user_conditional_on_finalize_balance = ctx - // .accounts - // .user_conditional_on_finalize_token_account - // .amount; - // let post_user_conditional_on_revert_balance = - // ctx.accounts.user_conditional_on_revert_token_account.amount; - // let post_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; - // let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; - // let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; + for (i, mint) in conditional_token_mints.iter_mut().enumerate() { + mint.reload()?; + assert!(mint.supply == pre_conditional_mint_supplies[i] + amount); + } - // // Only the paranoid survive ;) - // assert!(post_vault_underlying_balance == pre_vault_underlying_balance + amount); - // assert!( - // post_user_conditional_on_finalize_balance - // == pre_user_conditional_on_finalize_balance + amount - // ); - // assert!( - // post_user_conditional_on_revert_balance - // == pre_user_conditional_on_revert_balance + amount - // ); - // assert!(post_finalize_mint_supply == pre_finalize_mint_supply + amount); - // assert!(post_revert_mint_supply == pre_revert_mint_supply + amount); + for (i, acc) in user_conditional_token_accounts.iter_mut().enumerate() { + acc.reload()?; + assert!(acc.amount == pre_conditional_user_balances[i] + amount); + } Ok(()) } From 4d8c36e4c5c5d6c805d7bc8da578babbe2289a24 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 27/52] Add invariants to `redeem_tokens` --- programs/conditional_vault/src/error.rs | 2 +- .../src/instructions/redeem_tokens.rs | 133 ++++++++---------- sdk/src/types/conditional_vault.ts | 4 +- 3 files changed, 62 insertions(+), 77 deletions(-) diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index 38dfe600..a43cd613 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -8,7 +8,7 @@ pub enum VaultError { InvalidVaultUnderlyingTokenAccount, #[msg("This conditional token mint is not this vault's conditional token mint")] InvalidConditionalTokenMint, - #[msg("Vault needs to be settled as finalized before users can redeem conditional tokens for underlying tokens")] + #[msg("Question needs to be resolved before users can redeem conditional tokens for underlying tokens")] CantRedeemConditionalTokens, #[msg("Once a vault has been settled, its status as either finalized or reverted cannot be changed")] VaultAlreadySettled, diff --git a/programs/conditional_vault/src/instructions/redeem_tokens.rs b/programs/conditional_vault/src/instructions/redeem_tokens.rs index c2e7542a..cea47180 100644 --- a/programs/conditional_vault/src/instructions/redeem_tokens.rs +++ b/programs/conditional_vault/src/instructions/redeem_tokens.rs @@ -13,24 +13,35 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { pub fn handle_redeem_tokens(ctx: Context<'_, '_, 'c, 'info, Self>) -> Result<()> { let accs = &ctx.accounts; - let (conditional_token_mints, user_conditional_token_accounts) = + let (mut conditional_token_mints, mut user_conditional_token_accounts) = Self::get_mints_and_user_token_accounts(&ctx)?; + // calculate the expected future supplies of the conditional token mints + // as current supply - user balance + let expected_future_supplies: Vec = conditional_token_mints + .iter() + .zip(user_conditional_token_accounts.iter()) + .map(|(mint, account)| mint.supply - account.amount) + .collect(); + let vault = &accs.vault; let question = &accs.question; - // let pre_vault_underlying_balance = accs.vault_underlying_token_account.amount; - // let pre_finalize_mint_supply = accs.conditional_on_finalize_token_mint.supply; - // let pre_revert_mint_supply = accs.conditional_on_revert_token_mint.supply; - - // let pre_conditional_on_finalize_balance = - // accs.user_conditional_on_finalize_token_account.amount; - // let pre_conditional_on_revert_balance = - // accs.user_conditional_on_revert_token_account.amount; - let seeds = generate_new_vault_seeds!(vault); let signer = &[&seeds[..]]; + let user_underlying_balance_before = accs.user_underlying_token_account.amount; + let vault_underlying_balance_before = accs.vault_underlying_token_account.amount; + // safe because there is always at least two conditional tokens and thus + // at least two user conditional token accounts + let max_redeemable = user_conditional_token_accounts + .iter() + .map(|account| account.amount) + .max() + .unwrap(); + + let mut total_redeemable = 0; + for (conditional_mint, user_conditional_token_account) in conditional_token_mints .iter() .zip(user_conditional_token_accounts.iter()) @@ -42,9 +53,10 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { .position(|mint| mint == &conditional_mint.key()) .unwrap(); - let redeemable = ((user_conditional_token_account.amount as u128 + total_redeemable += ((user_conditional_token_account.amount as u128 * question.payout_numerators[payout_index] as u128) / question.payout_denominator as u128) as u64; + token::burn( CpiContext::new( accs.token_program.to_account_info(), @@ -56,72 +68,45 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { ), user_conditional_token_account.amount, )?; + } - token::transfer( - CpiContext::new_with_signer( - accs.token_program.to_account_info(), - Transfer { - from: accs.vault_underlying_token_account.to_account_info(), - to: accs.user_underlying_token_account.to_account_info(), - authority: accs.vault.to_account_info(), - }, - signer, - ), - redeemable, - )?; + token::transfer( + CpiContext::new_with_signer( + accs.token_program.to_account_info(), + Transfer { + from: accs.vault_underlying_token_account.to_account_info(), + to: accs.user_underlying_token_account.to_account_info(), + authority: accs.vault.to_account_info(), + }, + signer, + ), + total_redeemable, + )?; + + assert!(max_redeemable >= total_redeemable); + + ctx.accounts.user_underlying_token_account.reload()?; + ctx.accounts.vault_underlying_token_account.reload()?; + + assert!( + ctx.accounts.user_underlying_token_account.amount + == user_underlying_balance_before + total_redeemable + ); + + assert!( + ctx.accounts.vault_underlying_token_account.amount + == vault_underlying_balance_before - total_redeemable + ); + + for acc in user_conditional_token_accounts.iter_mut() { + acc.reload()?; + assert!(acc.amount == 0); } - // let redeemable = match vault_status { - // VaultStatus::Finalized => pre_conditional_on_finalize_balance, - // VaultStatus::Reverted => pre_conditional_on_revert_balance, - // _ => unreachable!(), - // }; - - // ctx.accounts - // .user_conditional_on_finalize_token_account - // .reload()?; - // ctx.accounts - // .user_conditional_on_revert_token_account - // .reload()?; - // ctx.accounts.vault_underlying_token_account.reload()?; - // ctx.accounts.conditional_on_finalize_token_mint.reload()?; - // ctx.accounts.conditional_on_revert_token_mint.reload()?; - - // let post_user_conditional_on_finalize_balance = ctx - // .accounts - // .user_conditional_on_finalize_token_account - // .amount; - // let post_user_conditional_on_revert_balance = - // ctx.accounts.user_conditional_on_revert_token_account.amount; - // let post_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; - // let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; - // let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; - - // assert!(post_user_conditional_on_finalize_balance == 0); - // assert!(post_user_conditional_on_revert_balance == 0); - // assert!( - // post_finalize_mint_supply - // == pre_finalize_mint_supply - pre_conditional_on_finalize_balance - // ); - // assert!( - // post_revert_mint_supply == pre_revert_mint_supply - pre_conditional_on_revert_balance - // ); - // match vault_status { - // VaultStatus::Finalized => { - // assert!( - // post_vault_underlying_balance - // == pre_vault_underlying_balance - pre_conditional_on_finalize_balance - // ); - // } - // VaultStatus::Reverted => { - // assert!(vault_status == VaultStatus::Reverted); - // assert!( - // post_vault_underlying_balance - // == pre_vault_underlying_balance - pre_conditional_on_revert_balance - // ); - // } - // _ => unreachable!(), - // } + for (mint, expected_supply) in conditional_token_mints.iter_mut().zip(expected_future_supplies.iter()) { + mint.reload()?; + assert!(mint.supply == *expected_supply); + } Ok(()) } diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index e1aa091d..c499f298 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -771,7 +771,7 @@ export type ConditionalVault = { { code: 6003; name: "CantRedeemConditionalTokens"; - msg: "Vault needs to be settled as finalized before users can redeem conditional tokens for underlying tokens"; + msg: "Question needs to be resolved before users can redeem conditional tokens for underlying tokens"; }, { code: 6004; @@ -1594,7 +1594,7 @@ export const IDL: ConditionalVault = { { code: 6003, name: "CantRedeemConditionalTokens", - msg: "Vault needs to be settled as finalized before users can redeem conditional tokens for underlying tokens", + msg: "Question needs to be resolved before users can redeem conditional tokens for underlying tokens", }, { code: 6004, From 63a53dd4d5ac5abcb2125804b51077f799c4edab Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 28/52] Add asserts to `merge_tokens` --- programs/conditional_vault/src/error.rs | 2 + .../src/instructions/merge_tokens.rs | 103 +++++++----------- sdk/src/types/conditional_vault.ts | 54 +++++---- 3 files changed, 76 insertions(+), 83 deletions(-) diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index a43cd613..bf0d04c7 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -4,6 +4,8 @@ use super::*; pub enum VaultError { #[msg("Insufficient underlying token balance to mint this amount of conditional tokens")] InsufficientUnderlyingTokens, + #[msg("Insufficient conditional token balance to merge this `amount`")] + InsufficientConditionalTokens, #[msg("This `vault_underlying_token_account` is not this vault's `underlying_token_account`")] InvalidVaultUnderlyingTokenAccount, #[msg("This conditional token mint is not this vault's conditional token mint")] diff --git a/programs/conditional_vault/src/instructions/merge_tokens.rs b/programs/conditional_vault/src/instructions/merge_tokens.rs index 65fe210a..4d4ef645 100644 --- a/programs/conditional_vault/src/instructions/merge_tokens.rs +++ b/programs/conditional_vault/src/instructions/merge_tokens.rs @@ -4,37 +4,33 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { pub fn handle_merge_tokens(ctx: Context<'_, '_, 'c, 'info, Self>, amount: u64) -> Result<()> { let accs = &ctx.accounts; - let (conditional_token_mints, user_conditional_token_accounts) = + let (mut conditional_token_mints, mut user_conditional_token_accounts) = Self::get_mints_and_user_token_accounts(&ctx)?; + for conditional_token_account in user_conditional_token_accounts.iter() { + require!( + conditional_token_account.amount >= amount, + VaultError::InsufficientConditionalTokens + ); + } let vault = &accs.vault; - // Store Pre-operation Balances - // let pre_user_conditional_on_finalize_balance = ctx - // .accounts - // .user_conditional_on_finalize_token_account - // .amount; - // let pre_user_conditional_on_revert_balance = - // ctx.accounts.user_conditional_on_revert_token_account.amount; - // let pre_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; - // let pre_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; - // let pre_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; + let pre_user_underlying_balance = accs.user_underlying_token_account.amount; + let pre_vault_underlying_balance = accs.vault_underlying_token_account.amount; + + let expected_future_balances: Vec = user_conditional_token_accounts + .iter() + .map(|account| account.amount - amount) + .collect(); + let expected_future_supplies: Vec = conditional_token_mints + .iter() + .map(|mint| mint.supply - amount) + .collect(); let seeds = generate_new_vault_seeds!(vault); let signer = &[&seeds[..]]; - // burn `amount` from both token accounts - // for (conditional_mint, user_conditional_token_account) in [ - // ( - // &accs.conditional_on_finalize_token_mint, - // &accs.user_conditional_on_finalize_token_account, - // ), - // ( - // &accs.conditional_on_revert_token_mint, - // &accs.user_conditional_on_revert_token_account, - // ), - // ] { for (conditional_mint, user_conditional_token_account) in conditional_token_mints .iter() .zip(user_conditional_token_accounts.iter()) @@ -66,48 +62,33 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { amount, )?; - // Reload Accounts to Reflect Changes - // ctx.accounts - // .user_conditional_on_finalize_token_account - // .reload()?; - // ctx.accounts - // .user_conditional_on_revert_token_account - // .reload()?; - // ctx.accounts.vault_underlying_token_account.reload()?; - // ctx.accounts.conditional_on_finalize_token_mint.reload()?; - // ctx.accounts.conditional_on_revert_token_mint.reload()?; - - // // Check post-operation balances - // let post_user_conditional_on_finalize_balance = ctx - // .accounts - // .user_conditional_on_finalize_token_account - // .amount; - // let post_user_conditional_on_revert_balance = - // ctx.accounts.user_conditional_on_revert_token_account.amount; - // let post_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; - // let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; - // let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; + ctx.accounts.user_underlying_token_account.reload()?; + ctx.accounts.vault_underlying_token_account.reload()?; - // Check that the user's conditional token balances are unchanged (since we're not necessarily burning all tokens) - // require_eq!( - // post_user_conditional_on_finalize_balance, - // pre_user_conditional_on_finalize_balance - amount - // ); - // require_eq!( - // post_user_conditional_on_revert_balance, - // pre_user_conditional_on_revert_balance - amount - // ); + assert!( + ctx.accounts.user_underlying_token_account.amount + == pre_user_underlying_balance + amount + ); + assert!( + ctx.accounts.vault_underlying_token_account.amount + == pre_vault_underlying_balance - amount + ); - // // Check that the mint supplies have been reduced by the burned amounts - // require_eq!(post_finalize_mint_supply, pre_finalize_mint_supply - amount); - // require_eq!(post_revert_mint_supply, pre_revert_mint_supply - amount); - - // // Check that the vault's underlying balance has been reduced by the transferred amount - // require_eq!( - // post_vault_underlying_balance, - // pre_vault_underlying_balance - amount - // ); + for (mint, expected_supply) in conditional_token_mints + .iter_mut() + .zip(expected_future_supplies.iter()) + { + mint.reload()?; + assert!(mint.supply == *expected_supply); + } + for (account, expected_balance) in user_conditional_token_accounts + .iter_mut() + .zip(expected_future_balances.iter()) + { + account.reload()?; + assert!(account.amount == *expected_balance); + } Ok(()) } diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index c499f298..ad5c53d9 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -760,61 +760,66 @@ export type ConditionalVault = { }, { code: 6001; + name: "InsufficientConditionalTokens"; + msg: "Insufficient conditional token balance to merge this `amount`"; + }, + { + code: 6002; name: "InvalidVaultUnderlyingTokenAccount"; msg: "This `vault_underlying_token_account` is not this vault's `underlying_token_account`"; }, { - code: 6002; + code: 6003; name: "InvalidConditionalTokenMint"; msg: "This conditional token mint is not this vault's conditional token mint"; }, { - code: 6003; + code: 6004; name: "CantRedeemConditionalTokens"; msg: "Question needs to be resolved before users can redeem conditional tokens for underlying tokens"; }, { - code: 6004; + code: 6005; name: "VaultAlreadySettled"; msg: "Once a vault has been settled, its status as either finalized or reverted cannot be changed"; }, { - code: 6005; + code: 6006; name: "InsufficientNumConditions"; msg: "Questions need 2 or more conditions"; }, { - code: 6006; + code: 6007; name: "InvalidNumPayoutNumerators"; msg: "Invalid number of payout numerators"; }, { - code: 6007; + code: 6008; name: "InvalidConditionals"; msg: "Client needs to pass in the list of conditional mints for a vault followed by the user's token accounts for those tokens"; }, { - code: 6008; + code: 6009; name: "ConditionalMintMismatch"; msg: "Conditional mint not in vault"; }, { - code: 6009; + code: 6010; name: "BadConditionalMint"; msg: "Unable to deserialize a conditional token mint"; }, { - code: 6010; + code: 6011; name: "BadConditionalTokenAccount"; msg: "Unable to deserialize a conditional token account"; }, { - code: 6011; + code: 6012; name: "ConditionalTokenMintMismatch"; msg: "User conditional token account mint does not match conditional mint"; }, { - code: 6012; + code: 6013; name: "PayoutZero"; msg: "Payouts must sum to 1 or more"; } @@ -1583,61 +1588,66 @@ export const IDL: ConditionalVault = { }, { code: 6001, + name: "InsufficientConditionalTokens", + msg: "Insufficient conditional token balance to merge this `amount`", + }, + { + code: 6002, name: "InvalidVaultUnderlyingTokenAccount", msg: "This `vault_underlying_token_account` is not this vault's `underlying_token_account`", }, { - code: 6002, + code: 6003, name: "InvalidConditionalTokenMint", msg: "This conditional token mint is not this vault's conditional token mint", }, { - code: 6003, + code: 6004, name: "CantRedeemConditionalTokens", msg: "Question needs to be resolved before users can redeem conditional tokens for underlying tokens", }, { - code: 6004, + code: 6005, name: "VaultAlreadySettled", msg: "Once a vault has been settled, its status as either finalized or reverted cannot be changed", }, { - code: 6005, + code: 6006, name: "InsufficientNumConditions", msg: "Questions need 2 or more conditions", }, { - code: 6006, + code: 6007, name: "InvalidNumPayoutNumerators", msg: "Invalid number of payout numerators", }, { - code: 6007, + code: 6008, name: "InvalidConditionals", msg: "Client needs to pass in the list of conditional mints for a vault followed by the user's token accounts for those tokens", }, { - code: 6008, + code: 6009, name: "ConditionalMintMismatch", msg: "Conditional mint not in vault", }, { - code: 6009, + code: 6010, name: "BadConditionalMint", msg: "Unable to deserialize a conditional token mint", }, { - code: 6010, + code: 6011, name: "BadConditionalTokenAccount", msg: "Unable to deserialize a conditional token account", }, { - code: 6011, + code: 6012, name: "ConditionalTokenMintMismatch", msg: "User conditional token account mint does not match conditional mint", }, { - code: 6012, + code: 6013, name: "PayoutZero", msg: "Payouts must sum to 1 or more", }, From c6d97077389304f78162f65c2446d8c0f3f8fa58 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 29/52] Add an assets >= liabilities invariant --- .../src/instructions/merge_tokens.rs | 6 +++ .../src/instructions/redeem_tokens.rs | 6 +++ .../src/instructions/split_tokens.rs | 6 +++ .../src/state/conditional_vault.rs | 41 ++++++++++++++++++- 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/programs/conditional_vault/src/instructions/merge_tokens.rs b/programs/conditional_vault/src/instructions/merge_tokens.rs index 4d4ef645..14b920e5 100644 --- a/programs/conditional_vault/src/instructions/merge_tokens.rs +++ b/programs/conditional_vault/src/instructions/merge_tokens.rs @@ -90,6 +90,12 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { assert!(account.amount == *expected_balance); } + ctx.accounts.vault.invariant( + &ctx.accounts.question, + conditional_token_mints.iter().map(|mint| mint.supply).collect::>(), + ctx.accounts.vault_underlying_token_account.amount, + )?; + Ok(()) } } diff --git a/programs/conditional_vault/src/instructions/redeem_tokens.rs b/programs/conditional_vault/src/instructions/redeem_tokens.rs index cea47180..96b71240 100644 --- a/programs/conditional_vault/src/instructions/redeem_tokens.rs +++ b/programs/conditional_vault/src/instructions/redeem_tokens.rs @@ -108,6 +108,12 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { assert!(mint.supply == *expected_supply); } + ctx.accounts.vault.invariant( + &ctx.accounts.question, + conditional_token_mints.iter().map(|mint| mint.supply).collect::>(), + ctx.accounts.vault_underlying_token_account.amount, + )?; + Ok(()) } } diff --git a/programs/conditional_vault/src/instructions/split_tokens.rs b/programs/conditional_vault/src/instructions/split_tokens.rs index 031fb56b..3f39a1d6 100644 --- a/programs/conditional_vault/src/instructions/split_tokens.rs +++ b/programs/conditional_vault/src/instructions/split_tokens.rs @@ -73,6 +73,12 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { assert!(acc.amount == pre_conditional_user_balances[i] + amount); } + ctx.accounts.vault.invariant( + &ctx.accounts.question, + conditional_token_mints.iter().map(|mint| mint.supply).collect::>(), + ctx.accounts.vault_underlying_token_account.amount, + )?; + Ok(()) } } diff --git a/programs/conditional_vault/src/state/conditional_vault.rs b/programs/conditional_vault/src/state/conditional_vault.rs index 34fe9872..171cd07a 100644 --- a/programs/conditional_vault/src/state/conditional_vault.rs +++ b/programs/conditional_vault/src/state/conditional_vault.rs @@ -17,6 +17,45 @@ pub struct NewConditionalVault { pub decimals: u8, } +impl NewConditionalVault { + /// Checks that the vault's assets are always greater than its potential + /// liabilities. Should be called anytime you mint or burn conditional + /// tokens. + /// + /// `conditional_token_supplies` should be in the same order as + /// `vault.conditional_token_mints`. + pub fn invariant( + &self, + question: &Question, + conditional_token_supplies: Vec, + vault_underlying_balance: u64, + ) -> Result<()> { + // if the question isn't resolved, the vault should have more underlying + // tokens than ANY conditional token mint's supply + + // if the question is resolved, the vault should have more underlying + // tokens than the sum of the conditional token mint's supplies multiplied + // by their respective payouts + + let max_possible_liability = if !question.is_resolved() { + // safe because conditional_token_supplies is non-empty + *conditional_token_supplies.iter().max().unwrap() + } else { + conditional_token_supplies + .iter() + .enumerate() + .map(|(i, supply)| { + *supply * question.payout_numerators[i] as u64 / question.payout_denominator as u64 + }) + .sum::() + }; + + assert!(vault_underlying_balance >= max_possible_liability); + + Ok(()) + } +} + #[macro_export] macro_rules! generate_new_vault_seeds { ($vault:expr) => {{ @@ -56,4 +95,4 @@ macro_rules! generate_vault_seeds { &[$vault.pda_bump], ] }}; -} +} \ No newline at end of file From c70b2dc23e8a1871c4c9f6c27e1b1f10337c3e53 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 30/52] Add amm unit tests --- tests/amm/amm.ts | 4 +- tests/amm/integration/ammLifecycle.test.ts | 68 ++++++ tests/amm/main.test.ts | 13 ++ tests/amm/unit/addLiquidity.test.ts | 79 +++++++ tests/amm/unit/initializeAmm.test.ts | 81 +++++++ tests/amm/unit/removeLiquidity.test.ts | 130 +++++++++++ tests/amm/unit/swap.test.ts | 248 +++++++++++++++++++++ tests/autocrat/{unit => }/autocrat.ts | 0 tests/autocrat/main.test.ts | 8 - tests/main.test.ts | 17 +- 10 files changed, 632 insertions(+), 16 deletions(-) create mode 100644 tests/amm/integration/ammLifecycle.test.ts create mode 100644 tests/amm/main.test.ts create mode 100644 tests/amm/unit/addLiquidity.test.ts create mode 100644 tests/amm/unit/initializeAmm.test.ts create mode 100644 tests/amm/unit/removeLiquidity.test.ts create mode 100644 tests/amm/unit/swap.test.ts rename tests/autocrat/{unit => }/autocrat.ts (100%) diff --git a/tests/amm/amm.ts b/tests/amm/amm.ts index fb8f12ad..221b068a 100644 --- a/tests/amm/amm.ts +++ b/tests/amm/amm.ts @@ -88,7 +88,7 @@ describe("amm", async function () { META, userMetaAccount, payer.publicKey, - 10_000 * 10 ** 9 + 100 * 10 ** 9 ); mintTo( banksClient, @@ -96,7 +96,7 @@ describe("amm", async function () { USDC, userUsdcAccount, payer.publicKey, - 1_000_000 * 10 ** 6 + 10_000 * 10 ** 6 ); proposal = Keypair.generate().publicKey; diff --git a/tests/amm/integration/ammLifecycle.test.ts b/tests/amm/integration/ammLifecycle.test.ts new file mode 100644 index 00000000..40e1f6f6 --- /dev/null +++ b/tests/amm/integration/ammLifecycle.test.ts @@ -0,0 +1,68 @@ +import { AmmClient, getAmmAddr, getAmmLpMintAddr } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createMint, createAssociatedTokenAccount, mintTo, getAccount, getMint } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import * as token from "@solana/spl-token"; +import { expectError } from "../../utils"; + +export default async function() { + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; + let amm: PublicKey; + + ammClient = this.ammClient; + META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); + USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); + + await this.createTokenAccount(META, this.payer.publicKey); + await this.createTokenAccount(USDC, this.payer.publicKey); + + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); + + let proposal = Keypair.generate().publicKey; + amm = await ammClient.createAmm(proposal, META, USDC, 500); + + // 1. Initialize AMM + const initialAmm = await ammClient.getAmm(amm); + assert.isTrue(initialAmm.baseAmount.eqn(0)); + assert.isTrue(initialAmm.quoteAmount.eqn(0)); + + + + + // 2. Add initial liquidity + await ammClient.addLiquidity(amm, 1000, 2); + const ammAfterInitialLiquidity = await ammClient.getAmm(amm); + assert.isTrue(ammAfterInitialLiquidity.baseAmount.gt(new anchor.BN(0))); + assert.isTrue(ammAfterInitialLiquidity.quoteAmount.gt(new anchor.BN(0))); + + // 3. Perform swaps + await ammClient.swap(amm, { buy: {} }, 100, 0.1); + await ammClient.swap(amm, { sell: {} }, 0.1, 50); + + // 4. Add more liquidity + await ammClient.addLiquidity(amm, 500, 1); + + // 5. Remove some liquidity + let userLpAccount = token.getAssociatedTokenAddressSync( + getAmmLpMintAddr(ammClient.program.programId, amm)[0], + this.payer.publicKey + ); + let userLpBalance = (await getAccount(this.banksClient, userLpAccount)).amount; + await ammClient.removeLiquidityIx(amm, META, USDC, new anchor.BN(Number(userLpBalance) / 2), new anchor.BN(0), new anchor.BN(0)).rpc(); + + // 6. Perform more swaps + await ammClient.swap(amm, { buy: {} }, 200, 0.2); + await ammClient.swap(amm, { sell: {} }, 0.2, 100); + + // 7. Remove all remaining liquidity + userLpBalance = (await getAccount(this.banksClient, userLpAccount)).amount; + await ammClient.removeLiquidityIx(amm, META, USDC, new anchor.BN(userLpBalance), new anchor.BN(0), new anchor.BN(0)).rpc(); + + const finalAmm = await ammClient.getAmm(amm); + assert.isTrue(finalAmm.baseAmount.eqn(0)); + assert.isTrue(finalAmm.quoteAmount.eqn(0)); +} \ No newline at end of file diff --git a/tests/amm/main.test.ts b/tests/amm/main.test.ts new file mode 100644 index 00000000..c77a0c04 --- /dev/null +++ b/tests/amm/main.test.ts @@ -0,0 +1,13 @@ +import initializeAmm from "./unit/initializeAmm.test"; +import addLiquidity from "./unit/addLiquidity.test"; +import swap from "./unit/swap.test"; +import removeLiquidity from "./unit/removeLiquidity.test"; +import ammLifecycle from "./integration/ammLifecycle.test"; + +export default function suite() { + describe("#initialize_amm", initializeAmm); + describe("#add_liquidity", addLiquidity); + describe("#swap", swap); + describe("#remove_liquidity", removeLiquidity); + it("AMM lifecycle", ammLifecycle); +} \ No newline at end of file diff --git a/tests/amm/unit/addLiquidity.test.ts b/tests/amm/unit/addLiquidity.test.ts new file mode 100644 index 00000000..a071fd3d --- /dev/null +++ b/tests/amm/unit/addLiquidity.test.ts @@ -0,0 +1,79 @@ +import { AmmClient, getAmmAddr, getAmmLpMintAddr } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createMint, createAssociatedTokenAccount, mintTo, getAccount } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import { expectError } from "../../utils"; +import * as token from "@solana/spl-token"; + +export default function suite() { + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; + let amm: PublicKey; + + beforeEach(async function() { + ammClient = this.ammClient; + META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); + USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); + + let proposal = Keypair.generate().publicKey; + amm = await ammClient.createAmm(proposal, META, USDC, 500); + + await this.createTokenAccount(META, this.payer.publicKey); + await this.createTokenAccount(USDC, this.payer.publicKey); + + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); + }); + + it("adds initial liquidity to an amm", async function() { + await ammClient + .addLiquidityIx( + amm, + META, + USDC, + new anchor.BN(5000 * 10 ** 6), + new anchor.BN(6 * 10 ** 9), + new anchor.BN(0) + ) + .rpc(); + + const storedAmm = await ammClient.getAmm(amm); + + assert.isTrue(storedAmm.baseAmount.eq(new anchor.BN(6 * 10 ** 9))); + assert.isTrue(storedAmm.quoteAmount.eq(new anchor.BN(5000 * 10 ** 6))); + + const lpMint = await getAccount( + this.banksClient, + token.getAssociatedTokenAddressSync(getAmmLpMintAddr(ammClient.program.programId, amm)[0], this.payer.publicKey), + ); + + assert.equal(lpMint.amount.toString(), (5000 * 10 ** 6).toString()); + }); + + it("adds liquidity after it's already been added", async function() { + await ammClient + .addLiquidityIx( + amm, + META, + USDC, + new anchor.BN(5000 * 10 ** 6), + new anchor.BN(6 * 10 ** 9), + new anchor.BN(0) + ) + .rpc(); + + const storedAmm = await ammClient.getAmm(amm); + + assert.isTrue(storedAmm.baseAmount.eq(new anchor.BN(6 * 10 ** 9))); + assert.isTrue(storedAmm.quoteAmount.eq(new anchor.BN(5000 * 10 ** 6))); + + // const lpMint = await getAccount( + // this.banksClient, + // getAmmLpMintAddr(ammClient.program.programId, amm)[0] + // ); + + // assert.equal(lpMint.amount.toString(), (7500 * 10 ** 6).toString()); + }); +} \ No newline at end of file diff --git a/tests/amm/unit/initializeAmm.test.ts b/tests/amm/unit/initializeAmm.test.ts new file mode 100644 index 00000000..6581f9ac --- /dev/null +++ b/tests/amm/unit/initializeAmm.test.ts @@ -0,0 +1,81 @@ +import { AmmClient, getAmmAddr, getAmmLpMintAddr, PriceMath } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createMint } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import { expectError } from "../../utils"; + +export default function suite() { + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; + + before(async function() { + ammClient = this.ammClient; + META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); + USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); + }); + + it("creates an amm", async function() { + let expectedInitialObservation = new anchor.BN(500_000_000_000); + let expectedMaxObservationChangePerUpdate = new anchor.BN(10_000_000_000); + + let bump: number; + let amm: PublicKey; + [amm, bump] = getAmmAddr( + ammClient.program.programId, + META, + USDC, + ); + + await ammClient.createAmm(Keypair.generate().publicKey, META, USDC, 500); + + const ammAcc = await ammClient.getAmm(amm); + + assert.equal(ammAcc.bump, bump); + assert.isTrue(ammAcc.createdAtSlot.eq(ammAcc.oracle.lastUpdatedSlot)); + assert.equal(ammAcc.baseMint.toBase58(), META.toBase58()); + assert.equal(ammAcc.quoteMint.toBase58(), USDC.toBase58()); + assert.equal(ammAcc.baseMintDecimals, 9); + assert.equal(ammAcc.quoteMintDecimals, 6); + assert.isTrue(ammAcc.baseAmount.eqn(0)); + assert.isTrue(ammAcc.quoteAmount.eqn(0)); + assert.isTrue( + ammAcc.oracle.lastObservation.eq(expectedInitialObservation) + ); + assert.isTrue(ammAcc.oracle.aggregator.eqn(0)); + assert.isTrue( + ammAcc.oracle.maxObservationChangePerUpdate.eq( + expectedMaxObservationChangePerUpdate + ) + ); + assert.isTrue( + ammAcc.oracle.initialObservation.eq(expectedInitialObservation) + ); + }); + + it("fails to create an amm with two identical mints", async function() { + let [ + twapFirstObservationScaled, + twapMaxObservationChangePerUpdateScaled, + ] = PriceMath.getAmmPrices(9, 9, 100, 1); + + const callbacks = expectError( + "SameTokenMints", + "create AMM succeeded despite same token mints" + ); + + let proposal = Keypair.generate().publicKey; + + await ammClient + .createAmmIx( + META, + META, + twapFirstObservationScaled, + twapMaxObservationChangePerUpdateScaled, + ) + .rpc() + .then(callbacks[0], callbacks[1]); + }); +} + diff --git a/tests/amm/unit/removeLiquidity.test.ts b/tests/amm/unit/removeLiquidity.test.ts new file mode 100644 index 00000000..0ed1c747 --- /dev/null +++ b/tests/amm/unit/removeLiquidity.test.ts @@ -0,0 +1,130 @@ +import { AmmClient, getAmmAddr, getAmmLpMintAddr } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createMint, createAssociatedTokenAccount, mintTo, getAccount, getMint } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import * as token from "@solana/spl-token"; +import { expectError } from "../../utils"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; + +export default function suite() { + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; + let amm: PublicKey; + + beforeEach(async function() { + ammClient = this.ammClient; + META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); + USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); + + await this.createTokenAccount(META, this.payer.publicKey); + await this.createTokenAccount(USDC, this.payer.publicKey); + + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); + + let proposal = Keypair.generate().publicKey; + amm = await ammClient.createAmm(proposal, META, USDC, 500); + + await ammClient.addLiquidity(amm, 1000, 2); + }); + + it("can't remove 0 liquidity", async function() { + const callbacks = expectError( + "ZeroLiquidityRemove", + "was able to remove 0 liquidity" + ); + + await ammClient + .removeLiquidityIx(amm, META, USDC, new anchor.BN(0), new anchor.BN(0), new anchor.BN(0)) + .rpc() + .then(callbacks[0], callbacks[1]); + }); + + it("remove some liquidity from an amm position", async function() { + const ammStart = await ammClient.getAmm(amm); + + let userLpAccount = getAssociatedTokenAddressSync( + ammStart.lpMint, + this.payer.publicKey + ); + + const userLpAccountStart = await getAccount(this.banksClient, userLpAccount); + const lpMintStart = await getMint(this.banksClient, ammStart.lpMint); + + await ammClient + .removeLiquidityIx( + amm, + META, + USDC, + new anchor.BN(userLpAccountStart.amount.toString()).divn(2), + new anchor.BN(0), + new anchor.BN(0) + ) + .rpc(); + + const userLpAccountEnd = await getAccount(this.banksClient, userLpAccount); + const lpMintEnd = await getMint(this.banksClient, ammStart.lpMint); + + const ammEnd = await ammClient.getAmm(amm); + + assert.isBelow(Number(lpMintEnd.supply), Number(lpMintStart.supply)); + assert.isBelow( + Number(userLpAccountEnd.amount), + Number(userLpAccountStart.amount) + ); + + assert.isBelow( + ammEnd.baseAmount.toNumber(), + ammStart.baseAmount.toNumber() + ); + assert.isBelow( + ammEnd.quoteAmount.toNumber(), + ammStart.quoteAmount.toNumber() + ); + }); + + it("remove all liquidity from an amm position", async function() { + const ammStart = await ammClient.getAmm(amm); + + let userLpAccount = getAssociatedTokenAddressSync( + ammStart.lpMint, + this.payer.publicKey + ); + + const userLpAccountStart = await getAccount(this.banksClient, userLpAccount); + const lpMintStart = await getMint(this.banksClient, ammStart.lpMint); + + await ammClient + .removeLiquidityIx( + amm, + META, + USDC, + new anchor.BN(userLpAccountStart.amount.toString()), + new anchor.BN(1 * 10 ** 9), + new anchor.BN(10 * 10 ** 6) + ) + .rpc(); + + const userLpAccountEnd = await getAccount(this.banksClient, userLpAccount); + const lpMintEnd = await getMint(this.banksClient, ammStart.lpMint); + + assert.isBelow(Number(lpMintEnd.supply), Number(lpMintStart.supply)); + assert.isBelow( + Number(userLpAccountEnd.amount), + Number(userLpAccountStart.amount) + ); + + const ammEnd = await ammClient.getAmm(amm); + + assert.isBelow( + ammEnd.baseAmount.toNumber(), + ammStart.baseAmount.toNumber() + ); + assert.isBelow( + ammEnd.quoteAmount.toNumber(), + ammStart.quoteAmount.toNumber() + ); + }); +} \ No newline at end of file diff --git a/tests/amm/unit/swap.test.ts b/tests/amm/unit/swap.test.ts new file mode 100644 index 00000000..988bc4dc --- /dev/null +++ b/tests/amm/unit/swap.test.ts @@ -0,0 +1,248 @@ +import { AmmClient, getAmmAddr, getAmmLpMintAddr } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createMint, createAssociatedTokenAccount, mintTo, getAccount, getMint } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import { expectError } from "../../utils"; +import { advanceBySlots } from "../../utils"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; + +export default function suite() { + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; + let amm: PublicKey; + + beforeEach(async function() { + ammClient = this.ammClient; + META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); + USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); + + await this.createTokenAccount(META, this.payer.publicKey); + await this.createTokenAccount(USDC, this.payer.publicKey); + + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); + + let proposal = Keypair.generate().publicKey; + amm = await ammClient.createAmm(proposal, META, USDC, 500); + + await ammClient + .addLiquidityIx( + amm, + META, + USDC, + new anchor.BN(10_000 * 10 ** 6), + new anchor.BN(10 * 10 ** 9), + new anchor.BN(0) + ) + .rpc(); + }); + + it("fails when you have insufficient balance", async () => { + let callbacks = expectError( + "InsufficientBalance", + "we should have caught a user not having enough balance" + ); + + await ammClient + .swap(amm, { buy: {} }, 10_000_000, 1) + .then(callbacks[0], callbacks[1]); + + await ammClient + .swap(amm, { sell: {} }, 100_000, 1) + .then(callbacks[0], callbacks[1]); + }); + + it("buys", async function() { + const expectedOut = 0.098029507; + + // Ensure user has enough USDC + await this.mintTo(USDC, this.payer.publicKey, this.payer, 200 * 10 ** 6); + + const storedAmm = await ammClient.getAmm(amm); + let sim = ammClient.simulateSwap( + new anchor.BN(100 * 10 ** 6), + { buy: {} }, + storedAmm.baseAmount, + storedAmm.quoteAmount + ); + assert.equal( + sim.expectedOut.toString(), + new anchor.BN(expectedOut * 10 ** 9).toString() + ); + + let callbacks = expectError( + "SwapSlippageExceeded", + "we got back too many tokens from the AMM" + ); + + await ammClient + .swap(amm, { buy: {} }, 100, expectedOut + 0.000000001) + .then(callbacks[0], callbacks[1]); + + await ammClient.swap(amm, { buy: {} }, 100, expectedOut); + + await validateAmmState({ + banksClient: this.banksClient, + ammClient, + amm, + base: META, + quote: USDC, + expectedBaseAmount: (10 - expectedOut) * 10 ** 9, + expectedQuoteAmount: 10_100 * 10 ** 6, + expectedLpSupply: 10_000 * 10 ** 6, + }); + }); + + it("sells", async function() { + const expectedOut = 900.818926; + + let callbacks = expectError( + "SwapSlippageExceeded", + "we got back too many tokens from the AMM" + ); + + await ammClient + .swap(amm, { sell: {} }, 1, expectedOut + 0.000001) + .then(callbacks[0], callbacks[1]); + + await ammClient.swap(amm, { sell: {} }, 1, expectedOut); + + await validateAmmState({ + banksClient: this.banksClient, + ammClient, + amm, + base: META, + quote: USDC, + expectedBaseAmount: 11 * 10 ** 9, + expectedQuoteAmount: (10_000 - expectedOut) * 10 ** 6, + expectedLpSupply: 10_000 * 10 ** 6, + }); + }); + + it("swap base to quote and back, should not be profitable", async function() { + const permissionlessAmmStart = await ammClient.program.account.amm.fetch(amm); + const ammEnd = await ammClient.getAmm(amm); + + let startingBaseSwapAmount = 1 * 10 ** 9; + + await ammClient + .swapIx( + amm, + META, + USDC, + { sell: {} }, + new anchor.BN(startingBaseSwapAmount), + new anchor.BN(1) + ) + .rpc(); + + await advanceBySlots(this.context, 1n); + + const ammMiddle = await ammClient.getAmm(amm); + let quoteReceived = + permissionlessAmmStart.quoteAmount.toNumber() - + ammMiddle.quoteAmount.toNumber(); + + await ammClient + .swapIx(amm, META, USDC, { buy: {} }, new anchor.BN(quoteReceived), new anchor.BN(1)) + .rpc(); + + const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(amm); + let baseReceived = + ammMiddle.baseAmount.toNumber() - + permissionlessAmmEnd.baseAmount.toNumber(); + + assert.isBelow(baseReceived, startingBaseSwapAmount); + assert.isAbove(baseReceived, startingBaseSwapAmount * 0.98); + }); + + it("swap quote to base and back, should not be profitable", async function() { + const ammStart = await ammClient.getAmm(amm); + + let startingQuoteSwapAmount = 1 * 10 ** 6; + + // Ensure user has enough USDC + await this.mintTo(USDC, this.payer.publicKey, this.payer, 2 * 10 ** 6); + + await ammClient + .swapIx( + amm, + META, + USDC, + { buy: {} }, + new anchor.BN(startingQuoteSwapAmount), + new anchor.BN(1) + ) + .rpc(); + + await advanceBySlots(this.context, 1n); + + const ammMiddle = await ammClient.getAmm(amm); + let baseReceived = + ammStart.baseAmount.toNumber() - ammMiddle.baseAmount.toNumber(); + + await ammClient + .swapIx(amm, META, USDC, { sell: {} }, new anchor.BN(baseReceived), new anchor.BN(1)) + .rpc(); + + const ammEnd = await ammClient.getAmm(amm); + let quoteReceived = + ammMiddle.quoteAmount.toNumber() - ammEnd.quoteAmount.toNumber(); + + assert.isBelow(quoteReceived, startingQuoteSwapAmount); + assert.isAbove(quoteReceived, startingQuoteSwapAmount * 0.98); + }); +} + +async function validateAmmState({ + banksClient, + ammClient, + amm, + base, + quote, + expectedBaseAmount, + expectedQuoteAmount, + expectedLpSupply, +}: { + banksClient: any; + ammClient: AmmClient; + amm: PublicKey; + base: PublicKey; + quote: PublicKey; + expectedBaseAmount: number; + expectedQuoteAmount: number; + expectedLpSupply: number; +}) { + const storedAmm = await ammClient.getAmm(amm); + + assert.equal(storedAmm.baseAmount.toString(), expectedBaseAmount.toString()); + assert.equal( + storedAmm.quoteAmount.toString(), + expectedQuoteAmount.toString() + ); + + assert.equal( + ( + await getAccount( + banksClient, + getAssociatedTokenAddressSync(base, amm, true) + ) + ).amount, + BigInt(expectedBaseAmount) + ); + assert.equal( + ( + await getAccount( + banksClient, + getAssociatedTokenAddressSync(quote, amm, true) + ) + ).amount, + BigInt(expectedQuoteAmount) + ); + assert.equal( + (await getMint(banksClient, storedAmm.lpMint)).supply, + BigInt(expectedLpSupply) + ); +} \ No newline at end of file diff --git a/tests/autocrat/unit/autocrat.ts b/tests/autocrat/autocrat.ts similarity index 100% rename from tests/autocrat/unit/autocrat.ts rename to tests/autocrat/autocrat.ts diff --git a/tests/autocrat/main.test.ts b/tests/autocrat/main.test.ts index ee4bfebc..e69de29b 100644 --- a/tests/autocrat/main.test.ts +++ b/tests/autocrat/main.test.ts @@ -1,8 +0,0 @@ -// import "./unit/autocrat.ts"; -import("./unit/autocrat"); - -describe("autocrat", async function () { - it("should work", async function () { - console.log("should work"); - }); -}); \ No newline at end of file diff --git a/tests/main.test.ts b/tests/main.test.ts index 8140e4cf..6cc508f8 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -1,15 +1,17 @@ -import { startAnchor } from "solana-bankrun"; import conditionalVault from "./conditionalVault/main.test"; +import amm from "./amm/main.test"; + +import { startAnchor } from "solana-bankrun"; import { BankrunProvider } from "anchor-bankrun"; import * as anchor from "@coral-xyz/anchor"; -import { ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { AmmClient, AutocratClient, ConditionalVaultClient } from "@metadaoproject/futarchy"; import { PublicKey, Keypair} from "@solana/web3.js"; import { createAssociatedTokenAccount, createMint, mintTo, getAccount, transfer } from "spl-token-bankrun"; import * as token from "@solana/spl-token"; import { assert } from "chai"; before(async function () { - let context = await startAnchor( + this.context = await startAnchor( "./", [], // [ @@ -22,8 +24,8 @@ before(async function () { // ], [] ); - this.banksClient = context.banksClient; - let provider = new BankrunProvider(context); + this.banksClient = this.context.banksClient; + let provider = new BankrunProvider(this.context); anchor.setProvider(provider); // umi = createUmi(anchor.AnchorProvider.env().connection); @@ -35,6 +37,8 @@ before(async function () { // ); this.vaultClient = ConditionalVaultClient.createClient({ provider: provider as any }); + this.autocratClient = AutocratClient.createClient({ provider: provider as any }); + this.ammClient = AmmClient.createClient({ provider: provider as any }); this.payer = provider.wallet.payer; @@ -67,4 +71,5 @@ before(async function () { } }); -describe("conditional_vault", conditionalVault); \ No newline at end of file +describe("conditional_vault", conditionalVault); +describe("amm", amm); \ No newline at end of file From ba95a6d50a08bcc4383c9b9037532e5728316224 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 31/52] v0.3.0-alpha.13 --- sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/package.json b/sdk/package.json index 0d4a9f8d..6d5e9391 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@metadaoproject/futarchy", - "version": "0.3.0-alpha.12", + "version": "0.3.0-alpha.13", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ From c63468a849b1b83fb984ca0781848b48ac625501 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 32/52] Update SDK version --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0f6f5293..6b12bce7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ }, "dependencies": { "@coral-xyz/anchor": "0.29.0", - "@metadaoproject/futarchy": "0.3.0-alpha.12", + "@metadaoproject/futarchy": "0.3.0-alpha.13", "@metaplex-foundation/mpl-token-metadata": "^3.2.0", "@metaplex-foundation/umi": "^0.9.1", "@metaplex-foundation/umi-bundle-defaults": "^0.9.1", diff --git a/yarn.lock b/yarn.lock index 5662b6f6..7c55326a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -910,10 +910,10 @@ decimal.js "10.3.1" jsbi "4.3.0" -"@metadaoproject/futarchy@0.3.0-alpha.12": - version "0.3.0-alpha.12" - resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.12.tgz#d615cab0c3836bc0a7fa54371e1fa1973733e704" - integrity sha512-s6MceULSXEFMrB80CPVAOilEj5RDsVm4sh84yjh1z/1ixIqmmJZOqBgI9lb9A6NxuKzCYDonry3F90Ec5ii7PQ== +"@metadaoproject/futarchy@0.3.0-alpha.13": + version "0.3.0-alpha.13" + resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.13.tgz#dd7d8f2a77ab22ea97bdd79b697ecddfdd832388" + integrity sha512-j0hUZcnoH5LnCU7zV8lm9h5HV9KNSsM4Kx/KQFQBBdqevVzfUCKu51S9mv49S2KCFbQC36B8/73E6YaA2l807w== dependencies: "@coral-xyz/anchor" "^0.29.0" "@noble/hashes" "^1.4.0" From 5a7909f9b67b94699a54375eeca4c658e24376ee Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 33/52] Remove some old deps --- package.json | 3 - yarn.lock | 2022 ++------------------------------------------------ 2 files changed, 44 insertions(+), 1981 deletions(-) diff --git a/package.json b/package.json index 6b12bce7..e776fdb7 100644 --- a/package.json +++ b/package.json @@ -12,17 +12,14 @@ "@metaplex-foundation/umi-uploader-bundlr": "^0.9.1", "@noble/ed25519": "^2.0.0", "@noble/secp256k1": "^2.0.0", - "@openbook-dex/openbook-v2": "0.1.7", "@solana/spl-token": "^0.3.7", "@solana/web3.js": "^1.90.0", - "@sqds/multisig": "^2.0.0", "anchor-bankrun": "^0.3.0", "arweave": "^1.14.4", "solana-bankrun": "^0.3.0", "spl-token-bankrun": "0.2.6" }, "devDependencies": { - "@mercurial-finance/dynamic-amm-sdk": "^0.4.19", "@solana/spl-memo": "^0.2.3", "@solana/spl-token-registry": "^0.2.4574", "@types/bn.js": "^5.1.0", diff --git a/yarn.lock b/yarn.lock index 7c55326a..73b1841a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,31 +2,13 @@ # yarn lockfile v1 -"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2", "@babel/runtime@^7.23.4": +"@babel/runtime@^7.17.2", "@babel/runtime@^7.23.4": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.0.tgz#584c450063ffda59697021430cb47101b085951e" integrity sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw== dependencies: regenerator-runtime "^0.14.0" -"@blockworks-foundation/mango-client@^3.4.7": - version "3.6.20" - resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.6.20.tgz#66ed0ae4545959fdc31ca1918de0f931de579d41" - integrity sha512-Te0i52KUyp5e8jQQZlIMsTy9fKIfefPHvkA8+NRGIH80kQcnJKKfzw3T1NxaDsc3KFMZwpuN3m4afDNpKTuF0g== - dependencies: - "@project-serum/anchor" "^0.21.0" - "@project-serum/serum" "^0.13.65" - "@project-serum/sol-wallet-adapter" "^0.2.0" - "@solana/spl-token" "^0.1.6" - "@solana/web3.js" "^1.43.5" - big.js "^6.1.1" - bn.js "^5.1.0" - buffer-layout "^1.2.1" - cross-fetch "^3.1.5" - dotenv "^10.0.0" - toformat "^2.0.0" - yargs "^17.0.1" - "@bundlr-network/client@^0.8.8": version "0.8.9" resolved "https://registry.yarnpkg.com/@bundlr-network/client/-/client-0.8.9.tgz#58e969a5d80f8d25d212d46bb7a060730a3c1736" @@ -72,85 +54,6 @@ superstruct "^0.15.4" toml "^3.0.0" -"@coral-xyz/anchor@^0.26.0": - version "0.26.0" - resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.26.0.tgz#c8e4f7177e93441afd030f22d777d54d0194d7d1" - integrity sha512-PxRl+wu5YyptWiR9F2MBHOLLibm87Z4IMUBPreX+DYBtPM+xggvcPi0KAN7+kIL4IrIhXI8ma5V0MCXxSN1pHg== - dependencies: - "@coral-xyz/borsh" "^0.26.0" - "@solana/web3.js" "^1.68.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^6.3.0" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - superstruct "^0.15.4" - toml "^3.0.0" - -"@coral-xyz/anchor@^0.28.0": - version "0.28.0" - resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.28.0.tgz#8345c3c9186a91f095f704d7b90cd256f7e8b2dc" - integrity sha512-kQ02Hv2ZqxtWP30WN1d4xxT4QqlOXYDxmEd3k/bbneqhV3X5QMO4LAtoUFs7otxyivOgoqam5Il5qx81FuI4vw== - dependencies: - "@coral-xyz/borsh" "^0.28.0" - "@solana/web3.js" "^1.68.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^6.3.0" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - superstruct "^0.15.4" - toml "^3.0.0" - -"@coral-xyz/anchor@^0.28.1-beta.2": - version "0.28.1-beta.2" - resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.28.1-beta.2.tgz#4ddd4b2b66af04407be47cf9524147793ec514a0" - integrity sha512-xreUcOFF8+IQKWOBUrDKJbIw2ftpRVybFlEPVrbSlOBCbreCWrQ5754Gt9cHIcuBDAzearCDiBqzsGQdNgPJiw== - dependencies: - "@coral-xyz/borsh" "^0.28.0" - "@noble/hashes" "^1.3.1" - "@solana/web3.js" "^1.68.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^6.3.0" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - pako "^2.0.3" - snake-case "^3.0.4" - superstruct "^0.15.4" - toml "^3.0.0" - -"@coral-xyz/borsh@^0.26.0": - version "0.26.0" - resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.26.0.tgz#d054f64536d824634969e74138f9f7c52bbbc0d5" - integrity sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ== - dependencies: - bn.js "^5.1.2" - buffer-layout "^1.2.0" - -"@coral-xyz/borsh@^0.28.0": - version "0.28.0" - resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.28.0.tgz#fa368a2f2475bbf6f828f4657f40a52102e02b6d" - integrity sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ== - dependencies: - bn.js "^5.1.2" - buffer-layout "^1.2.0" - "@coral-xyz/borsh@^0.29.0": version "0.29.0" resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.29.0.tgz#79f7045df2ef66da8006d47f5399c7190363e71f" @@ -726,190 +629,6 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": - version "9.3.0" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" - integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== - -"@hapi/topo@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" - integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@mercurial-finance/apricot-sdk@0.17.6": - version "0.17.6" - resolved "https://registry.yarnpkg.com/@mercurial-finance/apricot-sdk/-/apricot-sdk-0.17.6.tgz#e5e2266dbf3d454a09d745561f486aaa351d625a" - integrity sha512-/x/nMksG2h3uB7G4bPVXk0YjvsGVUniSikRmCf/VFFly9BqcRcBtSk4aMdSvIjYsJrpSzWVrHXUYeOtUF1ObDA== - dependencies: - "@solana/spl-token" "0.1.8" - "@solana/web3.js" "^1.37.0" - decimal.js "^10.3.1" - tiny-invariant "^1.1.0" - -"@mercurial-finance/cypher-client@^4.1.4": - version "4.1.4" - resolved "https://registry.yarnpkg.com/@mercurial-finance/cypher-client/-/cypher-client-4.1.4.tgz#17b4d76b9f53d2919e6aee4be2fa22432188c52f" - integrity sha512-W5zy+weni9LPPCL6ImGMjb/Ykg8x09kRVSsVg+OaXZ7qrA+/lwEvv32aEn8kKuha0drvuvTNdXHPUuC60UEnsg== - dependencies: - "@project-serum/anchor" "^0.26.0" - "@solana/web3.js" "~1.72.0" - -"@mercurial-finance/drift-sdk@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@mercurial-finance/drift-sdk/-/drift-sdk-1.0.3.tgz#c9426fd7c019aa973914841ca1f6c9397de444ca" - integrity sha512-I+K95ZK7FpSY67UcZ1JW0M2jOnEVoK5k3TMLzqbrv30m0IGBJYZ1zmrdPlIsNdd4N83KMsYbCjiGltL7h0/bYQ== - dependencies: - "@project-serum/anchor" "0.25.0-beta.1" - "@project-serum/serum" "^0.13.38" - "@pythnetwork/client" "2.5.3" - "@solana/spl-token" "^0.1.6" - "@solana/web3.js" "1.66.2" - "@switchboard-xyz/switchboard-v2" "^0.0.67" - bs58 "^5.0.0" - strict-event-emitter-types "^2.0.0" - uuid "^8.3.2" - -"@mercurial-finance/dynamic-amm-sdk@^0.4.19": - version "0.4.19" - resolved "https://registry.yarnpkg.com/@mercurial-finance/dynamic-amm-sdk/-/dynamic-amm-sdk-0.4.19.tgz#30a45199472b745cf536d5be5f30cdd8752c3adb" - integrity sha512-mN3EubUvwXnoY60RANsu67YhEfUIA3gyR25VsM9lHNAsOtwSPOc0gUl5u63TuDNZdcCVsLScklMJytUPuJ/AHg== - dependencies: - "@mercurial-finance/vault-sdk" "0.5.3" - "@project-serum/anchor" "0.24.2" - "@project-serum/borsh" "^0.2.5" - "@saberhq/anchor-contrib" "1.13.32" - "@saberhq/stableswap-sdk" "1.13.32" - "@saberhq/token-utils" "1.13.32" - "@solana/spl-token" "0.1.8" - "@solana/spl-token-registry" "0.2.1105" - "@solana/web3.js" "^1.42.0" - bn-sqrt "^1.0.0" - bn.js "5.2.1" - decimal.js "^10.4.1" - dotenv "^16.0.1" - invariant "^2.2.4" - jsbi "^4.3.0" - -"@mercurial-finance/frakt-sdk@0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@mercurial-finance/frakt-sdk/-/frakt-sdk-0.0.2.tgz#0a0f39aef240c1c7f092156c643f1eb27adfa566" - integrity sha512-x/3W7BMyUzMigkeJMLXNCnZunGw6JFSaG5hx4oaioDy9qT3MxT05OhaoKLs9zMl3RPLjy/xm1Y79eoDdz8qxEQ== - dependencies: - "@project-serum/anchor" "0.24.2" - axios "^1.2.1" - -"@mercurial-finance/francium-sdk@1.4.3": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@mercurial-finance/francium-sdk/-/francium-sdk-1.4.3.tgz#aa6c43da1a923a700bec0b0e425fe72a76d8dd68" - integrity sha512-beFsxFx9WsHsImCSZAPXlitE4kQMugHczATishmJdxXGEHvBfJ3hrncjaWf+znwlMTknx8zYgPFfeP9JzbHHgw== - -"@mercurial-finance/mango-v4@0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@mercurial-finance/mango-v4/-/mango-v4-0.5.4.tgz#9df78a7f146c59c8a645877b6587ab44c51e7303" - integrity sha512-KD0xdmU5+05F/Or7YYtzyhFGwAXqDD+LmN9fJIp/KJfieQcKq/GnLnFdGA8iTLMydyqcO0ymgSxu5Uky2IpyaA== - dependencies: - "@project-serum/anchor" "^0.25.0" - "@project-serum/serum" "^0.13.65" - "@pythnetwork/client" "~2.14.0" - "@solana/spl-token" "0.3.7" - "@solana/web3.js" "^1.63.1" - "@switchboard-xyz/sbv2-lite" "^0.1.6" - big.js "^6.1.1" - binance-api-node "^0.12.0" - bs58 "^5.0.0" - cross-fetch "^3.1.5" - dotenv "^16.0.3" - node-kraken-api "^2.2.2" - -"@mercurial-finance/marginfi-client-v2@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@mercurial-finance/marginfi-client-v2/-/marginfi-client-v2-1.0.2.tgz#685dc3b235081741c97ae0cfd5a63ee2241159c0" - integrity sha512-Bmd6yJhVZLdiHMkJvNmEP+MxT0QCNXDGwwKEgIQxtTsIhAYtnlgBQegXz1t8aad5qEmimWRjyYQWu8jjaZ7UcQ== - dependencies: - "@coral-xyz/anchor" "^0.26.0" - "@mrgnlabs/mrgn-common" "*" - "@project-serum/anchor" "^0.26.0" - "@pythnetwork/client" "^2.9.0" - "@solana/wallet-adapter-base" "^0.9.20" - "@solana/web3.js" "^1.71.0" - "@switchboard-xyz/solana.js" "^2.1.10" - bignumber.js "^9.1.1" - decimal.js "^10.4.3" - superstruct "^1.0.3" - -"@mercurial-finance/optimist@^0.1.4": - version "0.1.9" - resolved "https://registry.yarnpkg.com/@mercurial-finance/optimist/-/optimist-0.1.9.tgz#82b612fc6d602c1010cada4fb396d92b7fcae03a" - integrity sha512-cOJan58djQdg2iHKV/jPFgD1bNm2hffa5S0FXREKSNfzZRfsYLZOnRNJ24X0o+VJ9kC5BY0HYwduT/+dAyZ0AQ== - dependencies: - promise-retry "2.0.1" - -"@mercurial-finance/port-sdk@0.2.69": - version "0.2.69" - resolved "https://registry.yarnpkg.com/@mercurial-finance/port-sdk/-/port-sdk-0.2.69.tgz#79978c4be9175afb3640e18e16f21e439f203ef2" - integrity sha512-DDZBLkoIQVhLQRy+t7dVuiHNw0nAmWD7xqwMekHT71bBLU5ajQglRvQ9b8d2RQQn1WJsXhbGbc7eTUGWdZqWoQ== - dependencies: - "@solana/buffer-layout" "^3.0.0" - "@solana/spl-token" "0.1.8" - "@solana/spl-token-registry" "^0.2.1107" - "@solana/web3.js" "^1.32.0" - big.js "^6.1.1" - bn.js "^5.2.1" - buffer-layout "1.2.2" - -"@mercurial-finance/solend-sdk@0.6.5": - version "0.6.5" - resolved "https://registry.yarnpkg.com/@mercurial-finance/solend-sdk/-/solend-sdk-0.6.5.tgz#54b2fe6fb28356ba8e749e8c42fb9193cec4f258" - integrity sha512-544KqvD1IkpnyTUDOCt0yVlTcayt9rv+CTXHTFQDinWQQkA8uWBoiNbI0g49bJVE//Np3SbJSOgJmq5KlxgvWQ== - dependencies: - "@solana/web3.js" "^1.52.0" - bn.js "^5.2.0" - buffer "^6.0.3" - buffer-layout "^1.2.0" - -"@mercurial-finance/tulip-platform-sdk@2.0.30": - version "2.0.30" - resolved "https://registry.yarnpkg.com/@mercurial-finance/tulip-platform-sdk/-/tulip-platform-sdk-2.0.30.tgz#c528ca8efd96e4667d8d905274065d6435438ad6" - integrity sha512-ySPxAtFLtati9Vv7g3czrcNEiT9HBZboRDNnfEDmCFJqsub87Y2ZF125dpspaZzKUlolOrBIqBy1FZYIreMgfg== - dependencies: - "@project-serum/anchor" "^0.25.0" - "@project-serum/associated-token" "^0.1.1" - "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.66.2" - lodash "^4.17.21" - -"@mercurial-finance/vault-sdk@0.5.3": - version "0.5.3" - resolved "https://registry.yarnpkg.com/@mercurial-finance/vault-sdk/-/vault-sdk-0.5.3.tgz#4a6ef72474a6c81f86d1219c57cbbbd1978879fe" - integrity sha512-JnG5moumWHg15utCMVD/G+1xLveGXSTUAkTQUSiITs7Du1Nw47GtHlpJEa9+z+0uhcy6/eKNiNLkDUrq0z3zSg== - dependencies: - "@blockworks-foundation/mango-client" "^3.4.7" - "@mercurial-finance/apricot-sdk" "0.17.6" - "@mercurial-finance/cypher-client" "^4.1.4" - "@mercurial-finance/drift-sdk" "1.0.3" - "@mercurial-finance/frakt-sdk" "0.0.2" - "@mercurial-finance/francium-sdk" "1.4.3" - "@mercurial-finance/mango-v4" "0.5.4" - "@mercurial-finance/marginfi-client-v2" "1.0.2" - "@mercurial-finance/optimist" "^0.1.4" - "@mercurial-finance/port-sdk" "0.2.69" - "@mercurial-finance/solend-sdk" "0.6.5" - "@mercurial-finance/tulip-platform-sdk" "2.0.30" - "@mithraic-labs/psylend-utils" "0.0.3-rc39" - "@project-serum/anchor" "0.25.0" - "@quarryprotocol/quarry-sdk" "5.0.2" - "@saberhq/anchor-contrib" "1.13.32" - "@solana/buffer-layout" "^4.0.0" - "@solana/spl-token" "0.1.8" - "@solana/spl-token-registry" "0.2.1105" - "@solana/web3.js" "~1.72.0" - bn.js "5.2.1" - cross-fetch "^3.1.5" - decimal.js "10.3.1" - jsbi "4.3.0" - "@metadaoproject/futarchy@0.3.0-alpha.13": version "0.3.0-alpha.13" resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.13.tgz#dd7d8f2a77ab22ea97bdd79b697ecddfdd832388" @@ -923,72 +642,6 @@ decimal.js "^10.4.3" esbuild "^0.17.15" -"@metaplex-foundation/beet-solana@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.4.0.tgz#52891e78674aaa54e0031f1bca5bfbc40de12e8d" - integrity sha512-B1L94N3ZGMo53b0uOSoznbuM5GBNJ8LwSeznxBxJ+OThvfHQ4B5oMUqb+0zdLRfkKGS7Q6tpHK9P+QK0j3w2cQ== - dependencies: - "@metaplex-foundation/beet" ">=0.1.0" - "@solana/web3.js" "^1.56.2" - bs58 "^5.0.0" - debug "^4.3.4" - -"@metaplex-foundation/beet-solana@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.3.1.tgz#4b37cda5c7f32ffd2bdd8b3164edc05c6463ab35" - integrity sha512-tgyEl6dvtLln8XX81JyBvWjIiEcjTkUwZbrM5dIobTmoqMuGewSyk9CClno8qsMsFdB5T3jC91Rjeqmu/6xk2g== - dependencies: - "@metaplex-foundation/beet" ">=0.1.0" - "@solana/web3.js" "^1.56.2" - bs58 "^5.0.0" - debug "^4.3.4" - -"@metaplex-foundation/beet@0.7.1": - version "0.7.1" - resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.7.1.tgz#0975314211643f87b5f6f3e584fa31abcf4c612c" - integrity sha512-hNCEnS2WyCiYyko82rwuISsBY3KYpe828ubsd2ckeqZr7tl0WVLivGkoyA/qdiaaHEBGdGl71OpfWa2rqL3DiA== - dependencies: - ansicolors "^0.3.2" - bn.js "^5.2.0" - debug "^4.3.3" - -"@metaplex-foundation/beet@>=0.1.0": - version "0.7.2" - resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.7.2.tgz#fa4726e4cfd4fb6fed6cddc9b5213c1c2a2d0b77" - integrity sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg== - dependencies: - ansicolors "^0.3.2" - assert "^2.1.0" - bn.js "^5.2.0" - debug "^4.3.3" - -"@metaplex-foundation/beet@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.4.0.tgz#eb2a0a6eb084bb25d67dd9bed2f7387ee7e63a55" - integrity sha512-2OAKJnLatCc3mBXNL0QmWVQKAWK2C7XDfepgL0p/9+8oSx4bmRAFHFqptl1A/C0U5O3dxGwKfmKluW161OVGcA== - dependencies: - ansicolors "^0.3.2" - bn.js "^5.2.0" - debug "^4.3.3" - -"@metaplex-foundation/cusper@^0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@metaplex-foundation/cusper/-/cusper-0.0.2.tgz#dc2032a452d6c269e25f016aa4dd63600e2af975" - integrity sha512-S9RulC2fFCFOQraz61bij+5YCHhSO9llJegK8c8Y6731fSi6snUSQJdCUqYS8AIgR0TKbQvdvgSyIIdbDFZbBA== - -"@metaplex-foundation/mpl-token-metadata@2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-token-metadata/-/mpl-token-metadata-2.2.4.tgz#d934cf20d6e22f7923fc96b80c7534595780eda6" - integrity sha512-cayIZ7w/XUKkUygNyWyx5l5Q8PO5qzIemk6c/7dxKhdHLlaVPwKjRhbg6kIuzZ8tvYP5afUmJKln9rWnI8tQCA== - dependencies: - "@metaplex-foundation/beet" "^0.4.0" - "@metaplex-foundation/beet-solana" "^0.3.0" - "@metaplex-foundation/cusper" "^0.0.2" - "@solana/spl-token" "^0.2.0" - "@solana/web3.js" "^1.35.1" - bn.js "^5.2.0" - debug "^4.3.3" - "@metaplex-foundation/mpl-token-metadata@^3.2.0": version "3.2.1" resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-token-metadata/-/mpl-token-metadata-3.2.1.tgz#d424e378a1ee441a6431d2641d66873118d6dc67" @@ -1132,34 +785,6 @@ "@metaplex-foundation/umi-public-keys" "^0.8.9" "@metaplex-foundation/umi-serializers" "^0.9.0" -"@mithraic-labs/psylend-utils@0.0.3-rc39": - version "0.0.3-rc39" - resolved "https://registry.yarnpkg.com/@mithraic-labs/psylend-utils/-/psylend-utils-0.0.3-rc39.tgz#05b0d2733f5ad8ea0868e20b3e78922808d4a1f3" - integrity sha512-7bfGwg9brPF+mxDck8m9GNWUI23YV7eeWA21WKM51aPnz8IJXR6XBTbi3TrQPSA96B77Qx1cP3uE43CsI29tcA== - dependencies: - "@project-serum/anchor" "0.25.0" - "@project-serum/serum" "^0.13.65" - "@pythnetwork/client" "2.7.3" - "@solana/web3.js" "^1.30.2" - chai "^4.3.7" - psyfi-euros-test "0.0.2-rc.5" - spl2 "npm:@solana/spl-token@^0.2.0" - -"@mrgnlabs/mrgn-common@*": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@mrgnlabs/mrgn-common/-/mrgn-common-1.3.1.tgz#1651923ceda82517acd5c121a38e54dd3b48105f" - integrity sha512-AN+OEqNXGOAjgzbYWayBKCpWs8fXK8XyvOpNtrl/9C3rOAGddQgOCQEx050/5Uudxr7P/ZSTD7RaCV/+bKjLkg== - dependencies: - "@coral-xyz/anchor" "^0.28.1-beta.2" - "@solana/buffer-layout-utils" "^0.2.0" - "@solana/wallet-adapter-base" "^0.9.20" - "@solana/web3.js" "^1.87.6" - bignumber.js "^9.1.1" - bs58 "^5.0.0" - decimal.js "^10.4.3" - numeral "^2.0.6" - superstruct "^1.0.3" - "@noble/curves@^1.0.0", "@noble/curves@^1.2.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" @@ -1174,7 +799,7 @@ dependencies: "@noble/hashes" "1.4.0" -"@noble/ed25519@^1.6.1", "@noble/ed25519@^1.7.0": +"@noble/ed25519@^1.6.1": version "1.7.3" resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123" integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ== @@ -1184,7 +809,7 @@ resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-2.0.0.tgz#5964c8190a4b4b804985717ca566113b93379e43" integrity sha512-/extjhkwFupyopDrt80OMWKdLgP429qLZj+z6sYJz90rF2Iz0gjZh2ArMKPImUl13Kx+0EXI2hN9T/KJV0/Zng== -"@noble/hashes@1.3.3", "@noble/hashes@^1.1.2", "@noble/hashes@^1.3.1", "@noble/hashes@^1.3.3": +"@noble/hashes@1.3.3", "@noble/hashes@^1.3.1", "@noble/hashes@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== @@ -1194,328 +819,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== -"@noble/secp256k1@^1.6.3": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" - integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== - "@noble/secp256k1@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-2.0.0.tgz#c214269d45e0233ad6a8ae5104655453636e253d" integrity sha512-rUGBd95e2a45rlmFTqQJYEFA4/gdIARFfuTuTqLglz0PZ6AKyzyXsEZZq7UZn8hZsvaBgpCzKKBJizT2cJERXw== -"@openbook-dex/openbook-v2@0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@openbook-dex/openbook-v2/-/openbook-v2-0.1.7.tgz#55ae6a1bbc76faa84023cd59b6dfc759a886539b" - integrity sha512-dyL/q01BTmI+KtxjYfaHOat+JDppCIbqGiNQYFIFMqcawHlyXFaK/y4m3lR0A82iAOvr7+27z5FPtbAMWpyHHw== - dependencies: - "@coral-xyz/anchor" "^0.28.1-beta.2" - "@solana/spl-token" "0.3.8" - "@solana/web3.js" "^1.77.3" - big.js "^6.2.1" - -"@project-serum/anchor@0.23.0", "@project-serum/anchor@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.23.0.tgz#2b2eb6b51601b073e8db26663aa2d6c2f2841771" - integrity sha512-LV2/ifZOJVFTZ4GbEloXln3iVfCvO1YM8i7BBCrUm4tehP7irMx4nr4/IabHWOzrQcQElsxSP/lb1tBp+2ff8A== - dependencies: - "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.36.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - find "^0.3.0" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - -"@project-serum/anchor@0.24.2", "@project-serum/anchor@^0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.24.2.tgz#a3c52a99605c80735f446ca9b3a4885034731004" - integrity sha512-0/718g8/DnEuwAidUwh5wLYphUYXhUbiClkuRNhvNoa+1Y8a4g2tJyxoae+emV+PG/Gikd/QUBNMkIcimiIRTA== - dependencies: - "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.36.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - -"@project-serum/anchor@0.25.0", "@project-serum/anchor@^0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.25.0.tgz#88ee4843336005cf5a64c80636ce626f0996f503" - integrity sha512-E6A5Y/ijqpfMJ5psJvbw0kVTzLZFUcOFgs6eSM2M2iWE1lVRF18T6hWZVNl6zqZsoz98jgnNHtVGJMs+ds9A7A== - dependencies: - "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.36.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - superstruct "^0.15.4" - toml "^3.0.0" - -"@project-serum/anchor@0.25.0-beta.1": - version "0.25.0-beta.1" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.25.0-beta.1.tgz#7b113fb6604483d6740c8da9c6d86e9a5d5f6cf7" - integrity sha512-edesFlclgQzIluD2mC0xrGPnABBllKvbGd6MOtNZMCauUnx1Xbu073um8O6mrCeuZrz4PG9AhwAp1y5cOl3R4A== - dependencies: - "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.36.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - superstruct "^0.15.4" - toml "^3.0.0" - -"@project-serum/anchor@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.11.1.tgz#155bff2c70652eafdcfd5559c81a83bb19cec9ff" - integrity sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA== - dependencies: - "@project-serum/borsh" "^0.2.2" - "@solana/web3.js" "^1.17.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.0" - camelcase "^5.3.1" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - find "^0.3.0" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - -"@project-serum/anchor@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.21.0.tgz#ad5fb33744991ec1900cdb2fd22707c908b12b5f" - integrity sha512-flRuW/F+iC8mitNokx82LOXyND7Dyk6n5UUPJpQv/+NfySFrNFlzuQZaBZJ4CG5g9s8HS/uaaIz1nVkDR8V/QA== - dependencies: - "@project-serum/borsh" "^0.2.4" - "@solana/web3.js" "^1.17.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - find "^0.3.0" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - -"@project-serum/anchor@^0.22.0": - version "0.22.1" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.22.1.tgz#698a9620f94691de0a12bbc650a5c8380e2f0e8a" - integrity sha512-5pHeyvQhzLahIQ8aZymmDMZJAJFklN0joZdI+YIqFkK2uU/mlKr6rBLQjxysf/j1mLLiNG00tdyLfUtTAdQz7w== - dependencies: - "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.17.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - find "^0.3.0" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - -"@project-serum/anchor@^0.26.0": - version "0.26.0" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.26.0.tgz#99e15a3923a5d10514f8185b2d3909e5699d60d5" - integrity sha512-Nq+COIjE1135T7qfnOHEn7E0q39bQTgXLFk837/rgFe6Hkew9WML7eHsS+lSYD2p3OJaTiUOHTAq1lHy36oIqQ== - dependencies: - "@coral-xyz/borsh" "^0.26.0" - "@solana/web3.js" "^1.68.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^6.3.0" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - superstruct "^0.15.4" - toml "^3.0.0" - -"@project-serum/associated-token@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@project-serum/associated-token/-/associated-token-0.1.1.tgz#9acf745e84dad21e2ea26e06694704b9d698e532" - integrity sha512-Zc1wdqragbDiyBVagzIbIsMe37P7fgkArWZPIj+jJjDIoznlmYMK6ASU5mtdDZrPJ7sNABF/lzZ3+jvCCcU+oA== - -"@project-serum/borsh@^0.2.2", "@project-serum/borsh@^0.2.4", "@project-serum/borsh@^0.2.5": - version "0.2.5" - resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663" - integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q== - dependencies: - bn.js "^5.1.2" - buffer-layout "^1.2.0" - -"@project-serum/common@^0.0.1-beta.3": - version "0.0.1-beta.3" - resolved "https://registry.yarnpkg.com/@project-serum/common/-/common-0.0.1-beta.3.tgz#53586eaff9d9fd7e8938b1e12080c935b8b6ad07" - integrity sha512-gnQE/eUydTtto5okCgLWj1M97R9RRPJqnhKklikYI7jP/pnNhDmngSXC/dmfzED2GXSJEIKNIlxVw1k+E2Aw3w== - dependencies: - "@project-serum/serum" "^0.13.21" - bn.js "^5.1.2" - superstruct "0.8.3" - -"@project-serum/serum@^0.13.21", "@project-serum/serum@^0.13.38", "@project-serum/serum@^0.13.61", "@project-serum/serum@^0.13.65": - version "0.13.65" - resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.65.tgz#6d3cf07912f13985765237f053cca716fe84b0b0" - integrity sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA== - dependencies: - "@project-serum/anchor" "^0.11.1" - "@solana/spl-token" "^0.1.6" - "@solana/web3.js" "^1.21.0" - bn.js "^5.1.2" - buffer-layout "^1.2.0" - -"@project-serum/sol-wallet-adapter@^0.2.0": - version "0.2.6" - resolved "https://registry.yarnpkg.com/@project-serum/sol-wallet-adapter/-/sol-wallet-adapter-0.2.6.tgz#b4cd25a566294354427c97c26d716112b91a0107" - integrity sha512-cpIb13aWPW8y4KzkZAPDgw+Kb+DXjCC6rZoH74MGm3I/6e/zKyGnfAuW5olb2zxonFqsYgnv7ev8MQnvSgJ3/g== - dependencies: - bs58 "^4.0.1" - eventemitter3 "^4.0.7" - -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== - -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== - -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== - -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== - -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== - dependencies: - "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" - -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== - -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== - -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== - -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== - -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== - -"@pythnetwork/client@2.5.3": - version "2.5.3" - resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.5.3.tgz#86c9f92d01d8f282fdd8b5b11039da654e263988" - integrity sha512-NBLxPnA6A3tZb/DYUooD4SO63UJ70s9DzzFPGXcQNBR9itcycp7aaV+UA5oUPloD/4UHL9soo2fRuDVur0gmhA== - dependencies: - "@solana/web3.js" "^1.30.2" - assert "^2.0.0" - buffer "^6.0.1" - -"@pythnetwork/client@2.7.3": - version "2.7.3" - resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.7.3.tgz#6075a16dc394d1734e76b04e907a680490a61536" - integrity sha512-+2k5JXxv/yUA6WMESSppJlg4T/AP+nZZfBnHmeG3RPCIJx+bargxFLCK4B2KgpQYdeTWb+2z8yRCNF7tHooCFQ== - dependencies: - buffer "^6.0.1" - -"@pythnetwork/client@^2.9.0": - version "2.21.0" - resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.21.0.tgz#01506bcdf2b62878fbe1368656bfe1c7581c72d7" - integrity sha512-jqUuPLuVKRNUsZfwLuvK/MwnJ3LIrIxBNoz43xt0fjvVuH5QyTlz51ek76CkeKfCbomGKe41Vq7bvn8aqWVOGA== - dependencies: - "@coral-xyz/anchor" "^0.29.0" - "@coral-xyz/borsh" "^0.28.0" - buffer "^6.0.1" - -"@pythnetwork/client@~2.14.0": - version "2.14.0" - resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.14.0.tgz#0c12a7e1bcc66ff198fdb64c003b8d4a24431efc" - integrity sha512-tFLGnuIBjlzDa8TrJULzJIdykketGXDJZtO+8+i4XO9l2uOKXzxt+pjt05ng5B9iY63FzJqgAkawT/O3V0NAdQ== - dependencies: - "@coral-xyz/anchor" "^0.26.0" - buffer "^6.0.1" - -"@quarryprotocol/quarry-sdk@5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@quarryprotocol/quarry-sdk/-/quarry-sdk-5.0.2.tgz#f306ef4c0446a2ae7432e6e0593bc3dcb75812ee" - integrity sha512-wczlmNfb8fk6WCZsLLR7ysSjgxl6ZdEJ7cNDhgvFpU9E1YMSN1f2l2NK9yw+VksuLxWCightFsBrHSqqIftDzQ== - dependencies: - superstruct "^0.15.4" - tiny-invariant "^1.2.0" - tslib "^2.3.1" - "@randlabs/communication-bridge@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@randlabs/communication-bridge/-/communication-bridge-1.0.1.tgz#d1ecfc29157afcbb0ca2d73122d67905eecb5bf3" @@ -1528,90 +836,6 @@ dependencies: "@randlabs/communication-bridge" "1.0.1" -"@saberhq/anchor-contrib@1.13.32": - version "1.13.32" - resolved "https://registry.yarnpkg.com/@saberhq/anchor-contrib/-/anchor-contrib-1.13.32.tgz#99c7da5030c92a073a6b934dd11cee3c2893bc9d" - integrity sha512-coU3mFuwJCM5hdNkMSxse+WyJskDer6OzAAXCE5bxe8Lm2bQFN1P3uohrBmV6r+eQlK+im+oozne7CyCSmR1lw== - dependencies: - "@saberhq/solana-contrib" "^1.13.32" - eventemitter3 "^4.0.7" - lodash.camelcase "^4.3.0" - lodash.mapvalues "^4.6.0" - tslib "^2.4.0" - -"@saberhq/option-utils@^1.15.0": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@saberhq/option-utils/-/option-utils-1.15.0.tgz#6415e2ecacac060ef8e00a5f1b32fb113856c1ac" - integrity sha512-XVbS9H4b8PIGXJGaErkOurxV2FKFyvMwYq0pD8Y1iEPoi6HB//+HnpEKAv8tCssIQ5Nn1zQWzmQ9CmGkrwzcsw== - dependencies: - tslib "^2.6.2" - -"@saberhq/solana-contrib@^1.13.32", "@saberhq/solana-contrib@^1.15.0": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@saberhq/solana-contrib/-/solana-contrib-1.15.0.tgz#5b9059fd3bd73eda640a50c9fa89fcd030befe69" - integrity sha512-OExL5qGrNMmIKINU7qFUDmY7+xIwVM2s360g99k8CRNHSnjpnqIzwDjr2CnvEFpeQPp22OdGlS63woDp0w0JsQ== - dependencies: - "@saberhq/option-utils" "^1.15.0" - "@solana/buffer-layout" "^4.0.0" - "@types/promise-retry" "^1.1.6" - "@types/retry" "^0.12.5" - promise-retry "^2.0.1" - retry "^0.13.1" - tiny-invariant "^1.3.1" - tslib "^2.6.2" - -"@saberhq/stableswap-sdk@1.13.32": - version "1.13.32" - resolved "https://registry.yarnpkg.com/@saberhq/stableswap-sdk/-/stableswap-sdk-1.13.32.tgz#eb439f7ed08102c971571821e38980c4d0841223" - integrity sha512-dE9P6EeXCkH0ay+L0a41SmqQfD/YkSRP+z3p9LCADH4gnKpoSgObEB7LKm7Xp06kq+DQiClJV/ycKd9LRJjCVA== - dependencies: - "@saberhq/solana-contrib" "^1.13.32" - "@saberhq/token-utils" "^1.13.32" - "@solana/buffer-layout" "^4.0.0" - tiny-invariant "^1.2.0" - tslib "^2.4.0" - -"@saberhq/token-utils@1.13.32": - version "1.13.32" - resolved "https://registry.yarnpkg.com/@saberhq/token-utils/-/token-utils-1.13.32.tgz#2acc98bd4d3732b826396a70b958198e0d20dee8" - integrity sha512-n5ECiw82IQJwyq9bTkcrbNWVi+lAQoQlJlTmIye8odUQATBsqOWN+clqfrFkn/UMmezO60bo34bUaM0Oir7Pew== - dependencies: - "@saberhq/solana-contrib" "^1.13.32" - "@solana/buffer-layout" "^4.0.0" - "@solana/spl-token" "^0.1.8" - "@ubeswap/token-math" "^5.1.6" - tiny-invariant "^1.2.0" - tslib "^2.4.0" - -"@saberhq/token-utils@^1.13.32": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@saberhq/token-utils/-/token-utils-1.15.0.tgz#7f7a222110276ce53332183de00bae5f59eb284d" - integrity sha512-XydjtT08Qq6hdJXnfk1NtIZeyOhNyb0YXrVtM6K3OoaH88HjF36niIRv6kMMcWAGm+Hkp1111NyYFhk55PNfOA== - dependencies: - "@saberhq/solana-contrib" "^1.15.0" - "@solana/buffer-layout" "^4.0.0" - "@solana/spl-token" "^0.1.8" - "@ubeswap/token-math" "^5.2.1" - tiny-invariant "^1.3.1" - tslib "^2.6.2" - -"@sideway/address@^4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" - integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@sideway/formula@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" - integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== - -"@sideway/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" - integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== - "@solana/buffer-layout-utils@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" @@ -1622,13 +846,6 @@ bigint-buffer "^1.1.5" bignumber.js "^9.0.1" -"@solana/buffer-layout@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz#b9353caeb9a1589cb77a1b145bcb1a9a93114326" - integrity sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w== - dependencies: - buffer "~6.0.3" - "@solana/buffer-layout@^4.0.0", "@solana/buffer-layout@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15" @@ -1691,62 +908,14 @@ "@solana/options" "2.0.0-experimental.8618508" "@solana/spl-type-length-value" "0.1.0" -"@solana/spl-token-registry@0.2.1105": - version "0.2.1105" - resolved "https://registry.yarnpkg.com/@solana/spl-token-registry/-/spl-token-registry-0.2.1105.tgz#460fc363096aa59c5150f67736cddc1d5a810e8a" - integrity sha512-s9MIUoTAtqYsg1RaXIHXq7DhsUVS9VckvrwYuJBFn68YCZNSMUEquqaimbaHi88OVduFsApVAbKRmsGnJ9abIw== - dependencies: - cross-fetch "3.0.6" - -"@solana/spl-token-registry@^0.2.1107", "@solana/spl-token-registry@^0.2.4574": +"@solana/spl-token-registry@^0.2.4574": version "0.2.4574" resolved "https://registry.yarnpkg.com/@solana/spl-token-registry/-/spl-token-registry-0.2.4574.tgz#13f4636b7bec90d2bb43bbbb83512cd90d2ce257" integrity sha512-JzlfZmke8Rxug20VT/VpI2XsXlsqMlcORIUivF+Yucj7tFi7A0dXG7h+2UnD0WaZJw8BrUz2ABNkUnv89vbv1A== dependencies: cross-fetch "3.0.6" -"@solana/spl-token@0.1.8", "@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": - version "0.1.8" - resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" - integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ== - dependencies: - "@babel/runtime" "^7.10.5" - "@solana/web3.js" "^1.21.0" - bn.js "^5.1.0" - buffer "6.0.3" - buffer-layout "^1.2.0" - dotenv "10.0.0" - -"@solana/spl-token@0.3.7": - version "0.3.7" - resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.7.tgz#6f027f9ad8e841f792c32e50920d9d2e714fc8da" - integrity sha512-bKGxWTtIw6VDdCBngjtsGlKGLSmiu/8ghSt/IOYJV24BsymRbgq7r12GToeetpxmPaZYLddKwAz7+EwprLfkfg== - dependencies: - "@solana/buffer-layout" "^4.0.0" - "@solana/buffer-layout-utils" "^0.2.0" - buffer "^6.0.3" - -"@solana/spl-token@0.3.8": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.8.tgz#8e9515ea876e40a4cc1040af865f61fc51d27edf" - integrity sha512-ogwGDcunP9Lkj+9CODOWMiVJEdRtqHAtX2rWF62KxnnSWtMZtV9rDhTrZFshiyJmxDnRL/1nKE1yJHg4jjs3gg== - dependencies: - "@solana/buffer-layout" "^4.0.0" - "@solana/buffer-layout-utils" "^0.2.0" - buffer "^6.0.3" - -"@solana/spl-token@^0.2.0", "spl2@npm:@solana/spl-token@^0.2.0": - name spl2 - version "0.2.0" - resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.2.0.tgz#329bb6babb5de0f9c40035ddb1657f01a8347acd" - integrity sha512-RWcn31OXtdqIxmkzQfB2R+WpsJOVS6rKuvpxJFjvik2LyODd+WN58ZP3Rpjpro03fscGAkzlFuP3r42doRJgyQ== - dependencies: - "@solana/buffer-layout" "^4.0.0" - "@solana/buffer-layout-utils" "^0.2.0" - "@solana/web3.js" "^1.32.0" - start-server-and-test "^1.14.0" - -"@solana/spl-token@^0.3.6", "@solana/spl-token@^0.3.7", "@solana/spl-token@^0.3.8": +"@solana/spl-token@^0.3.7", "@solana/spl-token@^0.3.8": version "0.3.11" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.11.tgz#cdc10f9472b29b39c8983c92592cadd06627fb9a" integrity sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ== @@ -1763,7 +932,7 @@ dependencies: buffer "^6.0.3" -"@solana/wallet-adapter-base@^0.9.2", "@solana/wallet-adapter-base@^0.9.20": +"@solana/wallet-adapter-base@^0.9.2": version "0.9.23" resolved "https://registry.yarnpkg.com/@solana/wallet-adapter-base/-/wallet-adapter-base-0.9.23.tgz#3b17c28afd44e173f44f658bf9700fd637e12a11" integrity sha512-apqMuYwFp1jFi55NxDfvXUX2x1T0Zh07MxhZ/nCCTGys5raSfYUh82zen2BLv8BSDj/JxZ2P/s7jrQZGrX8uAw== @@ -1781,28 +950,7 @@ "@wallet-standard/base" "^1.0.1" "@wallet-standard/features" "^1.0.3" -"@solana/web3.js@1.66.2": - version "1.66.2" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.66.2.tgz#80b43c5868b846124fe3ebac7d3943930c3fa60c" - integrity sha512-RyaHMR2jGmaesnYP045VLeBGfR/gAW3cvZHzMFGg7bkO+WOYOYp1nEllf0/la4U4qsYGKCsO9eEevR5fhHiVHg== - dependencies: - "@babel/runtime" "^7.12.5" - "@noble/ed25519" "^1.7.0" - "@noble/hashes" "^1.1.2" - "@noble/secp256k1" "^1.6.3" - "@solana/buffer-layout" "^4.0.0" - bigint-buffer "^1.1.5" - bn.js "^5.0.0" - borsh "^0.7.0" - bs58 "^4.0.1" - buffer "6.0.1" - fast-stable-stringify "^1.0.0" - jayson "^3.4.4" - node-fetch "2" - rpc-websockets "^7.5.0" - superstruct "^0.14.2" - -"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.30.2", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.33.0", "@solana/web3.js@^1.35.1", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.37.0", "@solana/web3.js@^1.42.0", "@solana/web3.js@^1.43.5", "@solana/web3.js@^1.52.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.63.1", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.70.3", "@solana/web3.js@^1.71.0", "@solana/web3.js@^1.77.3", "@solana/web3.js@^1.78.3", "@solana/web3.js@^1.87.6", "@solana/web3.js@^1.90.0": +"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.90.0": version "1.91.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.91.0.tgz#a763b0fcca0fa005adce3d02f3a4b6d1b84eccb7" integrity sha512-iqOL9RjNra0TM9BbQWxBRUcZUiNmCJJO+vXLp0GiELUJhbNAoE/K6OV6s+gNEsC13dslvKtfA4mmzRnZNWXtIQ== @@ -1844,123 +992,12 @@ rpc-websockets "^7.5.1" superstruct "^0.14.2" -"@solana/web3.js@~1.72.0": - version "1.72.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.72.0.tgz#8d54de6887bc885c78a4a2bebe891c349fbb029e" - integrity sha512-xMoCk0y/GpiQhHbRjMcrd5NpmkwhAA0c01id7lrr6nhNdz6Uc/CywPdBeZw3Qz6BVZ/qlUoerpKPWeiXqMUjwA== - dependencies: - "@babel/runtime" "^7.12.5" - "@noble/ed25519" "^1.7.0" - "@noble/hashes" "^1.1.2" - "@noble/secp256k1" "^1.6.3" - "@solana/buffer-layout" "^4.0.0" - agentkeepalive "^4.2.1" - bigint-buffer "^1.1.5" - bn.js "^5.0.0" - borsh "^0.7.0" - bs58 "^4.0.1" - buffer "6.0.1" - fast-stable-stringify "^1.0.0" - jayson "^3.4.4" - node-fetch "2" - rpc-websockets "^7.5.0" - superstruct "^0.14.2" - -"@sqds/multisig@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sqds/multisig/-/multisig-2.0.0.tgz#ac9c842c47b828dc2994813e3f7858efb317b886" - integrity sha512-tiZB1cOi/6Xzolqq/xBAtjoXnmto9tH3qGkbTL9j1SsRFVkIdIZaP2qkzh2ocgH3vYb8e1xsfA4hHjiVJVHLXA== - dependencies: - "@metaplex-foundation/beet" "0.7.1" - "@metaplex-foundation/beet-solana" "0.4.0" - "@metaplex-foundation/cusper" "^0.0.2" - "@solana/spl-token" "^0.3.6" - "@solana/web3.js" "^1.70.3" - "@types/bn.js" "^5.1.1" - assert "^2.0.0" - bn.js "^5.2.1" - buffer "6.0.3" - invariant "2.2.4" - "@supercharge/promise-pool@^2.1.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@supercharge/promise-pool/-/promise-pool-2.4.0.tgz#6050eea8c2d7f92ddd4ddc582ee328b15c034ad3" integrity sha512-O9CMipBlq5OObdt1uKJGIzm9cdjpPWfj+a+Zw9EgWKxaMNHKC7EU7X9taj3H0EGQNLOSq2jAcOa3EzxlfHsD6w== -"@switchboard-xyz/common@^2.3.6": - version "2.3.17" - resolved "https://registry.yarnpkg.com/@switchboard-xyz/common/-/common-2.3.17.tgz#4da631540cf189909df394cb3aa54ca32241d9ba" - integrity sha512-gg3nLz1Esb1pMt1mhhEkAYNFsvlYEpH+n3mNyguaGPIUF7AFnpzj9xU3aKPQqT2MeJWg+EmU32WFOdLwTK7X7A== - dependencies: - big.js "^6.2.1" - bn.js "^5.2.1" - bs58 "^5.0.0" - cron-validator "^1.3.1" - decimal.js "^10.4.3" - lodash "^4.17.21" - protobufjs "^7.2.4" - yaml "^2.2.1" - -"@switchboard-xyz/sbv2-lite@^0.1.6": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@switchboard-xyz/sbv2-lite/-/sbv2-lite-0.1.6.tgz#dc3fbb5b3b028dbd3c688b991bcc48a670131ddb" - integrity sha512-yNNBBPpqefrf6QaUw7pKj1MYOtITaH5lqpGKdSMOqzGmtTOCBPI9P9Hz/ZfQEzbuRIUws1aNEazxYzitBo1q7Q== - dependencies: - "@project-serum/anchor" "^0.24.2" - big.js "^6.1.1" - -"@switchboard-xyz/solana.js@^2.1.10": - version "2.8.4" - resolved "https://registry.yarnpkg.com/@switchboard-xyz/solana.js/-/solana.js-2.8.4.tgz#383ee63d17040199acf36a88802a65df512ed9c3" - integrity sha512-kiFLAXKfNdAEUWZSdXuCCgs2ISKRJL8dayweKq5KIvQfxOmNEqENuaWwEaWP+7MiVUZotbF3oNG1xQNlKDcXLg== - dependencies: - "@coral-xyz/anchor" "^0.28.0" - "@coral-xyz/borsh" "^0.28.0" - "@solana/spl-token" "^0.3.8" - "@solana/web3.js" "^1.78.3" - "@switchboard-xyz/common" "^2.3.6" - cron-validator "^1.3.1" - dotenv "^16.3.1" - lodash "^4.17.21" - -"@switchboard-xyz/switchboard-api@^0.2.150": - version "0.2.201" - resolved "https://registry.yarnpkg.com/@switchboard-xyz/switchboard-api/-/switchboard-api-0.2.201.tgz#d082206d521d24dbcdeb06a77e6637a56ab883eb" - integrity sha512-hlxgeYmO6dbOEcmQzT1SqRxdiCFyVOMpyW4HFPgmPKT0+wSVkjsLc+BKkMGYPDaO0sWMLTJrj0FGhhTsrqd8Mg== - dependencies: - "@solana/web3.js" "^1.17.0" - form-data "^4.0.0" - protobufjs "^6.10.2" - rpc-websockets "^7.4.12" - typedoc "^0.22.15" - ws "^7.4.6" - -"@switchboard-xyz/switchboard-v2@^0.0.67": - version "0.0.67" - resolved "https://registry.yarnpkg.com/@switchboard-xyz/switchboard-v2/-/switchboard-v2-0.0.67.tgz#2fb1f2f18266f6963cca3ff31da1196dc3172c8d" - integrity sha512-6yFFCSrc7MGLEu2bfRt4dzcYfWyBF8JoA2N/hTZUDqAw9xaEgSSR7laTvHAjVy4m4MVwu7DLadpCivLy/QEPLA== - dependencies: - "@project-serum/anchor" "^0.22.0" - "@solana/spl-token" "^0.1.8" - "@solana/web3.js" "^1.33.0" - "@switchboard-xyz/switchboard-api" "^0.2.150" - assert "^2.0.0" - big.js "^6.1.1" - bs58 "^4.0.1" - buffer-layout "^1.2.0" - chan "^0.6.1" - crypto-js "^4.0.0" - long "^4.0.0" - protobufjs "^6.10.2" - ts-proto "^1.79.0" - typescript "^4.2.4" - -"@types/big.js@^6.1.6": - version "6.2.2" - resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-6.2.2.tgz#69422ec9ef59df1330ccfde2106d9e1159a083c3" - integrity sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA== - -"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": +"@types/bn.js@^5.1.0": version "5.1.5" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== @@ -1984,17 +1021,12 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/long@^4.0.1": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" - integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== - "@types/mocha@^10.0.7": version "10.0.7" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f" integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw== -"@types/node@*", "@types/node@>=13.7.0", "@types/node@^20.8.6": +"@types/node@*", "@types/node@^20.8.6": version "20.11.25" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.25.tgz#0f50d62f274e54dd7a49f7704cc16bfbcccaf49f" integrity sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw== @@ -2011,18 +1043,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== -"@types/promise-retry@^1.1.6": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@types/promise-retry/-/promise-retry-1.1.6.tgz#3c48826d8a27f68f9d4900fc7448f08a1532db44" - integrity sha512-EC1+OMXV0PZb0pf+cmyxc43MEP2CDumZe4AfuxWboxxEixztIebknpJPZAX5XlodGF1OY+C1E/RAeNGzxf+bJA== - dependencies: - "@types/retry" "*" - -"@types/retry@*", "@types/retry@^0.12.5": - version "0.12.5" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.5.tgz#f090ff4bd8d2e5b940ff270ab39fd5ca1834a07e" - integrity sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw== - "@types/ws@^7.4.4": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" @@ -2030,17 +1050,6 @@ dependencies: "@types/node" "*" -"@ubeswap/token-math@^5.1.6", "@ubeswap/token-math@^5.2.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@ubeswap/token-math/-/token-math-5.2.1.tgz#66e70ba8d65b5fdb1b7459332fbdad4ddec32a64" - integrity sha512-wkIKDKIl6rml4CVK3fvjjLVk55Z8qEYTgjxZx7MnrTwECazyhiDuekb9WAaDPXcW5QNffCu8uv4Ba8wE96CJsg== - dependencies: - "@types/big.js" "^6.1.6" - big.js "^6.2.1" - decimal.js-light "^2.5.1" - tiny-invariant "^1.2.0" - tslib "^2.4.0" - "@ungap/promise-all-settled@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" @@ -2071,14 +1080,7 @@ aes-js@3.0.0: resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -agentkeepalive@^4.2.1, agentkeepalive@^4.5.0: +agentkeepalive@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== @@ -2135,11 +1137,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -ansicolors@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" - integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg== - anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -2177,11 +1174,6 @@ arconnect@^0.4.2: dependencies: arweave "^1.10.13" -arg@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" - integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== - argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -2219,17 +1211,6 @@ asn1.js@^5.4.1: minimalistic-assert "^1.0.0" safer-buffer "^2.1.0" -assert@^2.0.0, assert@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" - integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== - dependencies: - call-bind "^1.0.2" - is-nan "^1.3.2" - object-is "^1.1.5" - object.assign "^4.1.4" - util "^0.12.5" - assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" @@ -2242,18 +1223,6 @@ async-retry@^1.3.3: dependencies: retry "0.13.1" -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -available-typed-arrays@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - "avsc@https://github.com/Irys-xyz/avsc#csp-fixes": version "5.4.7" resolved "https://github.com/Irys-xyz/avsc#a730cc8018b79e114b6a3381bbb57760a24c6cef" @@ -2272,23 +1241,6 @@ axios@^0.25.0: dependencies: follow-redirects "^1.14.7" -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== - dependencies: - follow-redirects "^1.14.9" - form-data "^4.0.0" - -axios@^1.2.1: - version "1.6.7" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" - integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== - dependencies: - follow-redirects "^1.15.4" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -2301,11 +1253,6 @@ base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" -base-x@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" - integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== - base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -2321,11 +1268,6 @@ bech32@1.1.4: resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== -big.js@^6.1.1, big.js@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.2.1.tgz#7205ce763efb17c2e41f26f121c420c6a7c2744f" - integrity sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ== - bigint-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" @@ -2333,24 +1275,11 @@ bigint-buffer@^1.1.5: dependencies: bindings "^1.3.0" -bignumber.js@^9.0.0, bignumber.js@^9.0.1, bignumber.js@^9.0.2, bignumber.js@^9.1.1: +bignumber.js@^9.0.0, bignumber.js@^9.0.1, bignumber.js@^9.0.2: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== -binance-api-node@^0.12.0: - version "0.12.7" - resolved "https://registry.yarnpkg.com/binance-api-node/-/binance-api-node-0.12.7.tgz#bce64742d5dc5a9398df3cbd861c486b4d4df075" - integrity sha512-hEIPaZg1YwZClOznAJo5Zb1JyxsqdYjT8twG48rhOwhbNVrLJRxkeGj+PTa881wFXOtyOtyrXsDytsEcI2EUHA== - dependencies: - https-proxy-agent "^5.0.0" - isomorphic-fetch "^3.0.0" - isomorphic-ws "^4.0.1" - json-bigint "^1.0.0" - lodash.zipobject "^4.1.3" - reconnecting-websocket "^4.2.0" - ws "^7.2.0" - binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -2390,33 +1319,21 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -bn-sqrt@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/bn-sqrt/-/bn-sqrt-1.0.0.tgz#aac3fcb56a359850d8339d3925213ac27c22b782" - integrity sha512-XdCMQ7tfEF/f7nrQgnrJ+DLQBwQzSQyPOKIXdUOTcGEvsRKBcIsdfORp7B5H8DWo8FOzZ4+a2TjSZzaqKgzicg== - dependencies: - bn.js "^5.2.0" - bn.js@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -bn.js@5.2.1, bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - bn.js@^4.0.0, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== +bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + borsh@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.6.0.tgz#a7c9eeca6a31ca9e0607cb49f329cb659eb791e1" @@ -2443,13 +1360,6 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -2474,32 +1384,17 @@ bs58@^4.0.0, bs58@^4.0.1: dependencies: base-x "^3.0.2" -bs58@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" - integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== - dependencies: - base-x "^4.0.0" - buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer-layout@1.2.2, buffer-layout@^1.2.0, buffer-layout@^1.2.1, buffer-layout@^1.2.2: +buffer-layout@^1.2.0, buffer-layout@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5" integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== -buffer@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2" - integrity sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -buffer@6.0.3, buffer@^6.0.1, buffer@^6.0.2, buffer@^6.0.3, buffer@~6.0.3: +buffer@6.0.3, buffer@^6.0.2, buffer@^6.0.3, buffer@~6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -2515,28 +1410,12 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bufferutil@^4.0.1, bufferutil@^4.0.6: +bufferutil@^4.0.1: version "4.0.8" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== dependencies: - node-gyp-build "^4.3.0" - -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + node-gyp-build "^4.3.0" camelcase@^6.0.0, camelcase@^6.3.0: version "6.3.0" @@ -2548,12 +1427,7 @@ capability@^0.2.5: resolved "https://registry.yarnpkg.com/capability/-/capability-0.2.5.tgz#51ad87353f1936ffd77f2f21c74633a4dea88801" integrity sha512-rsJZYVCgXd08sPqwmaIqjAd5SUTfonV0z/gDJ8D6cN8wQphky1kkAYEqQ+hmDxTw7UihvBfjUVUSY+DBEe44jg== -case-anything@^2.1.13: - version "2.1.13" - resolved "https://registry.yarnpkg.com/case-anything/-/case-anything-2.1.13.tgz#0cdc16278cb29a7fcdeb072400da3f342ba329e9" - integrity sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng== - -chai@^4.3.4, chai@^4.3.7: +chai@^4.3.4: version "4.4.1" resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== @@ -2574,11 +1448,6 @@ chalk@^4.1.0, chalk@^4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" -chan@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/chan/-/chan-0.6.1.tgz#ec0ad132e5bc62c27ef10ccbfc4d8dcd8ca00640" - integrity sha512-/TdBP2UhbBmw7qnqkzo9Mk4rzvwRv4dlNPXFerqWy90T8oBspKagJNZxrDbExKHhx9uXXHjo3f9mHgs9iKO3nQ== - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -2591,11 +1460,6 @@ check-error@^1.0.3: dependencies: get-func-name "^2.0.2" -check-more-types@2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" - integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== - chokidar@3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -2645,15 +1509,6 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -2671,13 +1526,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - commander@^2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -2693,11 +1541,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -crc@^4.1.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/crc/-/crc-4.3.2.tgz#49b7821cbf2cf61dfd079ed93863bbebd5469b9a" - integrity sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A== - create-hash@^1.1.0, create-hash@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" @@ -2721,11 +1564,6 @@ create-hmac@1.1.7, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cron-validator@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/cron-validator/-/cron-validator-1.3.1.tgz#8f2fe430f92140df77f91178ae31fc1e3a48a20e" - integrity sha512-C1HsxuPCY/5opR55G5/WNzyEGDWFVG+6GLrA+fW/sCTcP6A6NTjUP2AK7B8n2PyFs90kDG2qzwm8LMheADku6A== - cross-fetch@3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" @@ -2740,25 +1578,11 @@ cross-fetch@^3.1.5: dependencies: node-fetch "^2.6.12" -cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - crypto-hash@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247" integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg== -crypto-js@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" - integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== - csv-generate@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-4.4.0.tgz#007575d825e537d9690e687f82fdf4a745e8c226" @@ -2784,13 +1608,6 @@ csv@^6.0.5: csv-stringify "^6.4.6" stream-transform "^3.3.1" -debug@4, debug@4.3.4, debug@^4.3.3, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - debug@4.3.3: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" @@ -2803,17 +1620,7 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -decimal.js-light@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" - integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== - -decimal.js@10.3.1: - version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== - -decimal.js@^10.3.1, decimal.js@^10.4.1, decimal.js@^10.4.3: +decimal.js@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== @@ -2832,34 +1639,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-data-property@^1.0.1, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-properties@^1.1.3, define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - delay@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - depd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -2870,11 +1654,6 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== - diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -2893,28 +1672,6 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -dotenv@10.0.0, dotenv@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - -dotenv@^16.0.1, dotenv@^16.0.3, dotenv@^16.3.1: - version "16.4.5" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" - integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== - -dprint-node@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/dprint-node/-/dprint-node-1.0.8.tgz#a02470722d8208a7d7eb3704328afda1d6758625" - integrity sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg== - dependencies: - detect-libc "^1.0.3" - -duplexer@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - elliptic@6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -2946,11 +1703,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -err-code@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" - integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== - error-polyfill@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/error-polyfill/-/error-polyfill-0.1.3.tgz#df848b61ad8834f7a5db69a70b9913df86721d15" @@ -2960,18 +1712,6 @@ error-polyfill@^0.1.3: o3 "^1.0.3" u3 "^0.1.1" -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -3092,39 +1832,11 @@ ethers@^5.5.1: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -event-stream@=3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" - integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== - dependencies: - duplexer "~0.1.1" - from "~0" - map-stream "~0.1.0" - pause-stream "0.0.11" - split "0.3" - stream-combiner "~0.0.4" - through "~2.3.1" - eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -execa@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - exponential-backoff@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" @@ -3176,44 +1888,16 @@ find-up@5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -find@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8" - integrity sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw== - dependencies: - traverse-chain "~0.1.0" - flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -follow-redirects@^1.14.0, follow-redirects@^1.14.7, follow-redirects@^1.14.9, follow-redirects@^1.15.4: +follow-redirects@^1.14.0, follow-redirects@^1.14.7: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -from@~0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" - integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3224,11 +1908,6 @@ fsevents@~2.3.2, fsevents@~2.3.3: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -3239,22 +1918,6 @@ get-func-name@^2.0.1, get-func-name@^2.0.2: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - get-tsconfig@^4.7.2: version "4.7.3" resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.3.tgz#0498163d98f7b58484dd4906999c0c9d5f103f83" @@ -3281,24 +1944,6 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.3: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" @@ -3309,30 +1954,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0, has-tostringtag@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - hash-base@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" @@ -3350,13 +1971,6 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hasown@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa" - integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== - dependencies: - function-bind "^1.1.2" - he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -3387,19 +2001,6 @@ http-errors@^1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.1" -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -3453,21 +2054,6 @@ inquirer@^8.2.0: through "^2.3.6" wrap-ansi "^6.0.1" -invariant@2.2.4, invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - -is-arguments@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -3475,11 +2061,6 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-callable@^1.1.3: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -3490,13 +2071,6 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -3509,14 +2083,6 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-nan@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" - integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -3527,18 +2093,6 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-typed-array@^1.1.3: - version "1.1.13" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" - integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== - dependencies: - which-typed-array "^1.1.14" - is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" @@ -3549,38 +2103,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isomorphic-fetch@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" - integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== - dependencies: - node-fetch "^2.6.1" - whatwg-fetch "^3.4.1" - isomorphic-ws@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== -jayson@^3.4.4: - version "3.7.0" - resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.7.0.tgz#b735b12d06d348639ae8230d7a1e2916cb078f25" - integrity sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ== - dependencies: - "@types/connect" "^3.4.33" - "@types/node" "^12.12.54" - "@types/ws" "^7.4.4" - JSONStream "^1.3.5" - commander "^2.20.3" - delay "^5.0.0" - es6-promisify "^5.0.0" - eyes "^0.1.8" - isomorphic-ws "^4.0.1" - json-stringify-safe "^5.0.1" - lodash "^4.17.20" - uuid "^8.3.2" - ws "^7.4.5" - jayson@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.0.tgz#60dc946a85197317f2b1439d672a8b0a99cea2f9" @@ -3599,17 +2126,6 @@ jayson@^4.1.0: uuid "^8.3.2" ws "^7.4.5" -joi@^17.7.0: - version "17.12.2" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.12.2.tgz#283a664dabb80c7e52943c557aab82faea09f521" - integrity sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw== - dependencies: - "@hapi/hoek" "^9.3.0" - "@hapi/topo" "^5.1.0" - "@sideway/address" "^4.1.5" - "@sideway/formula" "^3.0.1" - "@sideway/pinpoint" "^2.0.0" - js-sha256@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" @@ -3625,11 +2141,6 @@ js-sha512@^0.8.0: resolved "https://registry.yarnpkg.com/js-sha512/-/js-sha512-0.8.0.tgz#dd22db8d02756faccf19f218e3ed61ec8249f7d4" integrity sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ== -"js-tokens@^3.0.0 || ^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - js-yaml@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -3637,11 +2148,6 @@ js-yaml@4.1.0: dependencies: argparse "^2.0.1" -jsbi@4.3.0, jsbi@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-4.3.0.tgz#b54ee074fb6fcbc00619559305c8f7e912b04741" - integrity sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g== - json-bigint@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" @@ -3661,11 +2167,6 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -jsonc-parser@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" - integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== - jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -3680,16 +2181,6 @@ keccak@^3.0.2: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -lazy-ass@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" - integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== - locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -3697,22 +2188,7 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== - -lodash.mapvalues@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" - integrity sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ== - -lodash.zipobject@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/lodash.zipobject/-/lodash.zipobject-4.1.3.tgz#b399f5aba8ff62a746f6979bf20b214f964dbef8" - integrity sha512-A9SzX4hMKWS25MyalwcOnNoplyHbkNVsjidhTp8ru0Sj23wY9GWBKS8gAIGDSAqeWjIjvE4KBEl24XXAs+v4wQ== - -lodash@^4.17.20, lodash@^4.17.21: +lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3725,23 +2201,6 @@ log-symbols@4.1.0, log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -long@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - -long@^5.0.0, long@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" - integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== - -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - loupe@^2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" @@ -3756,26 +2215,11 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lunr@^2.3.9: - version "2.3.9" - resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" - integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== - make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -map-stream@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" - integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== - -marked@^4.0.16: - version "4.3.0" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" - integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -3785,17 +2229,12 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.34: +mime-types@^2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -3831,14 +2270,7 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1, minimatch@^5.1.0: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.7: +minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -3962,52 +2394,28 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-fetch@2, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.7.0: +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.8.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== -node-kraken-api@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/node-kraken-api/-/node-kraken-api-2.2.2.tgz#28bf56eec1f0ffe7e784b2ffa29b360ed98b0e4c" - integrity sha512-f+BZpgT1gD9705hlsRDDGj9m96Psb0Gxu3Td/P2fs0/gFy58YQONrTPiqfYzOBxvpmYnuAMxCRuRmdmkw04eRw== - dependencies: - crc "^4.1.0" - ts-ev "^0.4.0" - ws "^8.5.0" - optionalDependencies: - bufferutil "^4.0.6" - utf-8-validate "^5.0.9" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -numeral@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" - integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== - o3@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/o3/-/o3-1.0.3.tgz#192ce877a882dfa6751f0412a865fafb2da1dac0" @@ -4015,29 +2423,6 @@ o3@^1.0.3: dependencies: capability "^0.2.5" -object-is@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" - integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.4: - version "4.1.5" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" - integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== - dependencies: - call-bind "^1.0.5" - define-properties "^1.2.1" - has-symbols "^1.0.3" - object-keys "^1.1.1" - once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -4045,7 +2430,7 @@ once@^1.3.0, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0, onetime@^5.1.2: +onetime@^5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -4101,23 +2486,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - pathval@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== -pause-stream@0.0.11: - version "0.0.11" - resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" - integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== - dependencies: - through "~2.3" - pbkdf2@^3.0.9: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" @@ -4134,11 +2507,6 @@ picomatch@^2.0.4, picomatch@^2.2.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -possible-typed-array-names@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" - integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== - prettier@^2.6.2: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" @@ -4149,87 +2517,6 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -promise-retry@2.0.1, promise-retry@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" - integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== - dependencies: - err-code "^2.0.2" - retry "^0.12.0" - -protobufjs@^6.10.2: - version "6.11.4" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" - integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" - "@types/node" ">=13.7.0" - long "^4.0.0" - -protobufjs@^7.2.4: - version "7.2.6" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" - integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/node" ">=13.7.0" - long "^5.0.0" - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -ps-tree@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" - integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== - dependencies: - event-stream "=3.3.4" - -psyfi-euros-test@0.0.2-rc.5: - version "0.0.2-rc.5" - resolved "https://registry.yarnpkg.com/psyfi-euros-test/-/psyfi-euros-test-0.0.2-rc.5.tgz#7b0db21ecbe7bac490de764f022feb01a9b501d9" - integrity sha512-bTeM4wrS9x6+9Z69uN9wWELTc7MyM3KpNdntp9T3uUkQwnN9Pygwjd/a+Mf/x7PbmAHKMabY7tLbSVxpifVt4g== - dependencies: - "@metaplex-foundation/mpl-token-metadata" "2.2.4" - "@project-serum/anchor" "0.23.0" - "@project-serum/common" "^0.0.1-beta.3" - "@project-serum/serum" "^0.13.61" - "@solana/spl-token" "0.1.8" - "@solana/web3.js" "^1.35.1" - psystake-test "0.0.1-rc.8" - -psystake-test@0.0.1-rc.8: - version "0.0.1-rc.8" - resolved "https://registry.yarnpkg.com/psystake-test/-/psystake-test-0.0.1-rc.8.tgz#67ce3c7546c47ac44bd213997fadcee346efe440" - integrity sha512-wng85jJDM8SwVeH/6fUMKBxHiVPl6SwFm6Y1j3fNmhqEINwu1L+kShO3YCIhPN7oK2tox7Fb7tUrXdbk5HQ54g== - dependencies: - "@project-serum/anchor" "^0.23.0" - "@project-serum/common" "^0.0.1-beta.3" - "@project-serum/serum" "^0.13.61" - "@solana/spl-token" "^0.1.8" - "@solana/web3.js" "^1.35.1" - randombytes@^2.0.1, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -4253,11 +2540,6 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -reconnecting-websocket@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783" - integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng== - regenerator-runtime@^0.14.0: version "0.14.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" @@ -4281,16 +2563,11 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" -retry@0.13.1, retry@^0.13.1: +retry@0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== - ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -4299,7 +2576,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rpc-websockets@^7.4.12, rpc-websockets@^7.5.0, rpc-websockets@^7.5.1: +rpc-websockets@^7.5.1: version "7.9.0" resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.9.0.tgz#a3938e16d6f134a3999fdfac422a503731bf8973" integrity sha512-DwKewQz1IUA5wfLvgM8wDpPRcr+nWSxuFxx5CbrI2z/MyyZ4nXLM86TvIA+cI1ZAdqC8JIBR1mZR55dzaLU+Hw== @@ -4317,7 +2594,7 @@ run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== -rxjs@^7.5.5, rxjs@^7.8.0: +rxjs@^7.5.5: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== @@ -4355,18 +2632,6 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -4380,28 +2645,7 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shiki@^0.10.1: - version "0.10.1" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.10.1.tgz#6f9a16205a823b56c072d0f1a0bcd0f2646bef14" - integrity sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng== - dependencies: - jsonc-parser "^3.0.0" - vscode-oniguruma "^1.6.1" - vscode-textmate "5.2.0" - -signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -4474,50 +2718,17 @@ spl-token-bankrun@0.2.6: "@solana/spl-token" "^0.3.8" solana-bankrun "^0.3.0" -split@0.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" - integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== - dependencies: - through "2" - -start-server-and-test@^1.14.0: - version "1.15.5" - resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.15.5.tgz#5c9103bd87c06678fc62658fbe97d09501714011" - integrity sha512-o3EmkX0++GV+qsvIJ/OKWm3w91fD8uS/bPQVPrh/7loaxkpXSuAIHdnmN/P/regQK9eNAK76aBJcHt+OSTk+nA== - dependencies: - arg "^5.0.2" - bluebird "3.7.2" - check-more-types "2.24.0" - debug "4.3.4" - execa "5.1.1" - lazy-ass "1.6.0" - ps-tree "1.2.0" - wait-on "7.0.1" - "statuses@>= 1.5.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -stream-combiner@~0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" - integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== - dependencies: - duplexer "~0.1.1" - stream-transform@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-3.3.1.tgz#f8bb6a811e505056b6a215c466e4c2760347189f" integrity sha512-BL8pv9QL8Ikd11oZwlRDp1qYMhGR0i50zI9ltoijKGc4ubQWal/Rc4p6SYJp1TBOGpE0uAGchwbxOZ1ycwTuqQ== -strict-event-emitter-types@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz#05e15549cb4da1694478a53543e4e2f4abcf277f" - integrity sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA== - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4545,24 +2756,11 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - strip-json-comments@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -superstruct@0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.8.3.tgz#fb4d8901aca3bf9f79afab1bbab7a7f335cc4ef2" - integrity sha512-LbtbFpktW1FcwxVIJlxdk7bCyBq/GzOx2FSFLRLTUhWIA1gHkYPIl3aXRG5mBdGZtnPNT6t+4eEcLDCMOuBHww== - dependencies: - kind-of "^6.0.2" - tiny-invariant "^1.0.6" - superstruct@^0.14.2: version "0.14.2" resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" @@ -4573,11 +2771,6 @@ superstruct@^0.15.4: resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.15.5.tgz#0f0a8d3ce31313f0d84c6096cd4fa1bfdedc9dab" integrity sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ== -superstruct@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.3.tgz#de626a5b49c6641ff4d37da3c7598e7a87697046" - integrity sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg== - supports-color@8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" @@ -4597,16 +2790,11 @@ text-encoding-utf-8@^1.0.2: resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== -through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1: +"through@>=2.2.7 <3", through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -tiny-invariant@^1.0.6, tiny-invariant@^1.1.0, tiny-invariant@^1.2.0, tiny-invariant@^1.3.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" - integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== - tmp-promise@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7" @@ -4633,11 +2821,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toformat@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8" - integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ== - toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -4653,16 +2836,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -traverse-chain@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" - integrity sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg== - -ts-ev@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/ts-ev/-/ts-ev-0.4.0.tgz#b30bbab35bd57516efba7ab89b6417424a1ebf0e" - integrity sha512-rLX6QdkC1/jA9sS4y9/DxHABTcOussp33J90h+TxHmya9CWvbGc9uLqdM4c/N4pNRmSdtq9zqhz7sB9KcN1NFQ== - ts-mocha@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/ts-mocha/-/ts-mocha-10.0.0.tgz#41a8d099ac90dbbc64b06976c5025ffaebc53cb9" @@ -4686,31 +2859,6 @@ ts-node@7.0.1: source-map-support "^0.5.6" yn "^2.0.0" -ts-poet@^6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/ts-poet/-/ts-poet-6.7.0.tgz#6b2ff3b7b0c70ea650d6d570dfe6899f73fb3c38" - integrity sha512-A0wvFtpkTCWPw7ftTIwbEH+L+7ul4CU0x3jXKQ+kCnmEQIAOwhpUaBmcAYKxZCxHae9/MUl4LbyTqw25BpzW5Q== - dependencies: - dprint-node "^1.0.8" - -ts-proto-descriptors@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/ts-proto-descriptors/-/ts-proto-descriptors-1.15.0.tgz#e859e3a2887da2d954c552524719b80bdb6ee355" - integrity sha512-TYyJ7+H+7Jsqawdv+mfsEpZPTIj9siDHS6EMCzG/z3b/PZiphsX+mWtqFfFVe5/N0Th6V3elK9lQqjnrgTOfrg== - dependencies: - long "^5.2.3" - protobufjs "^7.2.4" - -ts-proto@^1.79.0: - version "1.168.0" - resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.168.0.tgz#049895ec546bf729dd5d927866aa967e1fb96257" - integrity sha512-3ae1eXY3VCmhIvM/8Q/q4/3bJrdftmtY+5GwrSkhZh+UnHpI1l5rYu0mm1V75qiQJcdAXRc5m7gfnYbdnuQl5g== - dependencies: - case-anything "^2.1.13" - protobufjs "^7.2.4" - ts-poet "^6.7.0" - ts-proto-descriptors "1.15.0" - tsconfig-paths@^3.5.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -4721,7 +2869,7 @@ tsconfig-paths@^3.5.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.2: +tslib@^2.0.3, tslib@^2.1.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== @@ -4751,18 +2899,7 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -typedoc@^0.22.15: - version "0.22.18" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.22.18.tgz#1d000c33b66b88fd8cdfea14a26113a83b7e6591" - integrity sha512-NK9RlLhRUGMvc6Rw5USEYgT4DVAUFk7IF7Q6MYfpJ88KnTZP7EneEa4RcP+tX1auAcz7QT1Iy0bUSZBYYHdoyA== - dependencies: - glob "^8.0.3" - lunr "^2.3.9" - marked "^4.0.16" - minimatch "^5.1.0" - shiki "^0.10.1" - -typescript@^4.2.4, typescript@^4.3.5: +typescript@^4.3.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -4777,7 +2914,7 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -utf-8-validate@^5.0.2, utf-8-validate@^5.0.9: +utf-8-validate@^5.0.2: version "5.0.10" resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== @@ -4789,17 +2926,6 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -util@^0.12.5: - version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" - integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - which-typed-array "^1.1.2" - uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" @@ -4810,27 +2936,6 @@ vlq@^2.0.4: resolved "https://registry.yarnpkg.com/vlq/-/vlq-2.0.4.tgz#6057b85729245b9829e3cc7755f95b228d4fe041" integrity sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA== -vscode-oniguruma@^1.6.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" - integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== - -vscode-textmate@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" - integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== - -wait-on@7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-7.0.1.tgz#5cff9f8427e94f4deacbc2762e6b0a489b19eae9" - integrity sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog== - dependencies: - axios "^0.27.2" - joi "^17.7.0" - lodash "^4.17.21" - minimist "^1.2.7" - rxjs "^7.8.0" - wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -4843,11 +2948,6 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -whatwg-fetch@^3.4.1: - version "3.6.20" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" - integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -4856,18 +2956,7 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -which-typed-array@^1.1.14, which-typed-array@^1.1.2: - version "1.1.14" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.14.tgz#1f78a111aee1e131ca66164d8bdc3ab062c95a06" - integrity sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg== - dependencies: - available-typed-arrays "^1.0.6" - call-bind "^1.0.5" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.1" - -which@2.0.2, which@^2.0.1: +which@2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -4907,7 +2996,7 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@^7.2.0, ws@^7.4.5, ws@^7.4.6: +ws@^7.4.5: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== @@ -4922,11 +3011,6 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yaml@^2.2.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed" - integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg== - yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" @@ -4937,11 +3021,6 @@ yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" @@ -4965,19 +3044,6 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.0.1: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" From dc6c57ab4622edf8ab58203e528c23b4d564d10c Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 34/52] Add autocrat tests --- tests/autocrat/autocrat.ts | 13 +++++++------ tests/main.test.ts | 4 +++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/autocrat/autocrat.ts b/tests/autocrat/autocrat.ts index e9867b59..14ae10db 100644 --- a/tests/autocrat/autocrat.ts +++ b/tests/autocrat/autocrat.ts @@ -20,10 +20,10 @@ import { getAccount, } from "spl-token-bankrun"; -import { advanceBySlots, expectError } from "../../utils"; -import { Autocrat, IDL as AutocratIDL } from "../../../target/types/autocrat"; -import { ConditionalVault, IDL as ConditionalVaultIDL } from "../../../target/types/conditional_vault"; -import { AutocratMigrator, IDL as AutocratMigratorIDL } from "../../../target/types/autocrat_migrator"; +import { advanceBySlots, expectError } from "../utils"; +import { Autocrat, IDL as AutocratIDL } from "../../target/types/autocrat"; +import { ConditionalVault, IDL as ConditionalVaultIDL } from "../../target/types/conditional_vault"; +import { AutocratMigrator, IDL as AutocratMigratorIDL } from "../../target/types/autocrat_migrator"; const { PublicKey, Keypair } = anchor.web3; @@ -67,7 +67,8 @@ const AUTOCRAT_MIGRATOR_PROGRAM_ID = new PublicKey( const ONE_META = new BN(1_000_000_000); const ONE_USDC = new BN(1_000_000); -describe("autocrat", async function () { +// describe("autocrat", async function () { +export default function suite() { let provider, autocrat, payer, @@ -688,4 +689,4 @@ describe("autocrat", async function () { .then(callbacks[0], callbacks[1]); }); }); -}); +} diff --git a/tests/main.test.ts b/tests/main.test.ts index 6cc508f8..79bc5186 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -1,5 +1,6 @@ import conditionalVault from "./conditionalVault/main.test"; import amm from "./amm/main.test"; +import autocrat from "./autocrat/autocrat"; import { startAnchor } from "solana-bankrun"; import { BankrunProvider } from "anchor-bankrun"; @@ -72,4 +73,5 @@ before(async function () { }); describe("conditional_vault", conditionalVault); -describe("amm", amm); \ No newline at end of file +describe("amm", amm); +describe("autocrat", autocrat); \ No newline at end of file From 9c50fc3bd4c2dc5b602a2f58e8931841412a02c7 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 35/52] Migrate autocrat to new version of conditional vault --- .../src/instructions/finalize_proposal.rs | 69 ++-- .../src/instructions/initialize_proposal.rs | 17 +- programs/autocrat/src/lib.rs | 7 +- programs/autocrat/src/state/proposal.rs | 1 + programs/conditional_vault/src/lib.rs | 14 +- run.sh | 2 +- sdk/src/AutocratClient.ts | 336 ++++++++++-------- sdk/src/types/autocrat.ts | 32 +- sdk/src/types/conditional_vault.ts | 46 --- tests/autocrat/autocrat.ts | 44 ++- 10 files changed, 300 insertions(+), 268 deletions(-) diff --git a/programs/autocrat/src/instructions/finalize_proposal.rs b/programs/autocrat/src/instructions/finalize_proposal.rs index 659642fc..28475253 100644 --- a/programs/autocrat/src/instructions/finalize_proposal.rs +++ b/programs/autocrat/src/instructions/finalize_proposal.rs @@ -1,10 +1,12 @@ +use conditional_vault::{ResolveQuestionArgs, cpi::accounts::ResolveQuestion}; + use super::*; #[derive(Accounts)] pub struct FinalizeProposal<'info> { #[account(mut, - has_one = base_vault, - has_one = quote_vault, + // has_one = base_vault, + // has_one = quote_vault, has_one = pass_amm, has_one = fail_amm, has_one = dao, @@ -15,9 +17,11 @@ pub struct FinalizeProposal<'info> { #[account(has_one = treasury)] pub dao: Box>, #[account(mut)] - pub base_vault: Box>, - #[account(mut)] - pub quote_vault: Box>, + pub question: Account<'info, Question>, + // #[account(mut)] + // pub base_vault: Box>, + // #[account(mut)] + // pub quote_vault: Box>, /// CHECK: never read pub treasury: UncheckedAccount<'info>, #[account( @@ -71,8 +75,9 @@ impl FinalizeProposal<'_> { pass_amm, fail_amm, dao, - base_vault, - quote_vault, + question, + // base_vault, + // quote_vault, treasury, pass_lp_user_account, fail_lp_user_account, @@ -147,38 +152,40 @@ impl FinalizeProposal<'_> { .saturating_mul(MAX_BPS.saturating_add(dao.pass_threshold_bps).into()) / MAX_BPS as u128; - let (new_proposal_state, new_vault_state) = if pass_market_twap > threshold { - (ProposalState::Passed, VaultStatus::Finalized) + let (new_proposal_state, payout_numerators) = if pass_market_twap > threshold { + (ProposalState::Passed, vec![0, 1]) } else { - (ProposalState::Failed, VaultStatus::Reverted) + (ProposalState::Failed, vec![1, 0]) }; proposal.state = new_proposal_state; - for vault in [base_vault.to_account_info(), quote_vault.to_account_info()] { + // for vault in [base_vault.to_account_info(), quote_vault.to_account_info()] { let vault_program = vault_program.to_account_info(); - let cpi_accounts = SettleConditionalVault { - settlement_authority: proposal.to_account_info(), - vault, + let cpi_accounts = ResolveQuestion { + question: question.to_account_info(), + oracle: proposal.to_account_info(), }; let cpi_ctx = CpiContext::new(vault_program, cpi_accounts).with_signer(proposal_signer); - conditional_vault::cpi::settle_conditional_vault(cpi_ctx, new_vault_state)?; - } - - base_vault.reload()?; - quote_vault.reload()?; - - match new_proposal_state { - ProposalState::Passed => { - assert!(base_vault.status == VaultStatus::Finalized); - assert!(quote_vault.status == VaultStatus::Finalized); - } - ProposalState::Failed => { - assert!(base_vault.status == VaultStatus::Reverted); - assert!(quote_vault.status == VaultStatus::Reverted); - } - _ => unreachable!("Encountered an unexpected proposal state"), - } + conditional_vault::cpi::resolve_question(cpi_ctx, ResolveQuestionArgs { + payout_numerators, + })?; + // } + + // base_vault.reload()?; + // quote_vault.reload()?; + + // match new_proposal_state { + // ProposalState::Passed => { + // assert!(base_vault.status == VaultStatus::Finalized); + // assert!(quote_vault.status == VaultStatus::Finalized); + // } + // ProposalState::Failed => { + // assert!(base_vault.status == VaultStatus::Reverted); + // assert!(quote_vault.status == VaultStatus::Reverted); + // } + // _ => unreachable!("Encountered an unexpected proposal state"), + // } Ok(()) } diff --git a/programs/autocrat/src/instructions/initialize_proposal.rs b/programs/autocrat/src/instructions/initialize_proposal.rs index 4c57dd05..aaa227eb 100644 --- a/programs/autocrat/src/instructions/initialize_proposal.rs +++ b/programs/autocrat/src/instructions/initialize_proposal.rs @@ -25,19 +25,22 @@ pub struct InitializeProposal<'info> { pub proposal: Box>, #[account(mut)] pub dao: Box>, + pub question: Box>, #[account( constraint = quote_vault.underlying_token_mint == dao.usdc_mint, - constraint = quote_vault.settlement_authority == proposal.key() @ AutocratError::InvalidSettlementAuthority, + has_one = question, + // constraint = quote_vault.settlement_authority == proposal.key() @ AutocratError::InvalidSettlementAuthority, )] pub quote_vault: Account<'info, ConditionalVaultAccount>, #[account( constraint = base_vault.underlying_token_mint == dao.token_mint, - constraint = base_vault.settlement_authority == proposal.key() @ AutocratError::InvalidSettlementAuthority, + has_one = question, + // constraint = base_vault.settlement_authority == proposal.key() @ AutocratError::InvalidSettlementAuthority, )] pub base_vault: Account<'info, ConditionalVaultAccount>, #[account( - constraint = pass_amm.base_mint == base_vault.conditional_on_finalize_token_mint, - constraint = pass_amm.quote_mint == quote_vault.conditional_on_finalize_token_mint, + constraint = pass_amm.base_mint == base_vault.conditional_token_mints[1], + constraint = pass_amm.quote_mint == quote_vault.conditional_token_mints[1], )] pub pass_amm: Box>, #[account(constraint = pass_amm.lp_mint == pass_lp_mint.key())] @@ -45,8 +48,8 @@ pub struct InitializeProposal<'info> { #[account(constraint = fail_amm.lp_mint == fail_lp_mint.key())] pub fail_lp_mint: Account<'info, Mint>, #[account( - constraint = fail_amm.base_mint == base_vault.conditional_on_revert_token_mint, - constraint = fail_amm.quote_mint == quote_vault.conditional_on_revert_token_mint, + constraint = fail_amm.base_mint == base_vault.conditional_token_mints[0], + constraint = fail_amm.quote_mint == quote_vault.conditional_token_mints[0], )] pub fail_amm: Box>, #[account( @@ -110,6 +113,7 @@ impl InitializeProposal<'_> { let Self { base_vault, quote_vault, + question, proposal, dao, pass_amm, @@ -210,6 +214,7 @@ impl InitializeProposal<'_> { fail_lp_tokens_locked: fail_lp_tokens_to_lock, nonce, pda_bump: ctx.bumps.proposal, + question: question.key(), }); Ok(()) diff --git a/programs/autocrat/src/lib.rs b/programs/autocrat/src/lib.rs index 60d0ac21..007072ad 100644 --- a/programs/autocrat/src/lib.rs +++ b/programs/autocrat/src/lib.rs @@ -26,10 +26,11 @@ use anchor_lang::prelude::*; use anchor_lang::solana_program; use anchor_spl::token::{self, Mint, Token, TokenAccount, Transfer}; -use conditional_vault::cpi::accounts::SettleConditionalVault; +// use conditional_vault::cpi::accounts::SettleConditionalVault; use conditional_vault::program::ConditionalVault as ConditionalVaultProgram; -use conditional_vault::ConditionalVault as ConditionalVaultAccount; -use conditional_vault::VaultStatus; +use conditional_vault::NewConditionalVault as ConditionalVaultAccount; +use conditional_vault::Question; +// use conditional_vault::VaultStatus; pub mod error; pub mod instructions; diff --git a/programs/autocrat/src/state/proposal.rs b/programs/autocrat/src/state/proposal.rs index 27d7e5a0..f3438df5 100644 --- a/programs/autocrat/src/state/proposal.rs +++ b/programs/autocrat/src/state/proposal.rs @@ -43,6 +43,7 @@ pub struct Proposal { /// the math :D pub nonce: u64, pub pda_bump: u8, + pub question: Pubkey, } impl From<&ProposalInstruction> for Instruction { diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index 2bf3c565..b045627a 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -99,13 +99,13 @@ pub mod conditional_vault { AddMetadataToConditionalTokens::handle(ctx, args) } - #[access_control(ctx.accounts.validate())] - pub fn settle_conditional_vault( - ctx: Context, - new_status: VaultStatus, - ) -> Result<()> { - SettleConditionalVault::handle(ctx, new_status) - } + // #[access_control(ctx.accounts.validate())] + // pub fn settle_conditional_vault( + // ctx: Context, + // new_status: VaultStatus, + // ) -> Result<()> { + // SettleConditionalVault::handle(ctx, new_status) + // } #[access_control(ctx.accounts.validate_merge_conditional_tokens())] pub fn merge_conditional_tokens_for_underlying_tokens( diff --git a/run.sh b/run.sh index 72506cc5..325dea79 100755 --- a/run.sh +++ b/run.sh @@ -5,7 +5,7 @@ build() { } test() { - find programs tests sdk | entr -sc '(cd sdk && yarn build) && RUST_LOG= anchor test' + find programs tests sdk | entr -sc 'anchor build && (cd sdk && yarn build) && RUST_LOG= anchor test --skip-build' } build_vault() { diff --git a/sdk/src/AutocratClient.ts b/sdk/src/AutocratClient.ts index a285330f..56e9644a 100644 --- a/sdk/src/AutocratClient.ts +++ b/sdk/src/AutocratClient.ts @@ -32,8 +32,10 @@ import { MaxCUs, getAmmAddr, getAmmLpMintAddr, + getConditionalTokenMintAddr, getDaoTreasuryAddr, getProposalAddr, + getQuestionAddr, getVaultAddr, getVaultFinalizeMintAddr, getVaultRevertMintAddr, @@ -45,6 +47,7 @@ import { getAssociatedTokenAddressSync, unpackMint, } from "@solana/spl-token"; +import { sha256 } from "@noble/hashes/sha256"; export type CreateClientParams = { provider: AnchorProvider; @@ -121,6 +124,7 @@ export class AutocratClient { quoteMint: PublicKey, dao: PublicKey ): { + question: PublicKey; baseVault: PublicKey; quoteVault: PublicKey; passBaseMint: PublicKey; @@ -133,26 +137,45 @@ export class AutocratClient { failLp: PublicKey; } { let vaultProgramId = this.vaultClient.vaultProgram.programId; + const [question] = getQuestionAddr( + vaultProgramId, + sha256(`Will ${proposal} pass?/FAIL/PASS`), + proposal, + 2 + ); const [daoTreasury] = getDaoTreasuryAddr(this.autocrat.programId, dao); const [baseVault] = getVaultAddr( this.vaultClient.vaultProgram.programId, - proposal, + question, baseMint ); const [quoteVault] = getVaultAddr( this.vaultClient.vaultProgram.programId, - proposal, + question, quoteMint ); - const [passBaseMint] = getVaultFinalizeMintAddr(vaultProgramId, baseVault); - const [passQuoteMint] = getVaultFinalizeMintAddr( + const [failBaseMint] = getConditionalTokenMintAddr( + vaultProgramId, + baseVault, + 0 + ); + const [failQuoteMint] = getConditionalTokenMintAddr( vaultProgramId, - quoteVault + quoteVault, + 0 ); - const [failBaseMint] = getVaultRevertMintAddr(vaultProgramId, baseVault); - const [failQuoteMint] = getVaultRevertMintAddr(vaultProgramId, quoteVault); + const [passBaseMint] = getConditionalTokenMintAddr( + vaultProgramId, + baseVault, + 1 + ); + const [passQuoteMint] = getConditionalTokenMintAddr( + vaultProgramId, + quoteVault, + 1 + ); const [passAmm] = getAmmAddr( this.ammClient.program.programId, @@ -175,6 +198,7 @@ export class AutocratClient { ); return { + question, baseVault, quoteVault, passBaseMint, @@ -274,6 +298,12 @@ export class AutocratClient { nonce ); + await this.vaultClient.initializeQuestion( + sha256(`Will ${proposal} pass?/FAIL/PASS`), + proposal, + 2 + ); + const { baseVault, quoteVault, @@ -283,6 +313,7 @@ export class AutocratClient { passQuoteMint, failBaseMint, failQuoteMint, + question, } = this.getProposalPdas( proposal, storedDao.tokenMint, @@ -292,10 +323,14 @@ export class AutocratClient { // it's important that these happen in a single atomic transaction await this.vaultClient - .initializeVaultIx(proposal, storedDao.tokenMint) + .initializeNewVaultIx(question, storedDao.tokenMint, 2) .postInstructions( await InstructionUtils.getInstructions( - this.vaultClient.initializeVaultIx(proposal, storedDao.usdcMint), + this.vaultClient.initializeNewVaultIx( + question, + storedDao.usdcMint, + 2 + ), this.ammClient.createAmmIx( passBaseMint, passQuoteMint, @@ -313,13 +348,21 @@ export class AutocratClient { .rpc(); await this.vaultClient - .mintConditionalTokensIx(baseVault, storedDao.tokenMint, baseTokensToLP) + .splitTokensIx( + question, + baseVault, + storedDao.tokenMint, + baseTokensToLP, + 2 + ) .postInstructions( await InstructionUtils.getInstructions( - this.vaultClient.mintConditionalTokensIx( + this.vaultClient.splitTokensIx( + question, quoteVault, storedDao.usdcMint, - quoteTokensToLP + quoteTokensToLP, + 2 ) ) ) @@ -359,139 +402,141 @@ export class AutocratClient { storedDao.usdcMint, lpTokens, lpTokens, - nonce + nonce, + question ).rpc(); return proposal; } - async createProposalTxAndPDAs( - dao: PublicKey, - descriptionUrl: string, - instruction: ProposalInstruction, - baseTokensToLP: BN, - quoteTokensToLP: BN - ): Promise< - [ - Transaction[], - { - proposalAcct: PublicKey; - baseCondVaultAcct: PublicKey; - quoteCondVaultAcct: PublicKey; - passMarketAcct: PublicKey; - failMarketAcct: PublicKey; - } - ] - > { - const storedDao = await this.getDao(dao); - - const nonce = new BN(Math.random() * 2 ** 50); - - let [proposal] = getProposalAddr( - this.autocrat.programId, - this.provider.publicKey, - nonce - ); - - const { - baseVault, - quoteVault, - passAmm, - failAmm, - passBaseMint, - passQuoteMint, - failBaseMint, - failQuoteMint, - } = this.getProposalPdas( - proposal, - storedDao.tokenMint, - storedDao.usdcMint, - dao - ); - - // it's important that these happen in a single atomic transaction - const initVaultTx = await this.vaultClient - .initializeVaultIx(proposal, storedDao.tokenMint) - .postInstructions( - await InstructionUtils.getInstructions( - this.vaultClient.initializeVaultIx(proposal, storedDao.usdcMint), - this.ammClient.createAmmIx( - passBaseMint, - passQuoteMint, - storedDao.twapInitialObservation, - storedDao.twapMaxObservationChangePerUpdate - ), - this.ammClient.createAmmIx( - failBaseMint, - failQuoteMint, - storedDao.twapInitialObservation, - storedDao.twapMaxObservationChangePerUpdate - ) - ) - ) - .transaction(); - - const mintConditionalTokensTx = await this.vaultClient - .mintConditionalTokensIx(baseVault, storedDao.tokenMint, baseTokensToLP) - .postInstructions( - await InstructionUtils.getInstructions( - this.vaultClient.mintConditionalTokensIx( - quoteVault, - storedDao.usdcMint, - quoteTokensToLP - ) - ) - ) - .transaction(); - - const addLiquidityTx = await this.ammClient - .addLiquidityIx( - passAmm, - passBaseMint, - passQuoteMint, - quoteTokensToLP, - baseTokensToLP, - new BN(0) - ) - .postInstructions( - await InstructionUtils.getInstructions( - this.ammClient.addLiquidityIx( - failAmm, - failBaseMint, - failQuoteMint, - quoteTokensToLP, - baseTokensToLP, - new BN(0) - ) - ) - ) - .transaction(); - - // this is how many original tokens are created - const lpTokens = quoteTokensToLP; - - const initTx = await this.initializeProposalIx( - descriptionUrl, - instruction, - dao, - storedDao.tokenMint, - storedDao.usdcMint, - lpTokens, - lpTokens, - nonce - ).transaction(); - - return [ - [initVaultTx, mintConditionalTokensTx, addLiquidityTx, initTx], - { - baseCondVaultAcct: baseVault, - quoteCondVaultAcct: quoteVault, - failMarketAcct: failAmm, - passMarketAcct: passAmm, - proposalAcct: proposal, - }, - ]; - } + // async createProposalTxAndPDAs( + // dao: PublicKey, + // descriptionUrl: string, + // instruction: ProposalInstruction, + // baseTokensToLP: BN, + // quoteTokensToLP: BN + // ): Promise< + // [ + // Transaction[], + // { + // proposalAcct: PublicKey; + // baseCondVaultAcct: PublicKey; + // quoteCondVaultAcct: PublicKey; + // passMarketAcct: PublicKey; + // failMarketAcct: PublicKey; + // } + // ] + // > { + // const storedDao = await this.getDao(dao); + + // const nonce = new BN(Math.random() * 2 ** 50); + + // let [proposal] = getProposalAddr( + // this.autocrat.programId, + // this.provider.publicKey, + // nonce + // ); + + // const { + // baseVault, + // quoteVault, + // passAmm, + // failAmm, + // passBaseMint, + // passQuoteMint, + // failBaseMint, + // failQuoteMint, + // } = this.getProposalPdas( + // proposal, + // storedDao.tokenMint, + // storedDao.usdcMint, + // dao + // ); + + // // it's important that these happen in a single atomic transaction + // const initVaultTx = await this.vaultClient + // .initializeVaultIx(proposal, storedDao.tokenMint) + // .postInstructions( + // await InstructionUtils.getInstructions( + // this.vaultClient.initializeVaultIx(proposal, storedDao.usdcMint), + // this.ammClient.createAmmIx( + // passBaseMint, + // passQuoteMint, + // storedDao.twapInitialObservation, + // storedDao.twapMaxObservationChangePerUpdate + // ), + // this.ammClient.createAmmIx( + // failBaseMint, + // failQuoteMint, + // storedDao.twapInitialObservation, + // storedDao.twapMaxObservationChangePerUpdate + // ) + // ) + // ) + // .transaction(); + + // const mintConditionalTokensTx = await this.vaultClient + // .mintConditionalTokensIx(baseVault, storedDao.tokenMint, baseTokensToLP) + // .postInstructions( + // await InstructionUtils.getInstructions( + // this.vaultClient.mintConditionalTokensIx( + // quoteVault, + // storedDao.usdcMint, + // quoteTokensToLP + // ) + // ) + // ) + // .transaction(); + + // const addLiquidityTx = await this.ammClient + // .addLiquidityIx( + // passAmm, + // passBaseMint, + // passQuoteMint, + // quoteTokensToLP, + // baseTokensToLP, + // new BN(0) + // ) + // .postInstructions( + // await InstructionUtils.getInstructions( + // this.ammClient.addLiquidityIx( + // failAmm, + // failBaseMint, + // failQuoteMint, + // quoteTokensToLP, + // baseTokensToLP, + // new BN(0) + // ) + // ) + // ) + // .transaction(); + + // // this is how many original tokens are created + // const lpTokens = quoteTokensToLP; + + // const initTx = await this.initializeProposalIx( + // descriptionUrl, + // instruction, + // dao, + // storedDao.tokenMint, + // storedDao.usdcMint, + // lpTokens, + // lpTokens, + // nonce, + // question + // ).transaction(); + + // return [ + // [initVaultTx, mintConditionalTokensTx, addLiquidityTx, initTx], + // { + // baseCondVaultAcct: baseVault, + // quoteCondVaultAcct: quoteVault, + // failMarketAcct: failAmm, + // passMarketAcct: passAmm, + // proposalAcct: proposal, + // }, + // ]; + // } initializeProposalIx( descriptionUrl: string, @@ -501,7 +546,8 @@ export class AutocratClient { quoteMint: PublicKey, passLpTokensToLock: BN, failLpTokensToLock: BN, - nonce: BN + nonce: BN, + question: PublicKey ) { let [proposal] = getProposalAddr( this.autocrat.programId, @@ -545,6 +591,7 @@ export class AutocratClient { nonce, }) .accounts({ + question, proposal, dao, baseVault, @@ -606,7 +653,7 @@ export class AutocratClient { let vaultProgramId = this.vaultClient.vaultProgram.programId; const [daoTreasury] = getDaoTreasuryAddr(this.autocrat.programId, dao); - const { baseVault, quoteVault, passAmm, failAmm } = this.getProposalPdas( + const { question, passAmm, failAmm } = this.getProposalPdas( proposal, daoToken, usdc, @@ -627,8 +674,9 @@ export class AutocratClient { passAmm, failAmm, dao, - baseVault, - quoteVault, + question, + // baseVault, + // quoteVault, passLpUserAccount: getAssociatedTokenAddressSync(passLp, proposer), failLpUserAccount: getAssociatedTokenAddressSync(failLp, proposer), passLpVaultAccount: getAssociatedTokenAddressSync( diff --git a/sdk/src/types/autocrat.ts b/sdk/src/types/autocrat.ts index 2265d9b1..3825b3ef 100644 --- a/sdk/src/types/autocrat.ts +++ b/sdk/src/types/autocrat.ts @@ -53,6 +53,11 @@ export type Autocrat = { isMut: true; isSigner: false; }, + { + name: "question"; + isMut: false; + isSigner: false; + }, { name: "quoteVault"; isMut: false; @@ -152,12 +157,7 @@ export type Autocrat = { isSigner: false; }, { - name: "baseVault"; - isMut: true; - isSigner: false; - }, - { - name: "quoteVault"; + name: "question"; isMut: true; isSigner: false; }, @@ -390,6 +390,10 @@ export type Autocrat = { { name: "pdaBump"; type: "u8"; + }, + { + name: "question"; + type: "publicKey"; } ]; }; @@ -685,6 +689,11 @@ export const IDL: Autocrat = { isMut: true, isSigner: false, }, + { + name: "question", + isMut: false, + isSigner: false, + }, { name: "quoteVault", isMut: false, @@ -784,12 +793,7 @@ export const IDL: Autocrat = { isSigner: false, }, { - name: "baseVault", - isMut: true, - isSigner: false, - }, - { - name: "quoteVault", + name: "question", isMut: true, isSigner: false, }, @@ -1023,6 +1027,10 @@ export const IDL: Autocrat = { name: "pdaBump", type: "u8", }, + { + name: "question", + type: "publicKey", + }, ], }, }, diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index ad5c53d9..5185cd46 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -343,29 +343,6 @@ export type ConditionalVault = { } ]; }, - { - name: "settleConditionalVault"; - accounts: [ - { - name: "settlementAuthority"; - isMut: false; - isSigner: true; - }, - { - name: "vault"; - isMut: true; - isSigner: false; - } - ]; - args: [ - { - name: "newStatus"; - type: { - defined: "VaultStatus"; - }; - } - ]; - }, { name: "mergeConditionalTokensForUnderlyingTokens"; accounts: [ @@ -1171,29 +1148,6 @@ export const IDL: ConditionalVault = { }, ], }, - { - name: "settleConditionalVault", - accounts: [ - { - name: "settlementAuthority", - isMut: false, - isSigner: true, - }, - { - name: "vault", - isMut: true, - isSigner: false, - }, - ], - args: [ - { - name: "newStatus", - type: { - defined: "VaultStatus", - }, - }, - ], - }, { name: "mergeConditionalTokensForUnderlyingTokens", accounts: [ diff --git a/tests/autocrat/autocrat.ts b/tests/autocrat/autocrat.ts index 14ae10db..5ae4920d 100644 --- a/tests/autocrat/autocrat.ts +++ b/tests/autocrat/autocrat.ts @@ -99,7 +99,7 @@ export default function suite() { ammClient = AmmClient.createClient({ provider }); vaultClient = ConditionalVaultClient.createClient({ provider }); - autocratClient = await AutocratClient.createClient({ provider }); + autocratClient = AutocratClient.createClient({ provider }); autocrat = new anchor.Program( AutocratIDL, @@ -339,14 +339,14 @@ export default function suite() { ONE_USDC.muln(5000) ); - let { baseVault, quoteVault } = autocratClient.getProposalPdas( + let { baseVault, quoteVault, question } = autocratClient.getProposalPdas( proposal, META, USDC, dao ); - await vaultClient.mintConditionalTokens(baseVault, 10); - await vaultClient.mintConditionalTokens(quoteVault, 10_000); + await vaultClient.splitTokensIx(question, baseVault, META, new BN(10 * 10**9), 2).rpc(); + await vaultClient.splitTokensIx(question, quoteVault, USDC, new BN(10_000 * 1_000_000), 2).rpc(); }); it("doesn't finalize proposals that are too young", async function () { @@ -372,6 +372,7 @@ export default function suite() { quoteVault, passLp, failLp, + question, } = autocratClient.getProposalPdas(proposal, META, USDC, dao); // swap $500 in the pass market, make it pass @@ -449,11 +450,16 @@ export default function suite() { console.log(PriceMath.getHumanPrice(passTwap, 9, 6)); console.log(PriceMath.getHumanPrice(failTwap, 9, 6)); - let storedBaseVault = await vaultClient.getVault(baseVault); - let storedQuoteVault = await vaultClient.getVault(quoteVault); + let storedQuestion = await vaultClient.fetchQuestion(question); - assert.exists(storedBaseVault.status.finalized); - assert.exists(storedQuoteVault.status.finalized); + assert.equal(storedQuestion.payoutDenominator, 1); + assert.deepEqual(storedQuestion.payoutNumerators, [0, 1]); + + // let storedBaseVault = await vaultClient.fetchVault(baseVault); + // let storedQuoteVault = await vaultClient.fetchVault(quoteVault); + + // assert.exists(storedBaseVault.status.finalized); + // assert.exists(storedQuoteVault.status.finalized); }); it("rejects proposals when pass price TWAP < fail price TWAP", async function () { @@ -462,8 +468,7 @@ export default function suite() { failAmm, failBaseMint, failQuoteMint, - baseVault, - quoteVault, + question, } = autocratClient.getProposalPdas(proposal, META, USDC, dao); // swap $500 in the fail market, make it fail @@ -512,16 +517,16 @@ export default function suite() { console.log(PriceMath.getHumanPrice(passTwap, 9, 6)); console.log(PriceMath.getHumanPrice(failTwap, 9, 6)); - let storedBaseVault = await vaultClient.getVault(baseVault); - let storedQuoteVault = await vaultClient.getVault(quoteVault); + let storedQuestion = await vaultClient.fetchQuestion(question); + + assert.equal(storedQuestion.payoutDenominator, 1); + assert.deepEqual(storedQuestion.payoutNumerators, [1, 0]); - assert.exists(storedBaseVault.status.reverted); - assert.exists(storedQuoteVault.status.reverted); }); }); describe("#execute_proposal", async function () { - let proposal, passAmm, failAmm, baseVault, quoteVault, instruction; + let proposal, passAmm, failAmm, baseVault, quoteVault, question: PublicKey,instruction; beforeEach(async function () { await mintToOverride(context, treasuryMetaAccount, 1_000_000_000n); @@ -563,11 +568,14 @@ export default function suite() { ONE_META.muln(10), ONE_USDC.muln(6_000) ); - ({ baseVault, quoteVault, passAmm, failAmm } = + + // console.log(await autocrat.account.proposal.fetch(proposal)); + ({ baseVault, quoteVault, passAmm, failAmm, question } = await autocrat.account.proposal.fetch(proposal)); - await vaultClient.mintConditionalTokens(baseVault, 10); - await vaultClient.mintConditionalTokens(quoteVault, 10_000); + + await vaultClient.splitTokensIx(question, baseVault, META, new BN(10 * 10**9), 2).rpc(); + await vaultClient.splitTokensIx(question, quoteVault, USDC, new BN(10_000 * 1_000_000), 2).rpc(); }); it("doesn't allow pending proposals to be executed", async function () { From 0d00c82a0db732711db4492024d4133396ab023b Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 36/52] v0.3.0-alpha.14 --- sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/package.json b/sdk/package.json index 6d5e9391..e2abb5ed 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@metadaoproject/futarchy", - "version": "0.3.0-alpha.13", + "version": "0.3.0-alpha.14", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ From aee266314633f6abae3a13daa6b151e593351d33 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 37/52] Update to 0.3.0-14 SDK --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e776fdb7..9f1472b8 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ }, "dependencies": { "@coral-xyz/anchor": "0.29.0", - "@metadaoproject/futarchy": "0.3.0-alpha.13", + "@metadaoproject/futarchy": "0.3.0-alpha.14", "@metaplex-foundation/mpl-token-metadata": "^3.2.0", "@metaplex-foundation/umi": "^0.9.1", "@metaplex-foundation/umi-bundle-defaults": "^0.9.1", diff --git a/yarn.lock b/yarn.lock index 73b1841a..96a62240 100644 --- a/yarn.lock +++ b/yarn.lock @@ -629,10 +629,10 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@metadaoproject/futarchy@0.3.0-alpha.13": - version "0.3.0-alpha.13" - resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.13.tgz#dd7d8f2a77ab22ea97bdd79b697ecddfdd832388" - integrity sha512-j0hUZcnoH5LnCU7zV8lm9h5HV9KNSsM4Kx/KQFQBBdqevVzfUCKu51S9mv49S2KCFbQC36B8/73E6YaA2l807w== +"@metadaoproject/futarchy@0.3.0-alpha.14": + version "0.3.0-alpha.14" + resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.14.tgz#ed6640cef72986f72835f6bbef12854e5ef3a404" + integrity sha512-ut3IAhe0zS10ySzDUdQlaL1GHue/rm3Xcp9JlLu8bKGu0F10Tj/l1OEwaJNoL1eBsvjZbq3uzClOV1cs5gjnBQ== dependencies: "@coral-xyz/anchor" "^0.29.0" "@noble/hashes" "^1.4.0" From 3c8bf6dcf60eea8dd67fb13658f476e7e4de0c21 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sun, 25 Aug 2024 00:00:00 +0000 Subject: [PATCH 38/52] Final removes & lints --- .../src/instructions/finalize_proposal.rs | 21 +- .../src/instructions/initialize_proposal.rs | 3 + programs/autocrat/src/lib.rs | 2 +- .../src/instructions/common.rs | 76 +- .../initialize_conditional_vault.rs | 99 ++- .../initialize_new_conditional_vault.rs | 104 --- .../src/instructions/initialize_question.rs | 6 +- .../instructions/merge_conditional_tokens.rs | 114 --- .../src/instructions/merge_tokens.rs | 7 +- .../instructions/mint_conditional_tokens.rs | 96 -- .../conditional_vault/src/instructions/mod.rs | 25 +- .../src/instructions/new_common.rs | 70 -- ...onditional_tokens_for_underlying_tokens.rs | 122 --- .../src/instructions/redeem_tokens.rs | 12 +- .../src/instructions/resolve_question.rs | 6 +- .../instructions/settle_conditional_vault.rs | 30 - .../src/instructions/split_tokens.rs | 7 +- programs/conditional_vault/src/lib.rs | 53 +- .../src/state/conditional_vault.rs | 38 +- .../conditional_vault/src/state/question.rs | 6 +- programs/optimistic_timelock/src/lib.rs | 26 +- sdk/src/AutocratClient.ts | 8 +- sdk/src/ConditionalVaultClient.ts | 332 ++----- sdk/src/types/conditional_vault.ts | 828 +----------------- sdk/src/utils/pda.ts | 4 +- tests/amm/amm.ts | 8 +- tests/amm/integration/ammLifecycle.test.ts | 162 ++-- tests/amm/main.test.ts | 12 +- tests/amm/unit/addLiquidity.test.ts | 142 +-- tests/amm/unit/initializeAmm.test.ts | 134 +-- tests/amm/unit/removeLiquidity.test.ts | 279 +++--- tests/amm/unit/swap.test.ts | 484 +++++----- tests/autocrat/autocrat.ts | 57 +- tests/autocratMigrator/migrator.ts | 5 +- .../binaryPredictionMarket.test.ts | 180 ++-- .../integration/scalarGrantMarket.test.ts | 80 +- tests/conditionalVault/main.test.ts | 18 +- tests/conditionalVault/unit.ts | 62 +- .../unit/initializeConditionalVault.test.ts | 151 ++-- .../unit/initializeQuestion.test.ts | 52 +- .../conditionalVault/unit/mergeTokens.test.ts | 142 +-- .../unit/redeemTokens.test.ts | 195 +++-- .../unit/resolveQuestion.test.ts | 68 +- .../conditionalVault/unit/splitTokens.test.ts | 293 +++---- tests/main.test.ts | 155 ++-- tests/optimisticTimelock/timelock.ts | 5 +- 46 files changed, 1806 insertions(+), 2973 deletions(-) delete mode 100644 programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs delete mode 100644 programs/conditional_vault/src/instructions/merge_conditional_tokens.rs delete mode 100644 programs/conditional_vault/src/instructions/mint_conditional_tokens.rs delete mode 100644 programs/conditional_vault/src/instructions/new_common.rs delete mode 100644 programs/conditional_vault/src/instructions/redeem_conditional_tokens_for_underlying_tokens.rs delete mode 100644 programs/conditional_vault/src/instructions/settle_conditional_vault.rs diff --git a/programs/autocrat/src/instructions/finalize_proposal.rs b/programs/autocrat/src/instructions/finalize_proposal.rs index 28475253..71709731 100644 --- a/programs/autocrat/src/instructions/finalize_proposal.rs +++ b/programs/autocrat/src/instructions/finalize_proposal.rs @@ -1,4 +1,4 @@ -use conditional_vault::{ResolveQuestionArgs, cpi::accounts::ResolveQuestion}; +use conditional_vault::{cpi::accounts::ResolveQuestion, ResolveQuestionArgs}; use super::*; @@ -161,15 +161,16 @@ impl FinalizeProposal<'_> { proposal.state = new_proposal_state; // for vault in [base_vault.to_account_info(), quote_vault.to_account_info()] { - let vault_program = vault_program.to_account_info(); - let cpi_accounts = ResolveQuestion { - question: question.to_account_info(), - oracle: proposal.to_account_info(), - }; - let cpi_ctx = CpiContext::new(vault_program, cpi_accounts).with_signer(proposal_signer); - conditional_vault::cpi::resolve_question(cpi_ctx, ResolveQuestionArgs { - payout_numerators, - })?; + let vault_program = vault_program.to_account_info(); + let cpi_accounts = ResolveQuestion { + question: question.to_account_info(), + oracle: proposal.to_account_info(), + }; + let cpi_ctx = CpiContext::new(vault_program, cpi_accounts).with_signer(proposal_signer); + conditional_vault::cpi::resolve_question( + cpi_ctx, + ResolveQuestionArgs { payout_numerators }, + )?; // } // base_vault.reload()?; diff --git a/programs/autocrat/src/instructions/initialize_proposal.rs b/programs/autocrat/src/instructions/initialize_proposal.rs index aaa227eb..c7951422 100644 --- a/programs/autocrat/src/instructions/initialize_proposal.rs +++ b/programs/autocrat/src/instructions/initialize_proposal.rs @@ -25,6 +25,9 @@ pub struct InitializeProposal<'info> { pub proposal: Box>, #[account(mut)] pub dao: Box>, + #[account( + constraint = question.oracle == proposal.key() + )] pub question: Box>, #[account( constraint = quote_vault.underlying_token_mint == dao.usdc_mint, diff --git a/programs/autocrat/src/lib.rs b/programs/autocrat/src/lib.rs index 007072ad..2fcc6c74 100644 --- a/programs/autocrat/src/lib.rs +++ b/programs/autocrat/src/lib.rs @@ -28,7 +28,7 @@ use anchor_lang::solana_program; use anchor_spl::token::{self, Mint, Token, TokenAccount, Transfer}; // use conditional_vault::cpi::accounts::SettleConditionalVault; use conditional_vault::program::ConditionalVault as ConditionalVaultProgram; -use conditional_vault::NewConditionalVault as ConditionalVaultAccount; +use conditional_vault::ConditionalVault as ConditionalVaultAccount; use conditional_vault::Question; // use conditional_vault::VaultStatus; diff --git a/programs/conditional_vault/src/instructions/common.rs b/programs/conditional_vault/src/instructions/common.rs index ec979880..b4050407 100644 --- a/programs/conditional_vault/src/instructions/common.rs +++ b/programs/conditional_vault/src/instructions/common.rs @@ -1,34 +1,16 @@ use super::*; #[derive(Accounts)] -pub struct InteractWithVault<'info> { - #[account( - has_one = conditional_on_finalize_token_mint @ VaultError::InvalidConditionalTokenMint, - has_one = conditional_on_revert_token_mint @ VaultError::InvalidConditionalTokenMint, - )] +pub struct InteractWithNewVault<'info> { + pub question: Account<'info, Question>, + #[account(has_one = question)] pub vault: Account<'info, ConditionalVault>, - #[account(mut)] - pub conditional_on_finalize_token_mint: Account<'info, Mint>, - #[account(mut)] - pub conditional_on_revert_token_mint: Account<'info, Mint>, #[account( mut, constraint = vault_underlying_token_account.key() == vault.underlying_token_account @ VaultError::InvalidVaultUnderlyingTokenAccount )] pub vault_underlying_token_account: Account<'info, TokenAccount>, pub authority: Signer<'info>, - #[account( - mut, - token::authority = authority, - token::mint = conditional_on_finalize_token_mint - )] - pub user_conditional_on_finalize_token_account: Account<'info, TokenAccount>, - #[account( - mut, - token::authority = authority, - token::mint = conditional_on_revert_token_mint - )] - pub user_conditional_on_revert_token_account: Account<'info, TokenAccount>, #[account( mut, token::authority = authority, @@ -37,3 +19,55 @@ pub struct InteractWithVault<'info> { pub user_underlying_token_account: Account<'info, TokenAccount>, pub token_program: Program<'info, Token>, } + +impl<'info, 'c: 'info> InteractWithNewVault<'info> { + pub fn get_mints_and_user_token_accounts( + ctx: &Context<'_, '_, 'c, 'info, Self>, + ) -> Result<(Vec>, Vec>)> { + msg!("HelloWorld"); + let remaining_accs = &mut ctx.remaining_accounts.iter(); + // msg!("{:?}", remaining_accs); + + let expected_num_conditional_tokens = ctx.accounts.question.num_outcomes(); + require_eq!( + remaining_accs.len(), + expected_num_conditional_tokens * 2, + VaultError::InvalidConditionals + ); + + let mut conditional_token_mints = vec![]; + let mut user_conditional_token_accounts = vec![]; + + for i in 0..expected_num_conditional_tokens { + let conditional_token_mint = next_account_info(remaining_accs)?; + require_eq!( + ctx.accounts.vault.conditional_token_mints[i], + conditional_token_mint.key(), + VaultError::ConditionalMintMismatch + ); + + conditional_token_mints.push( + Account::::try_from(conditional_token_mint) + .or(Err(VaultError::BadConditionalMint))?, + ); + } + + for i in 0..expected_num_conditional_tokens { + let user_conditional_token_account = next_account_info(remaining_accs)?; + + let user_conditional_token_account = + Account::::try_from(user_conditional_token_account) + .or(Err(VaultError::BadConditionalTokenAccount))?; + + require_eq!( + user_conditional_token_account.mint, + conditional_token_mints[i].key(), + VaultError::ConditionalTokenMintMismatch + ); + + user_conditional_token_accounts.push(user_conditional_token_account); + } + + Ok((conditional_token_mints, user_conditional_token_accounts)) + } +} diff --git a/programs/conditional_vault/src/instructions/initialize_conditional_vault.rs b/programs/conditional_vault/src/instructions/initialize_conditional_vault.rs index 2ed0c5d5..f5a0f9e4 100644 --- a/programs/conditional_vault/src/instructions/initialize_conditional_vault.rs +++ b/programs/conditional_vault/src/instructions/initialize_conditional_vault.rs @@ -1,46 +1,24 @@ use super::*; -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct InitializeConditionalVaultArgs { - pub settlement_authority: Pubkey, -} +use anchor_lang::system_program; +use anchor_spl::token; #[derive(Accounts)] -#[instruction(args: InitializeConditionalVaultArgs)] pub struct InitializeConditionalVault<'info> { #[account( init, payer = payer, - space = 8 + std::mem::size_of::(), + space = 8 + std::mem::size_of::() + (32 * question.num_outcomes()), seeds = [ b"conditional_vault", - args.settlement_authority.key().as_ref(), + question.key().as_ref(), underlying_token_mint.key().as_ref(), ], bump )] pub vault: Box>, + pub question: Account<'info, Question>, pub underlying_token_mint: Account<'info, Mint>, - #[account( - init, - payer = payer, - seeds = [b"conditional_on_finalize_mint", vault.key().as_ref()], - bump, - mint::authority = vault, - mint::freeze_authority = vault, - mint::decimals = underlying_token_mint.decimals - )] - pub conditional_on_finalize_token_mint: Box>, - #[account( - init, - payer = payer, - seeds = [b"conditional_on_revert_mint", vault.key().as_ref()], - bump, - mint::authority = vault, - mint::freeze_authority = vault, - mint::decimals = underlying_token_mint.decimals - )] - pub conditional_on_revert_token_mint: Box>, #[account( associated_token::authority = vault, associated_token::mint = underlying_token_mint @@ -53,26 +31,67 @@ pub struct InitializeConditionalVault<'info> { pub system_program: Program<'info, System>, } -impl InitializeConditionalVault<'_> { - pub fn handle(ctx: Context, args: InitializeConditionalVaultArgs) -> Result<()> { +impl<'info, 'c: 'info> InitializeConditionalVault<'info> { + pub fn handle(ctx: Context<'_, '_, 'c, 'info, Self>) -> Result<()> { let vault = &mut ctx.accounts.vault; - let InitializeConditionalVaultArgs { - settlement_authority, - } = args; + let decimals = ctx.accounts.underlying_token_mint.decimals; + + let remaining_accs = &mut ctx.remaining_accounts.iter(); + + let expected_num_conditional_tokens = ctx.accounts.question.num_outcomes(); + let mut conditional_token_mints = vec![]; + + let mint_lamports = Rent::get()?.minimum_balance(Mint::LEN); + for i in 0..expected_num_conditional_tokens { + let (conditional_token_mint_address, pda_bump) = Pubkey::find_program_address( + &[b"conditional_token", vault.key().as_ref(), &[i as u8]], + ctx.program_id, + ); + + let conditional_token_mint = next_account_info(remaining_accs)?; + require_eq!(conditional_token_mint.key(), conditional_token_mint_address); + + conditional_token_mints.push(conditional_token_mint_address); + + let cpi_program = ctx.accounts.system_program.to_account_info(); + let cpi_accounts = system_program::CreateAccount { + from: ctx.accounts.payer.to_account_info(), + to: conditional_token_mint.to_account_info(), + }; + let vault_key = vault.key(); + let seeds = &[ + b"conditional_token", + vault_key.as_ref(), + &[i as u8], + &[pda_bump], + ]; + let signer = &[&seeds[..]]; + let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); + + system_program::create_account( + cpi_ctx, + mint_lamports, + Mint::LEN as u64, + ctx.accounts.token_program.key, + )?; + + let cpi_program = ctx.accounts.token_program.to_account_info(); + let cpi_accounts = token::InitializeMint2 { + mint: conditional_token_mint.to_account_info(), + }; + let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + + token::initialize_mint2(cpi_ctx, decimals, &vault.key(), None)?; + } vault.set_inner(ConditionalVault { - status: VaultStatus::Active, - settlement_authority, + question: ctx.accounts.question.key(), underlying_token_mint: ctx.accounts.underlying_token_mint.key(), underlying_token_account: ctx.accounts.vault_underlying_token_account.key(), - conditional_on_finalize_token_mint: ctx - .accounts - .conditional_on_finalize_token_mint - .key(), - conditional_on_revert_token_mint: ctx.accounts.conditional_on_revert_token_mint.key(), + conditional_token_mints, pda_bump: ctx.bumps.vault, - decimals: ctx.accounts.underlying_token_mint.decimals, + decimals, }); Ok(()) diff --git a/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs b/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs deleted file mode 100644 index 3b50569e..00000000 --- a/programs/conditional_vault/src/instructions/initialize_new_conditional_vault.rs +++ /dev/null @@ -1,104 +0,0 @@ -use super::*; - -use anchor_lang::system_program; -use anchor_spl::token; - -#[derive(Accounts)] -pub struct InitializeNewConditionalVault<'info> { - #[account( - init, - payer = payer, - space = 8 + std::mem::size_of::() + (32 * question.num_outcomes()), - seeds = [ - b"conditional_vault", - question.key().as_ref(), - underlying_token_mint.key().as_ref(), - ], - bump - )] - pub vault: Box>, - pub question: Account<'info, Question>, - pub underlying_token_mint: Account<'info, Mint>, - #[account( - associated_token::authority = vault, - associated_token::mint = underlying_token_mint - )] - pub vault_underlying_token_account: Box>, - #[account(mut)] - pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, - pub associated_token_program: Program<'info, AssociatedToken>, - pub system_program: Program<'info, System>, -} - -impl<'info, 'c: 'info> InitializeNewConditionalVault<'info> { - pub fn handle(ctx: Context<'_, '_, 'c, 'info, Self>) -> Result<()> { - let vault = &mut ctx.accounts.vault; - - let decimals = ctx.accounts.underlying_token_mint.decimals; - - let remaining_accs = &mut ctx.remaining_accounts.iter(); - - let expected_num_conditional_tokens = ctx.accounts.question.num_outcomes(); - let mut conditional_token_mints = vec![]; - - let mint_lamports = Rent::get()?.minimum_balance(Mint::LEN); - for i in 0..expected_num_conditional_tokens { - let (conditional_token_mint_address, pda_bump) = Pubkey::find_program_address( - &[b"conditional_token", vault.key().as_ref(), &[i as u8]], - ctx.program_id, - ); - - let conditional_token_mint = next_account_info(remaining_accs)?; - require_eq!(conditional_token_mint.key(), conditional_token_mint_address); - - conditional_token_mints.push(conditional_token_mint_address); - - let cpi_program = ctx.accounts.system_program.to_account_info(); - let cpi_accounts = system_program::CreateAccount { - from: ctx.accounts.payer.to_account_info(), - to: conditional_token_mint.to_account_info(), - }; - let vault_key = vault.key(); - let seeds = &[ - b"conditional_token", - vault_key.as_ref(), - &[i as u8], - &[pda_bump], - ]; - let signer = &[&seeds[..]]; - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - - system_program::create_account( - cpi_ctx, - mint_lamports, - Mint::LEN as u64, - ctx.accounts.token_program.key, - )?; - - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_accounts = token::InitializeMint2 { - mint: conditional_token_mint.to_account_info(), - }; - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - - token::initialize_mint2( - cpi_ctx, - decimals, - &vault.key(), - None, - )?; - } - - vault.set_inner(NewConditionalVault { - question: ctx.accounts.question.key(), - underlying_token_mint: ctx.accounts.underlying_token_mint.key(), - underlying_token_account: ctx.accounts.vault_underlying_token_account.key(), - conditional_token_mints, - pda_bump: ctx.bumps.vault, - decimals, - }); - - Ok(()) - } -} diff --git a/programs/conditional_vault/src/instructions/initialize_question.rs b/programs/conditional_vault/src/instructions/initialize_question.rs index 3824e8ce..760fca22 100644 --- a/programs/conditional_vault/src/instructions/initialize_question.rs +++ b/programs/conditional_vault/src/instructions/initialize_question.rs @@ -30,7 +30,11 @@ pub struct InitializeQuestion<'info> { impl InitializeQuestion<'_> { pub fn handle(ctx: Context, args: InitializeQuestionArgs) -> Result<()> { - require_gte!(args.num_conditions, 2, VaultError::InsufficientNumConditions); + require_gte!( + args.num_conditions, + 2, + VaultError::InsufficientNumConditions + ); let question = &mut ctx.accounts.question; diff --git a/programs/conditional_vault/src/instructions/merge_conditional_tokens.rs b/programs/conditional_vault/src/instructions/merge_conditional_tokens.rs deleted file mode 100644 index e8c16919..00000000 --- a/programs/conditional_vault/src/instructions/merge_conditional_tokens.rs +++ /dev/null @@ -1,114 +0,0 @@ -use super::*; - -impl InteractWithVault<'_> { - pub fn validate_merge_conditional_tokens(&self) -> Result<()> { - require!( - self.vault.status == VaultStatus::Active, - VaultError::VaultAlreadySettled - ); - - Ok(()) - } - - pub fn handle_merge_conditional_tokens(ctx: Context, amount: u64) -> Result<()> { - let accs = &ctx.accounts; - - let vault = &accs.vault; - - // Store Pre-operation Balances - let pre_user_conditional_on_finalize_balance = ctx - .accounts - .user_conditional_on_finalize_token_account - .amount; - let pre_user_conditional_on_revert_balance = - ctx.accounts.user_conditional_on_revert_token_account.amount; - let pre_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; - let pre_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; - let pre_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; - - let seeds = generate_vault_seeds!(vault); - let signer = &[&seeds[..]]; - - // burn `amount` from both token accounts - for (conditional_mint, user_conditional_token_account) in [ - ( - &accs.conditional_on_finalize_token_mint, - &accs.user_conditional_on_finalize_token_account, - ), - ( - &accs.conditional_on_revert_token_mint, - &accs.user_conditional_on_revert_token_account, - ), - ] { - token::burn( - CpiContext::new( - accs.token_program.to_account_info(), - Burn { - mint: conditional_mint.to_account_info(), - from: user_conditional_token_account.to_account_info(), - authority: accs.authority.to_account_info(), - }, - ), - amount, - )?; - } - - // Transfer `amount` from vault to user - token::transfer( - CpiContext::new_with_signer( - accs.token_program.to_account_info(), - Transfer { - from: accs.vault_underlying_token_account.to_account_info(), - to: accs.user_underlying_token_account.to_account_info(), - authority: accs.vault.to_account_info(), - }, - signer, - ), - amount, - )?; - - // Reload Accounts to Reflect Changes - ctx.accounts - .user_conditional_on_finalize_token_account - .reload()?; - ctx.accounts - .user_conditional_on_revert_token_account - .reload()?; - ctx.accounts.vault_underlying_token_account.reload()?; - ctx.accounts.conditional_on_finalize_token_mint.reload()?; - ctx.accounts.conditional_on_revert_token_mint.reload()?; - - // Check post-operation balances - let post_user_conditional_on_finalize_balance = ctx - .accounts - .user_conditional_on_finalize_token_account - .amount; - let post_user_conditional_on_revert_balance = - ctx.accounts.user_conditional_on_revert_token_account.amount; - let post_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; - let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; - let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; - - // Check that the user's conditional token balances are unchanged (since we're not necessarily burning all tokens) - require_eq!( - post_user_conditional_on_finalize_balance, - pre_user_conditional_on_finalize_balance - amount - ); - require_eq!( - post_user_conditional_on_revert_balance, - pre_user_conditional_on_revert_balance - amount - ); - - // Check that the mint supplies have been reduced by the burned amounts - require_eq!(post_finalize_mint_supply, pre_finalize_mint_supply - amount); - require_eq!(post_revert_mint_supply, pre_revert_mint_supply - amount); - - // Check that the vault's underlying balance has been reduced by the transferred amount - require_eq!( - post_vault_underlying_balance, - pre_vault_underlying_balance - amount - ); - - Ok(()) - } -} diff --git a/programs/conditional_vault/src/instructions/merge_tokens.rs b/programs/conditional_vault/src/instructions/merge_tokens.rs index 14b920e5..ebc0fffb 100644 --- a/programs/conditional_vault/src/instructions/merge_tokens.rs +++ b/programs/conditional_vault/src/instructions/merge_tokens.rs @@ -28,7 +28,7 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { .map(|mint| mint.supply - amount) .collect(); - let seeds = generate_new_vault_seeds!(vault); + let seeds = generate_vault_seeds!(vault); let signer = &[&seeds[..]]; for (conditional_mint, user_conditional_token_account) in conditional_token_mints @@ -92,7 +92,10 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { ctx.accounts.vault.invariant( &ctx.accounts.question, - conditional_token_mints.iter().map(|mint| mint.supply).collect::>(), + conditional_token_mints + .iter() + .map(|mint| mint.supply) + .collect::>(), ctx.accounts.vault_underlying_token_account.amount, )?; diff --git a/programs/conditional_vault/src/instructions/mint_conditional_tokens.rs b/programs/conditional_vault/src/instructions/mint_conditional_tokens.rs deleted file mode 100644 index 35c64e39..00000000 --- a/programs/conditional_vault/src/instructions/mint_conditional_tokens.rs +++ /dev/null @@ -1,96 +0,0 @@ -use super::*; - -impl InteractWithVault<'_> { - pub fn handle_mint_conditional_tokens(ctx: Context, amount: u64) -> Result<()> { - let accs = &ctx.accounts; - - let pre_vault_underlying_balance = accs.vault_underlying_token_account.amount; - let pre_user_conditional_on_finalize_balance = - accs.user_conditional_on_finalize_token_account.amount; - let pre_user_conditional_on_revert_balance = - accs.user_conditional_on_revert_token_account.amount; - let pre_finalize_mint_supply = accs.conditional_on_finalize_token_mint.supply; - let pre_revert_mint_supply = accs.conditional_on_revert_token_mint.supply; - - require!( - accs.user_underlying_token_account.amount >= amount, - VaultError::InsufficientUnderlyingTokens - ); - - let vault = &accs.vault; - - let seeds = generate_vault_seeds!(vault); - let signer = &[&seeds[..]]; - - token::transfer( - CpiContext::new( - accs.token_program.to_account_info(), - Transfer { - from: accs.user_underlying_token_account.to_account_info(), - to: accs.vault_underlying_token_account.to_account_info(), - authority: accs.authority.to_account_info(), - }, - ), - amount, - )?; - - for (conditional_mint, user_conditional_token_account) in [ - ( - &accs.conditional_on_finalize_token_mint, - &accs.user_conditional_on_finalize_token_account, - ), - ( - &accs.conditional_on_revert_token_mint, - &accs.user_conditional_on_revert_token_account, - ), - ] { - token::mint_to( - CpiContext::new_with_signer( - accs.token_program.to_account_info(), - MintTo { - mint: conditional_mint.to_account_info(), - to: user_conditional_token_account.to_account_info(), - authority: accs.vault.to_account_info(), - }, - signer, - ), - amount, - )?; - } - - ctx.accounts - .user_conditional_on_finalize_token_account - .reload()?; - ctx.accounts - .user_conditional_on_revert_token_account - .reload()?; - ctx.accounts.vault_underlying_token_account.reload()?; - ctx.accounts.conditional_on_finalize_token_mint.reload()?; - ctx.accounts.conditional_on_revert_token_mint.reload()?; - - let post_user_conditional_on_finalize_balance = ctx - .accounts - .user_conditional_on_finalize_token_account - .amount; - let post_user_conditional_on_revert_balance = - ctx.accounts.user_conditional_on_revert_token_account.amount; - let post_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; - let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; - let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; - - // Only the paranoid survive ;) - assert!(post_vault_underlying_balance == pre_vault_underlying_balance + amount); - assert!( - post_user_conditional_on_finalize_balance - == pre_user_conditional_on_finalize_balance + amount - ); - assert!( - post_user_conditional_on_revert_balance - == pre_user_conditional_on_revert_balance + amount - ); - assert!(post_finalize_mint_supply == pre_finalize_mint_supply + amount); - assert!(post_revert_mint_supply == pre_revert_mint_supply + amount); - - Ok(()) - } -} diff --git a/programs/conditional_vault/src/instructions/mod.rs b/programs/conditional_vault/src/instructions/mod.rs index ce700980..2b0df556 100644 --- a/programs/conditional_vault/src/instructions/mod.rs +++ b/programs/conditional_vault/src/instructions/mod.rs @@ -1,28 +1,19 @@ use super::*; -pub mod add_metadata_to_conditional_tokens; +// pub mod add_metadata_to_conditional_tokens; pub mod common; -pub mod initialize_question; pub mod initialize_conditional_vault; -pub mod initialize_new_conditional_vault; -pub mod merge_conditional_tokens; -pub mod mint_conditional_tokens; -pub mod redeem_conditional_tokens_for_underlying_tokens; -pub mod settle_conditional_vault; -pub mod resolve_question; -pub mod new_common; -pub mod split_tokens; +pub mod initialize_question; pub mod merge_tokens; pub mod redeem_tokens; +pub mod resolve_question; +pub mod split_tokens; pub use initialize_question::*; -pub use add_metadata_to_conditional_tokens::*; +// pub use add_metadata_to_conditional_tokens::*; pub use common::*; pub use initialize_conditional_vault::*; -pub use initialize_new_conditional_vault::*; -pub use settle_conditional_vault::*; pub use resolve_question::*; -pub use new_common::*; -pub use split_tokens::*; -pub use merge_tokens::*; -pub use redeem_tokens::*; +// pub use split_tokens::*; +// pub use merge_tokens::*; +// pub use redeem_tokens::*; diff --git a/programs/conditional_vault/src/instructions/new_common.rs b/programs/conditional_vault/src/instructions/new_common.rs deleted file mode 100644 index 93e37d2d..00000000 --- a/programs/conditional_vault/src/instructions/new_common.rs +++ /dev/null @@ -1,70 +0,0 @@ -use super::*; - -#[derive(Accounts)] -pub struct InteractWithNewVault<'info> { - pub question: Account<'info, Question>, - #[account(has_one = question)] - pub vault: Account<'info, NewConditionalVault>, - #[account( - mut, - constraint = vault_underlying_token_account.key() == vault.underlying_token_account @ VaultError::InvalidVaultUnderlyingTokenAccount - )] - pub vault_underlying_token_account: Account<'info, TokenAccount>, - pub authority: Signer<'info>, - #[account( - mut, - token::authority = authority, - token::mint = vault.underlying_token_mint - )] - pub user_underlying_token_account: Account<'info, TokenAccount>, - pub token_program: Program<'info, Token>, -} - -impl<'info, 'c: 'info> InteractWithNewVault<'info> { - pub fn get_mints_and_user_token_accounts( - ctx: &Context<'_, '_, 'c, 'info, Self>, - ) -> Result<(Vec>, Vec>)> { - msg!("HelloWorld"); - let remaining_accs = &mut ctx.remaining_accounts.iter(); - // msg!("{:?}", remaining_accs); - - let expected_num_conditional_tokens = ctx.accounts.question.num_outcomes(); - require_eq!( - remaining_accs.len(), - expected_num_conditional_tokens * 2, - VaultError::InvalidConditionals - ); - - let mut conditional_token_mints = vec![]; - let mut user_conditional_token_accounts = vec![]; - - for i in 0..expected_num_conditional_tokens { - let conditional_token_mint = next_account_info(remaining_accs)?; - require_eq!( - ctx.accounts.vault.conditional_token_mints[i], - conditional_token_mint.key(), - VaultError::ConditionalMintMismatch - ); - - conditional_token_mints.push(Account::::try_from(conditional_token_mint) - .or(Err(VaultError::BadConditionalMint))?); - } - - for i in 0..expected_num_conditional_tokens { - let user_conditional_token_account = next_account_info(remaining_accs)?; - - let user_conditional_token_account = Account::::try_from(user_conditional_token_account) - .or(Err(VaultError::BadConditionalTokenAccount))?; - - require_eq!( - user_conditional_token_account.mint, - conditional_token_mints[i].key(), - VaultError::ConditionalTokenMintMismatch - ); - - user_conditional_token_accounts.push(user_conditional_token_account); - } - - Ok((conditional_token_mints, user_conditional_token_accounts)) - } -} diff --git a/programs/conditional_vault/src/instructions/redeem_conditional_tokens_for_underlying_tokens.rs b/programs/conditional_vault/src/instructions/redeem_conditional_tokens_for_underlying_tokens.rs deleted file mode 100644 index a7e55c01..00000000 --- a/programs/conditional_vault/src/instructions/redeem_conditional_tokens_for_underlying_tokens.rs +++ /dev/null @@ -1,122 +0,0 @@ -use super::*; - -impl InteractWithVault<'_> { - pub fn validate_redeem_conditional_tokens(&self) -> Result<()> { - require!( - self.vault.status != VaultStatus::Active, - VaultError::CantRedeemConditionalTokens - ); - - Ok(()) - } - - pub fn handle_redeem_conditional_tokens(ctx: Context) -> Result<()> { - let accs = &ctx.accounts; - let vault = &accs.vault; - let vault_status = vault.status; - - // storing some numbers for later invariant checks - let pre_vault_underlying_balance = accs.vault_underlying_token_account.amount; - let pre_finalize_mint_supply = accs.conditional_on_finalize_token_mint.supply; - let pre_revert_mint_supply = accs.conditional_on_revert_token_mint.supply; - - let pre_conditional_on_finalize_balance = - accs.user_conditional_on_finalize_token_account.amount; - let pre_conditional_on_revert_balance = - accs.user_conditional_on_revert_token_account.amount; - - let seeds = generate_vault_seeds!(vault); - let signer = &[&seeds[..]]; - - // burn from both accounts even though we technically only need to burn from one - for (conditional_mint, user_conditional_token_account) in [ - ( - &accs.conditional_on_finalize_token_mint, - &accs.user_conditional_on_finalize_token_account, - ), - ( - &accs.conditional_on_revert_token_mint, - &accs.user_conditional_on_revert_token_account, - ), - ] { - token::burn( - CpiContext::new( - accs.token_program.to_account_info(), - Burn { - mint: conditional_mint.to_account_info(), - from: user_conditional_token_account.to_account_info(), - authority: accs.authority.to_account_info(), - }, - ), - user_conditional_token_account.amount, - )?; - } - - let redeemable = match vault_status { - VaultStatus::Finalized => pre_conditional_on_finalize_balance, - VaultStatus::Reverted => pre_conditional_on_revert_balance, - _ => unreachable!(), - }; - - token::transfer( - CpiContext::new_with_signer( - accs.token_program.to_account_info(), - Transfer { - from: accs.vault_underlying_token_account.to_account_info(), - to: accs.user_underlying_token_account.to_account_info(), - authority: accs.vault.to_account_info(), - }, - signer, - ), - redeemable, - )?; - - ctx.accounts - .user_conditional_on_finalize_token_account - .reload()?; - ctx.accounts - .user_conditional_on_revert_token_account - .reload()?; - ctx.accounts.vault_underlying_token_account.reload()?; - ctx.accounts.conditional_on_finalize_token_mint.reload()?; - ctx.accounts.conditional_on_revert_token_mint.reload()?; - - let post_user_conditional_on_finalize_balance = ctx - .accounts - .user_conditional_on_finalize_token_account - .amount; - let post_user_conditional_on_revert_balance = - ctx.accounts.user_conditional_on_revert_token_account.amount; - let post_vault_underlying_balance = ctx.accounts.vault_underlying_token_account.amount; - let post_finalize_mint_supply = ctx.accounts.conditional_on_finalize_token_mint.supply; - let post_revert_mint_supply = ctx.accounts.conditional_on_revert_token_mint.supply; - - assert!(post_user_conditional_on_finalize_balance == 0); - assert!(post_user_conditional_on_revert_balance == 0); - assert!( - post_finalize_mint_supply - == pre_finalize_mint_supply - pre_conditional_on_finalize_balance - ); - assert!( - post_revert_mint_supply == pre_revert_mint_supply - pre_conditional_on_revert_balance - ); - match vault_status { - VaultStatus::Finalized => { - assert!( - post_vault_underlying_balance - == pre_vault_underlying_balance - pre_conditional_on_finalize_balance - ); - } - VaultStatus::Reverted => { - assert!(vault_status == VaultStatus::Reverted); - assert!( - post_vault_underlying_balance - == pre_vault_underlying_balance - pre_conditional_on_revert_balance - ); - } - _ => unreachable!(), - } - - Ok(()) - } -} diff --git a/programs/conditional_vault/src/instructions/redeem_tokens.rs b/programs/conditional_vault/src/instructions/redeem_tokens.rs index 96b71240..4b5731fc 100644 --- a/programs/conditional_vault/src/instructions/redeem_tokens.rs +++ b/programs/conditional_vault/src/instructions/redeem_tokens.rs @@ -27,7 +27,7 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { let vault = &accs.vault; let question = &accs.question; - let seeds = generate_new_vault_seeds!(vault); + let seeds = generate_vault_seeds!(vault); let signer = &[&seeds[..]]; let user_underlying_balance_before = accs.user_underlying_token_account.amount; @@ -103,14 +103,20 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { assert!(acc.amount == 0); } - for (mint, expected_supply) in conditional_token_mints.iter_mut().zip(expected_future_supplies.iter()) { + for (mint, expected_supply) in conditional_token_mints + .iter_mut() + .zip(expected_future_supplies.iter()) + { mint.reload()?; assert!(mint.supply == *expected_supply); } ctx.accounts.vault.invariant( &ctx.accounts.question, - conditional_token_mints.iter().map(|mint| mint.supply).collect::>(), + conditional_token_mints + .iter() + .map(|mint| mint.supply) + .collect::>(), ctx.accounts.vault_underlying_token_account.amount, )?; diff --git a/programs/conditional_vault/src/instructions/resolve_question.rs b/programs/conditional_vault/src/instructions/resolve_question.rs index 66af74a5..e6c84d0e 100644 --- a/programs/conditional_vault/src/instructions/resolve_question.rs +++ b/programs/conditional_vault/src/instructions/resolve_question.rs @@ -26,11 +26,7 @@ impl ResolveQuestion<'_> { question.payout_denominator = args.payout_numerators.iter().sum(); question.payout_numerators = args.payout_numerators; - require_gt!( - question.payout_denominator, - 0, - VaultError::PayoutZero - ); + require_gt!(question.payout_denominator, 0, VaultError::PayoutZero); Ok(()) } diff --git a/programs/conditional_vault/src/instructions/settle_conditional_vault.rs b/programs/conditional_vault/src/instructions/settle_conditional_vault.rs deleted file mode 100644 index 0fb51e75..00000000 --- a/programs/conditional_vault/src/instructions/settle_conditional_vault.rs +++ /dev/null @@ -1,30 +0,0 @@ -use super::*; - -#[derive(Accounts)] -pub struct SettleConditionalVault<'info> { - pub settlement_authority: Signer<'info>, - #[account( - mut, - has_one = settlement_authority, - )] - pub vault: Account<'info, ConditionalVault>, -} - -impl SettleConditionalVault<'_> { - pub fn validate(&self) -> Result<()> { - require!( - self.vault.status == VaultStatus::Active, - VaultError::VaultAlreadySettled - ); - - Ok(()) - } - - pub fn handle(ctx: Context, new_status: VaultStatus) -> Result<()> { - let vault = &mut ctx.accounts.vault; - - vault.status = new_status; - - Ok(()) - } -} diff --git a/programs/conditional_vault/src/instructions/split_tokens.rs b/programs/conditional_vault/src/instructions/split_tokens.rs index 3f39a1d6..472bc9d1 100644 --- a/programs/conditional_vault/src/instructions/split_tokens.rs +++ b/programs/conditional_vault/src/instructions/split_tokens.rs @@ -24,7 +24,7 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { let vault = &accs.vault; - let seeds = generate_new_vault_seeds!(vault); + let seeds = generate_vault_seeds!(vault); let signer = &[&seeds[..]]; token::transfer( @@ -75,7 +75,10 @@ impl<'info, 'c: 'info> InteractWithNewVault<'info> { ctx.accounts.vault.invariant( &ctx.accounts.question, - conditional_token_mints.iter().map(|mint| mint.supply).collect::>(), + conditional_token_mints + .iter() + .map(|mint| mint.supply) + .collect::>(), ctx.accounts.vault_underlying_token_account.amount, )?; diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index b045627a..bdb043aa 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -52,10 +52,10 @@ pub mod conditional_vault { ResolveQuestion::handle(ctx, args) } - pub fn initialize_new_conditional_vault<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, InitializeNewConditionalVault<'info>>, + pub fn initialize_conditional_vault<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, InitializeConditionalVault<'info>>, ) -> Result<()> { - InitializeNewConditionalVault::handle(ctx) + InitializeConditionalVault::handle(ctx) } pub fn split_tokens<'c: 'info, 'info>( @@ -79,50 +79,11 @@ pub mod conditional_vault { InteractWithNewVault::handle_redeem_tokens(ctx) } - // pub fn split_tokens() - // merge tokens - - // redeem tokens - - pub fn initialize_conditional_vault( - ctx: Context, - args: InitializeConditionalVaultArgs, - ) -> Result<()> { - InitializeConditionalVault::handle(ctx, args) - } - - #[access_control(ctx.accounts.validate())] - pub fn add_metadata_to_conditional_tokens( - ctx: Context, - args: AddMetadataToConditionalTokensArgs, - ) -> Result<()> { - AddMetadataToConditionalTokens::handle(ctx, args) - } - // #[access_control(ctx.accounts.validate())] - // pub fn settle_conditional_vault( - // ctx: Context, - // new_status: VaultStatus, + // pub fn add_metadata_to_conditional_tokens( + // ctx: Context, + // args: AddMetadataToConditionalTokensArgs, // ) -> Result<()> { - // SettleConditionalVault::handle(ctx, new_status) + // AddMetadataToConditionalTokens::handle(ctx, args) // } - - #[access_control(ctx.accounts.validate_merge_conditional_tokens())] - pub fn merge_conditional_tokens_for_underlying_tokens( - ctx: Context, - amount: u64, - ) -> Result<()> { - InteractWithVault::handle_merge_conditional_tokens(ctx, amount) - } - - pub fn mint_conditional_tokens(ctx: Context, amount: u64) -> Result<()> { - InteractWithVault::handle_mint_conditional_tokens(ctx, amount) - } - - #[access_control(ctx.accounts.validate_redeem_conditional_tokens())] - pub fn redeem_conditional_tokens_for_underlying_tokens( - ctx: Context, - ) -> Result<()> { - InteractWithVault::handle_redeem_conditional_tokens(ctx) - } } diff --git a/programs/conditional_vault/src/state/conditional_vault.rs b/programs/conditional_vault/src/state/conditional_vault.rs index 171cd07a..3bd4f2da 100644 --- a/programs/conditional_vault/src/state/conditional_vault.rs +++ b/programs/conditional_vault/src/state/conditional_vault.rs @@ -8,7 +8,7 @@ pub enum VaultStatus { } #[account] -pub struct NewConditionalVault { +pub struct ConditionalVault { pub question: Pubkey, pub underlying_token_mint: Pubkey, pub underlying_token_account: Pubkey, @@ -17,7 +17,7 @@ pub struct NewConditionalVault { pub decimals: u8, } -impl NewConditionalVault { +impl ConditionalVault { /// Checks that the vault's assets are always greater than its potential /// liabilities. Should be called anytime you mint or burn conditional /// tokens. @@ -45,7 +45,8 @@ impl NewConditionalVault { .iter() .enumerate() .map(|(i, supply)| { - *supply * question.payout_numerators[i] as u64 / question.payout_denominator as u64 + *supply * question.payout_numerators[i] as u64 + / question.payout_denominator as u64 }) .sum::() }; @@ -57,7 +58,7 @@ impl NewConditionalVault { } #[macro_export] -macro_rules! generate_new_vault_seeds { +macro_rules! generate_vault_seeds { ($vault:expr) => {{ &[ b"conditional_vault", @@ -67,32 +68,3 @@ macro_rules! generate_new_vault_seeds { ] }}; } - -#[account] -pub struct ConditionalVault { - pub status: VaultStatus, - /// The account that can either finalize the vault to make conditional tokens - /// redeemable for underlying tokens or revert the vault to make deposit - /// slips redeemable for underlying tokens. - pub settlement_authority: Pubkey, - /// The mint of the tokens that are deposited into the vault. - pub underlying_token_mint: Pubkey, - /// The vault's storage account for deposited funds. - pub underlying_token_account: Pubkey, - pub conditional_on_finalize_token_mint: Pubkey, - pub conditional_on_revert_token_mint: Pubkey, - pub pda_bump: u8, - pub decimals: u8, -} - -#[macro_export] -macro_rules! generate_vault_seeds { - ($vault:expr) => {{ - &[ - b"conditional_vault", - $vault.settlement_authority.as_ref(), - $vault.underlying_token_mint.as_ref(), - &[$vault.pda_bump], - ] - }}; -} \ No newline at end of file diff --git a/programs/conditional_vault/src/state/question.rs b/programs/conditional_vault/src/state/question.rs index 724ee7ad..c5d68173 100644 --- a/programs/conditional_vault/src/state/question.rs +++ b/programs/conditional_vault/src/state/question.rs @@ -9,7 +9,7 @@ use super::*; /// /// Questions have 2 or more possible outcomes. For a question like "will this /// proposal pass," the outcomes are "yes" and "no." For a question like "who -/// will be hired," the outcomes could be "Alice," "Bob," and "neither." +/// will be hired," the outcomes could be "Alice," "Bob," and "neither." /// /// Outcomes resolve to a number between 0 and 1. Binary questions like "will /// this proposal pass" have outcomes that resolve to exactly 0 or 1. You can @@ -18,7 +18,7 @@ use super::*; /// "ineffective" and "effective." If the grant committee deems the grant 70% /// effective, the "effective" outcome would resolve to 0.7 and the "ineffective" /// outcome would resolve to 0.3. -/// +/// /// Once resolved, the sum of all outcome resolutions is exactly 1. #[account] pub struct Question { @@ -36,4 +36,4 @@ impl Question { pub fn is_resolved(&self) -> bool { self.payout_denominator != 0 } -} \ No newline at end of file +} diff --git a/programs/optimistic_timelock/src/lib.rs b/programs/optimistic_timelock/src/lib.rs index 2bfc1c28..a068207a 100644 --- a/programs/optimistic_timelock/src/lib.rs +++ b/programs/optimistic_timelock/src/lib.rs @@ -38,7 +38,11 @@ impl Timelock { pub fn check_authority(&self, authority: Pubkey) -> Result { if authority == self.authority { Ok(AuthorityType::TimelockAuthority) - } else if self.optimistic_proposers.iter().any(|proposer| proposer.pubkey == authority) { + } else if self + .optimistic_proposers + .iter() + .any(|proposer| proposer.pubkey == authority) + { Ok(AuthorityType::OptimisticProposer) } else { Err(TimelockError::NoAuthority.into()) @@ -136,7 +140,10 @@ pub mod optimistic_timelock { Ok(()) } - pub fn set_optimistic_proposer_cooldown_slots(ctx: Context, cooldown_slots: u64) -> Result<()> { + pub fn set_optimistic_proposer_cooldown_slots( + ctx: Context, + cooldown_slots: u64, + ) -> Result<()> { let timelock = &mut ctx.accounts.timelock; timelock.optimistic_proposer_cooldown_slots = cooldown_slots; @@ -148,7 +155,11 @@ pub mod optimistic_timelock { let timelock = &mut ctx.accounts.timelock; // idempotent - if timelock.optimistic_proposers.iter().any(|enq| enq.pubkey == enqueuer) { + if timelock + .optimistic_proposers + .iter() + .any(|enq| enq.pubkey == enqueuer) + { return Ok(()); } @@ -160,7 +171,10 @@ pub mod optimistic_timelock { Ok(()) } - pub fn remove_optimistic_proposer(ctx: Context, optimistic_proposer: Pubkey) -> Result<()> { + pub fn remove_optimistic_proposer( + ctx: Context, + optimistic_proposer: Pubkey, + ) -> Result<()> { let timelock = &mut ctx.accounts.timelock; let index = timelock @@ -455,7 +469,9 @@ pub enum TimelockError { CannotExecuteTransactions, #[msg("The signer is neither the timelock authority nor an optimistic proposer")] NoAuthority, - #[msg("Optimistic proposers can't cancel transaction batches enqueued by the timelock authority")] + #[msg( + "Optimistic proposers can't cancel transaction batches enqueued by the timelock authority" + )] InsufficientPermissions, #[msg("This optimistic proposer is still in its cooldown period")] OptimisticProposerCooldown, diff --git a/sdk/src/AutocratClient.ts b/sdk/src/AutocratClient.ts index 56e9644a..e71db721 100644 --- a/sdk/src/AutocratClient.ts +++ b/sdk/src/AutocratClient.ts @@ -323,14 +323,10 @@ export class AutocratClient { // it's important that these happen in a single atomic transaction await this.vaultClient - .initializeNewVaultIx(question, storedDao.tokenMint, 2) + .initializeVaultIx(question, storedDao.tokenMint, 2) .postInstructions( await InstructionUtils.getInstructions( - this.vaultClient.initializeNewVaultIx( - question, - storedDao.usdcMint, - 2 - ), + this.vaultClient.initializeVaultIx(question, storedDao.usdcMint, 2), this.ammClient.createAmmIx( passBaseMint, passQuoteMint, diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 525e35c3..50d50f5e 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -66,7 +66,7 @@ export class ConditionalVaultClient { } async fetchVault(vault: PublicKey) { - return this.vaultProgram.account.newConditionalVault.fetch(vault); + return this.vaultProgram.account.conditionalVault.fetch(vault); } async getVault(vault: PublicKey) { @@ -113,7 +113,7 @@ export class ConditionalVaultClient { return question; } - initializeNewVaultIx( + initializeVaultIx( question: PublicKey, underlyingTokenMint: PublicKey, numOutcomes: number @@ -141,7 +141,7 @@ export class ConditionalVaultClient { ); return this.vaultProgram.methods - .initializeNewConditionalVault() + .initializeConditionalVault() .accounts({ vault, question, @@ -167,7 +167,7 @@ export class ConditionalVaultClient { ); } - async initializeNewVault( + async initializeVault( question: PublicKey, underlyingTokenMint: PublicKey, numOutcomes: number @@ -178,7 +178,7 @@ export class ConditionalVaultClient { underlyingTokenMint ); - await this.initializeNewVaultIx( + await this.initializeVaultIx( question, underlyingTokenMint, numOutcomes @@ -203,27 +203,6 @@ export class ConditionalVaultClient { .signers([oracle]); } - async mintConditionalTokens( - vault: PublicKey, - uiAmount: number, - user?: PublicKey | Keypair - ) { - const storedVault = await this.getVault(vault); - - return ( - this.mintConditionalTokensIx( - vault, - storedVault.underlyingTokenMint, - new BN(uiAmount).mul(new BN(10).pow(new BN(storedVault.decimals))), - user - ) - // .preInstructions([ - // createAssociatedTokenAccountIdempotentInstruction(this.provider.publicKey, ) - // ]) - .rpc() - ); - } - getConditionalTokenMints(vault: PublicKey, numOutcomes: number): PublicKey[] { let conditionalTokenMintAddrs = []; for (let i = 0; i < numOutcomes; i++) { @@ -441,261 +420,48 @@ export class ConditionalVaultClient { return ix; } - mintConditionalTokensIx( - vault: PublicKey, - underlyingTokenMint: PublicKey, - amount: BN, - user?: PublicKey | Keypair - ) { - const userPubkey = - user instanceof Keypair - ? user.publicKey - : user || this.provider.publicKey; - - const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( - this.vaultProgram.programId, - vault - ); - const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( - this.vaultProgram.programId, - vault - ); - - let userConditionalOnFinalizeTokenAccount = getAssociatedTokenAddressSync( - conditionalOnFinalizeTokenMint, - userPubkey - ); - - let userConditionalOnRevertTokenAccount = getAssociatedTokenAddressSync( - conditionalOnRevertTokenMint, - userPubkey - ); - - let ix = this.vaultProgram.methods - .mintConditionalTokens(amount) - .accounts({ - authority: userPubkey, - vault, - vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( - underlyingTokenMint, - vault, - true - ), - userUnderlyingTokenAccount: getAssociatedTokenAddressSync( - underlyingTokenMint, - userPubkey, - true - ), - conditionalOnFinalizeTokenMint, - userConditionalOnFinalizeTokenAccount, - conditionalOnRevertTokenMint, - userConditionalOnRevertTokenAccount, - }) - .preInstructions([ - createAssociatedTokenAccountIdempotentInstruction( - userPubkey, - userConditionalOnFinalizeTokenAccount, - userPubkey, - conditionalOnFinalizeTokenMint - ), - createAssociatedTokenAccountIdempotentInstruction( - userPubkey, - userConditionalOnRevertTokenAccount, - userPubkey, - conditionalOnRevertTokenMint - ), - ]); - if (user instanceof Keypair) { - ix = ix.signers([user]); - } - - return ix; - } - - initializeVaultIx( - settlementAuthority: PublicKey, - underlyingTokenMint: PublicKey - ): MethodsBuilder { - const [vault] = getVaultAddr( - this.vaultProgram.programId, - settlementAuthority, - underlyingTokenMint - ); - - const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( - this.vaultProgram.programId, - vault - ); - const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( - this.vaultProgram.programId, - vault - ); - - const vaultUnderlyingTokenAccount = getAssociatedTokenAddressSync( - underlyingTokenMint, - vault, - true - ); - - return this.vaultProgram.methods - .initializeConditionalVault({ settlementAuthority }) - .accounts({ - vault, - underlyingTokenMint, - vaultUnderlyingTokenAccount, - conditionalOnFinalizeTokenMint, - conditionalOnRevertTokenMint, - }) - .preInstructions([ - createAssociatedTokenAccountIdempotentInstruction( - this.provider.publicKey, - vaultUnderlyingTokenAccount, - vault, - underlyingTokenMint - ), - ]); - } - - addMetadataToConditionalTokensIx( - vault: PublicKey, - underlyingTokenMint: PublicKey, - proposalNumber: number, - onFinalizeUri: string, - onRevertUri: string - ) { - const [underlyingTokenMetadata] = getMetadataAddr(underlyingTokenMint); - - const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( - this.vaultProgram.programId, - vault - ); - const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( - this.vaultProgram.programId, - vault - ); - - const [conditionalOnFinalizeTokenMetadata] = getMetadataAddr( - conditionalOnFinalizeTokenMint - ); - - const [conditionalOnRevertTokenMetadata] = getMetadataAddr( - conditionalOnRevertTokenMint - ); - - return this.vaultProgram.methods - .addMetadataToConditionalTokens({ - proposalNumber: new BN(proposalNumber), - onFinalizeUri, - onRevertUri, - }) - .accounts({ - payer: this.provider.publicKey, - vault, - underlyingTokenMint, - underlyingTokenMetadata, - conditionalOnFinalizeTokenMint, - conditionalOnRevertTokenMint, - conditionalOnFinalizeTokenMetadata, - conditionalOnRevertTokenMetadata, - tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, - }); - } - - redeemConditionalTokensIx(vault: PublicKey, underlyingTokenMint: PublicKey) { - const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( - this.vaultProgram.programId, - vault - ); - const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( - this.vaultProgram.programId, - vault - ); - - return this.vaultProgram.methods - .redeemConditionalTokensForUnderlyingTokens() - .accounts({ - authority: this.provider.publicKey, - vault, - vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( - underlyingTokenMint, - vault, - true - ), - userUnderlyingTokenAccount: getAssociatedTokenAddressSync( - underlyingTokenMint, - this.provider.publicKey, - true - ), - conditionalOnFinalizeTokenMint, - userConditionalOnFinalizeTokenAccount: getAssociatedTokenAddressSync( - conditionalOnFinalizeTokenMint, - this.provider.publicKey - ), - conditionalOnRevertTokenMint, - userConditionalOnRevertTokenAccount: getAssociatedTokenAddressSync( - conditionalOnRevertTokenMint, - this.provider.publicKey - ), - }); - } - - mergeConditionalTokensIx( - vault: PublicKey, - underlyingTokenMint: PublicKey, - amount: BN - ) { - const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( - this.vaultProgram.programId, - vault - ); - const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( - this.vaultProgram.programId, - vault - ); - - return this.vaultProgram.methods - .mergeConditionalTokensForUnderlyingTokens(amount) - .accounts({ - authority: this.provider.publicKey, - vault, - vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( - underlyingTokenMint, - vault, - true - ), - userUnderlyingTokenAccount: getAssociatedTokenAddressSync( - underlyingTokenMint, - this.provider.publicKey, - true - ), - conditionalOnFinalizeTokenMint, - userConditionalOnFinalizeTokenAccount: getAssociatedTokenAddressSync( - conditionalOnFinalizeTokenMint, - this.provider.publicKey - ), - conditionalOnRevertTokenMint, - userConditionalOnRevertTokenAccount: getAssociatedTokenAddressSync( - conditionalOnRevertTokenMint, - this.provider.publicKey - ), - }); - } - - async initializeVault( - settlementAuthority: PublicKey, - underlyingTokenMint: PublicKey - ): Promise { - const [vault] = getVaultAddr( - this.vaultProgram.programId, - settlementAuthority, - underlyingTokenMint - ); - - await this.initializeVaultIx( - settlementAuthority, - underlyingTokenMint - ).rpc(); - - return vault; - } + // addMetadataToConditionalTokensIx( + // vault: PublicKey, + // underlyingTokenMint: PublicKey, + // proposalNumber: number, + // onFinalizeUri: string, + // onRevertUri: string + // ) { + // const [underlyingTokenMetadata] = getMetadataAddr(underlyingTokenMint); + + // const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( + // this.vaultProgram.programId, + // vault + // ); + // const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + // this.vaultProgram.programId, + // vault + // ); + + // const [conditionalOnFinalizeTokenMetadata] = getMetadataAddr( + // conditionalOnFinalizeTokenMint + // ); + + // const [conditionalOnRevertTokenMetadata] = getMetadataAddr( + // conditionalOnRevertTokenMint + // ); + + // return this.vaultProgram.methods + // .addMetadataToConditionalTokens({ + // proposalNumber: new BN(proposalNumber), + // onFinalizeUri, + // onRevertUri, + // }) + // .accounts({ + // payer: this.provider.publicKey, + // vault, + // underlyingTokenMint, + // underlyingTokenMetadata, + // conditionalOnFinalizeTokenMint, + // conditionalOnRevertTokenMint, + // conditionalOnFinalizeTokenMetadata, + // conditionalOnRevertTokenMetadata, + // tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, + // }); + // } } diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index 5185cd46..2a3a3a71 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -54,7 +54,7 @@ export type ConditionalVault = { ]; }, { - name: "initializeNewConditionalVault"; + name: "initializeConditionalVault"; accounts: [ { name: "vault"; @@ -216,300 +216,11 @@ export type ConditionalVault = { } ]; args: []; - }, - { - name: "initializeConditionalVault"; - accounts: [ - { - name: "vault"; - isMut: true; - isSigner: false; - }, - { - name: "underlyingTokenMint"; - isMut: false; - isSigner: false; - }, - { - name: "conditionalOnFinalizeTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "conditionalOnRevertTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "vaultUnderlyingTokenAccount"; - isMut: false; - isSigner: false; - }, - { - name: "payer"; - isMut: true; - isSigner: true; - }, - { - name: "tokenProgram"; - isMut: false; - isSigner: false; - }, - { - name: "associatedTokenProgram"; - isMut: false; - isSigner: false; - }, - { - name: "systemProgram"; - isMut: false; - isSigner: false; - } - ]; - args: [ - { - name: "args"; - type: { - defined: "InitializeConditionalVaultArgs"; - }; - } - ]; - }, - { - name: "addMetadataToConditionalTokens"; - accounts: [ - { - name: "payer"; - isMut: true; - isSigner: true; - }, - { - name: "vault"; - isMut: true; - isSigner: false; - }, - { - name: "underlyingTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "underlyingTokenMetadata"; - isMut: false; - isSigner: false; - }, - { - name: "conditionalOnFinalizeTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "conditionalOnRevertTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "conditionalOnFinalizeTokenMetadata"; - isMut: true; - isSigner: false; - }, - { - name: "conditionalOnRevertTokenMetadata"; - isMut: true; - isSigner: false; - }, - { - name: "tokenMetadataProgram"; - isMut: false; - isSigner: false; - }, - { - name: "systemProgram"; - isMut: false; - isSigner: false; - }, - { - name: "rent"; - isMut: false; - isSigner: false; - } - ]; - args: [ - { - name: "args"; - type: { - defined: "AddMetadataToConditionalTokensArgs"; - }; - } - ]; - }, - { - name: "mergeConditionalTokensForUnderlyingTokens"; - accounts: [ - { - name: "vault"; - isMut: false; - isSigner: false; - }, - { - name: "conditionalOnFinalizeTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "conditionalOnRevertTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "vaultUnderlyingTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "authority"; - isMut: false; - isSigner: true; - }, - { - name: "userConditionalOnFinalizeTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "userConditionalOnRevertTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "userUnderlyingTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "tokenProgram"; - isMut: false; - isSigner: false; - } - ]; - args: [ - { - name: "amount"; - type: "u64"; - } - ]; - }, - { - name: "mintConditionalTokens"; - accounts: [ - { - name: "vault"; - isMut: false; - isSigner: false; - }, - { - name: "conditionalOnFinalizeTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "conditionalOnRevertTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "vaultUnderlyingTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "authority"; - isMut: false; - isSigner: true; - }, - { - name: "userConditionalOnFinalizeTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "userConditionalOnRevertTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "userUnderlyingTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "tokenProgram"; - isMut: false; - isSigner: false; - } - ]; - args: [ - { - name: "amount"; - type: "u64"; - } - ]; - }, - { - name: "redeemConditionalTokensForUnderlyingTokens"; - accounts: [ - { - name: "vault"; - isMut: false; - isSigner: false; - }, - { - name: "conditionalOnFinalizeTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "conditionalOnRevertTokenMint"; - isMut: true; - isSigner: false; - }, - { - name: "vaultUnderlyingTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "authority"; - isMut: false; - isSigner: true; - }, - { - name: "userConditionalOnFinalizeTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "userConditionalOnRevertTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "userUnderlyingTokenAccount"; - isMut: true; - isSigner: false; - }, - { - name: "tokenProgram"; - isMut: false; - isSigner: false; - } - ]; - args: []; } ]; accounts: [ { - name: "newConditionalVault"; + name: "conditionalVault"; type: { kind: "struct"; fields: [ @@ -542,55 +253,6 @@ export type ConditionalVault = { ]; }; }, - { - name: "conditionalVault"; - type: { - kind: "struct"; - fields: [ - { - name: "status"; - type: { - defined: "VaultStatus"; - }; - }, - { - name: "settlementAuthority"; - docs: [ - "The account that can either finalize the vault to make conditional tokens", - "redeemable for underlying tokens or revert the vault to make deposit", - "slips redeemable for underlying tokens." - ]; - type: "publicKey"; - }, - { - name: "underlyingTokenMint"; - docs: ["The mint of the tokens that are deposited into the vault."]; - type: "publicKey"; - }, - { - name: "underlyingTokenAccount"; - docs: ["The vault's storage account for deposited funds."]; - type: "publicKey"; - }, - { - name: "conditionalOnFinalizeTokenMint"; - type: "publicKey"; - }, - { - name: "conditionalOnRevertTokenMint"; - type: "publicKey"; - }, - { - name: "pdaBump"; - type: "u8"; - }, - { - name: "decimals"; - type: "u8"; - } - ]; - }; - }, { name: "question"; docs: [ @@ -643,38 +305,6 @@ export type ConditionalVault = { } ]; types: [ - { - name: "AddMetadataToConditionalTokensArgs"; - type: { - kind: "struct"; - fields: [ - { - name: "proposalNumber"; - type: "u64"; - }, - { - name: "onFinalizeUri"; - type: "string"; - }, - { - name: "onRevertUri"; - type: "string"; - } - ]; - }; - }, - { - name: "InitializeConditionalVaultArgs"; - type: { - kind: "struct"; - fields: [ - { - name: "settlementAuthority"; - type: "publicKey"; - } - ]; - }; - }, { name: "InitializeQuestionArgs"; type: { @@ -796,231 +426,67 @@ export type ConditionalVault = { msg: "User conditional token account mint does not match conditional mint"; }, { - code: 6013; - name: "PayoutZero"; - msg: "Payouts must sum to 1 or more"; - } - ]; -}; - -export const IDL: ConditionalVault = { - version: "0.4.0", - name: "conditional_vault", - instructions: [ - { - name: "initializeQuestion", - accounts: [ - { - name: "question", - isMut: true, - isSigner: false, - }, - { - name: "payer", - isMut: true, - isSigner: true, - }, - { - name: "systemProgram", - isMut: false, - isSigner: false, - }, - ], - args: [ - { - name: "args", - type: { - defined: "InitializeQuestionArgs", - }, - }, - ], - }, - { - name: "resolveQuestion", - accounts: [ - { - name: "question", - isMut: true, - isSigner: false, - }, - { - name: "oracle", - isMut: false, - isSigner: true, - }, - ], - args: [ - { - name: "args", - type: { - defined: "ResolveQuestionArgs", - }, - }, - ], - }, - { - name: "initializeNewConditionalVault", - accounts: [ - { - name: "vault", - isMut: true, - isSigner: false, - }, - { - name: "question", - isMut: false, - isSigner: false, - }, - { - name: "underlyingTokenMint", - isMut: false, - isSigner: false, - }, - { - name: "vaultUnderlyingTokenAccount", - isMut: false, - isSigner: false, - }, - { - name: "payer", - isMut: true, - isSigner: true, - }, - { - name: "tokenProgram", - isMut: false, - isSigner: false, - }, - { - name: "associatedTokenProgram", - isMut: false, - isSigner: false, - }, - { - name: "systemProgram", - isMut: false, - isSigner: false, - }, - ], - args: [], - }, - { - name: "splitTokens", - accounts: [ - { - name: "question", - isMut: false, - isSigner: false, - }, - { - name: "vault", - isMut: false, - isSigner: false, - }, - { - name: "vaultUnderlyingTokenAccount", - isMut: true, - isSigner: false, - }, - { - name: "authority", - isMut: false, - isSigner: true, - }, - { - name: "userUnderlyingTokenAccount", - isMut: true, - isSigner: false, - }, - { - name: "tokenProgram", - isMut: false, - isSigner: false, - }, - ], - args: [ - { - name: "amount", - type: "u64", - }, - ], - }, + code: 6013; + name: "PayoutZero"; + msg: "Payouts must sum to 1 or more"; + } + ]; +}; + +export const IDL: ConditionalVault = { + version: "0.4.0", + name: "conditional_vault", + instructions: [ { - name: "mergeTokens", + name: "initializeQuestion", accounts: [ { name: "question", - isMut: false, - isSigner: false, - }, - { - name: "vault", - isMut: false, - isSigner: false, - }, - { - name: "vaultUnderlyingTokenAccount", isMut: true, isSigner: false, }, { - name: "authority", - isMut: false, - isSigner: true, - }, - { - name: "userUnderlyingTokenAccount", + name: "payer", isMut: true, - isSigner: false, + isSigner: true, }, { - name: "tokenProgram", + name: "systemProgram", isMut: false, isSigner: false, }, ], args: [ { - name: "amount", - type: "u64", + name: "args", + type: { + defined: "InitializeQuestionArgs", + }, }, ], }, { - name: "redeemTokens", + name: "resolveQuestion", accounts: [ { name: "question", - isMut: false, - isSigner: false, - }, - { - name: "vault", - isMut: false, - isSigner: false, - }, - { - name: "vaultUnderlyingTokenAccount", isMut: true, isSigner: false, }, { - name: "authority", + name: "oracle", isMut: false, isSigner: true, }, + ], + args: [ { - name: "userUnderlyingTokenAccount", - isMut: true, - isSigner: false, - }, - { - name: "tokenProgram", - isMut: false, - isSigner: false, + name: "args", + type: { + defined: "ResolveQuestionArgs", + }, }, ], - args: [], }, { name: "initializeConditionalVault", @@ -1031,18 +497,13 @@ export const IDL: ConditionalVault = { isSigner: false, }, { - name: "underlyingTokenMint", + name: "question", isMut: false, isSigner: false, }, { - name: "conditionalOnFinalizeTokenMint", - isMut: true, - isSigner: false, - }, - { - name: "conditionalOnRevertTokenMint", - isMut: true, + name: "underlyingTokenMint", + isMut: false, isSigner: false, }, { @@ -1071,101 +532,21 @@ export const IDL: ConditionalVault = { isSigner: false, }, ], - args: [ - { - name: "args", - type: { - defined: "InitializeConditionalVaultArgs", - }, - }, - ], + args: [], }, { - name: "addMetadataToConditionalTokens", + name: "splitTokens", accounts: [ { - name: "payer", - isMut: true, - isSigner: true, - }, - { - name: "vault", - isMut: true, - isSigner: false, - }, - { - name: "underlyingTokenMint", - isMut: true, - isSigner: false, - }, - { - name: "underlyingTokenMetadata", - isMut: false, - isSigner: false, - }, - { - name: "conditionalOnFinalizeTokenMint", - isMut: true, - isSigner: false, - }, - { - name: "conditionalOnRevertTokenMint", - isMut: true, - isSigner: false, - }, - { - name: "conditionalOnFinalizeTokenMetadata", - isMut: true, - isSigner: false, - }, - { - name: "conditionalOnRevertTokenMetadata", - isMut: true, - isSigner: false, - }, - { - name: "tokenMetadataProgram", - isMut: false, - isSigner: false, - }, - { - name: "systemProgram", - isMut: false, - isSigner: false, - }, - { - name: "rent", + name: "question", isMut: false, isSigner: false, }, - ], - args: [ - { - name: "args", - type: { - defined: "AddMetadataToConditionalTokensArgs", - }, - }, - ], - }, - { - name: "mergeConditionalTokensForUnderlyingTokens", - accounts: [ { name: "vault", isMut: false, isSigner: false, }, - { - name: "conditionalOnFinalizeTokenMint", - isMut: true, - isSigner: false, - }, - { - name: "conditionalOnRevertTokenMint", - isMut: true, - isSigner: false, - }, { name: "vaultUnderlyingTokenAccount", isMut: true, @@ -1176,16 +557,6 @@ export const IDL: ConditionalVault = { isMut: false, isSigner: true, }, - { - name: "userConditionalOnFinalizeTokenAccount", - isMut: true, - isSigner: false, - }, - { - name: "userConditionalOnRevertTokenAccount", - isMut: true, - isSigner: false, - }, { name: "userUnderlyingTokenAccount", isMut: true, @@ -1205,21 +576,16 @@ export const IDL: ConditionalVault = { ], }, { - name: "mintConditionalTokens", + name: "mergeTokens", accounts: [ { - name: "vault", + name: "question", isMut: false, isSigner: false, }, { - name: "conditionalOnFinalizeTokenMint", - isMut: true, - isSigner: false, - }, - { - name: "conditionalOnRevertTokenMint", - isMut: true, + name: "vault", + isMut: false, isSigner: false, }, { @@ -1232,16 +598,6 @@ export const IDL: ConditionalVault = { isMut: false, isSigner: true, }, - { - name: "userConditionalOnFinalizeTokenAccount", - isMut: true, - isSigner: false, - }, - { - name: "userConditionalOnRevertTokenAccount", - isMut: true, - isSigner: false, - }, { name: "userUnderlyingTokenAccount", isMut: true, @@ -1261,21 +617,16 @@ export const IDL: ConditionalVault = { ], }, { - name: "redeemConditionalTokensForUnderlyingTokens", + name: "redeemTokens", accounts: [ { - name: "vault", + name: "question", isMut: false, isSigner: false, }, { - name: "conditionalOnFinalizeTokenMint", - isMut: true, - isSigner: false, - }, - { - name: "conditionalOnRevertTokenMint", - isMut: true, + name: "vault", + isMut: false, isSigner: false, }, { @@ -1288,16 +639,6 @@ export const IDL: ConditionalVault = { isMut: false, isSigner: true, }, - { - name: "userConditionalOnFinalizeTokenAccount", - isMut: true, - isSigner: false, - }, - { - name: "userConditionalOnRevertTokenAccount", - isMut: true, - isSigner: false, - }, { name: "userUnderlyingTokenAccount", isMut: true, @@ -1314,7 +655,7 @@ export const IDL: ConditionalVault = { ], accounts: [ { - name: "newConditionalVault", + name: "conditionalVault", type: { kind: "struct", fields: [ @@ -1347,55 +688,6 @@ export const IDL: ConditionalVault = { ], }, }, - { - name: "conditionalVault", - type: { - kind: "struct", - fields: [ - { - name: "status", - type: { - defined: "VaultStatus", - }, - }, - { - name: "settlementAuthority", - docs: [ - "The account that can either finalize the vault to make conditional tokens", - "redeemable for underlying tokens or revert the vault to make deposit", - "slips redeemable for underlying tokens.", - ], - type: "publicKey", - }, - { - name: "underlyingTokenMint", - docs: ["The mint of the tokens that are deposited into the vault."], - type: "publicKey", - }, - { - name: "underlyingTokenAccount", - docs: ["The vault's storage account for deposited funds."], - type: "publicKey", - }, - { - name: "conditionalOnFinalizeTokenMint", - type: "publicKey", - }, - { - name: "conditionalOnRevertTokenMint", - type: "publicKey", - }, - { - name: "pdaBump", - type: "u8", - }, - { - name: "decimals", - type: "u8", - }, - ], - }, - }, { name: "question", docs: [ @@ -1448,38 +740,6 @@ export const IDL: ConditionalVault = { }, ], types: [ - { - name: "AddMetadataToConditionalTokensArgs", - type: { - kind: "struct", - fields: [ - { - name: "proposalNumber", - type: "u64", - }, - { - name: "onFinalizeUri", - type: "string", - }, - { - name: "onRevertUri", - type: "string", - }, - ], - }, - }, - { - name: "InitializeConditionalVaultArgs", - type: { - kind: "struct", - fields: [ - { - name: "settlementAuthority", - type: "publicKey", - }, - ], - }, - }, { name: "InitializeQuestionArgs", type: { diff --git a/sdk/src/utils/pda.ts b/sdk/src/utils/pda.ts index 9f2d4bbb..c7dc1b89 100644 --- a/sdk/src/utils/pda.ts +++ b/sdk/src/utils/pda.ts @@ -34,13 +34,13 @@ export const getQuestionAddr = ( export const getVaultAddr = ( programId: PublicKey, - settlementAuthority: PublicKey, + question: PublicKey, underlyingTokenMint: PublicKey ) => { return PublicKey.findProgramAddressSync( [ utils.bytes.utf8.encode("conditional_vault"), - settlementAuthority.toBuffer(), + question.toBuffer(), underlyingTokenMint.toBuffer(), ], programId diff --git a/tests/amm/amm.ts b/tests/amm/amm.ts index 221b068a..db149295 100644 --- a/tests/amm/amm.ts +++ b/tests/amm/amm.ts @@ -110,11 +110,7 @@ describe("amm", async function () { let expectedMaxObservationChangePerUpdate = new BN(10_000_000_000); let bump: number; - [amm, bump] = getAmmAddr( - ammClient.program.programId, - META, - USDC, - ); + [amm, bump] = getAmmAddr(ammClient.program.programId, META, USDC); const ammAcc = await ammClient.getAmm(amm); @@ -159,7 +155,7 @@ describe("amm", async function () { META, META, twapFirstObservationScaled, - twapMaxObservationChangePerUpdateScaled, + twapMaxObservationChangePerUpdateScaled ) .rpc() .then(callbacks[0], callbacks[1]); diff --git a/tests/amm/integration/ammLifecycle.test.ts b/tests/amm/integration/ammLifecycle.test.ts index 40e1f6f6..57e5ac7f 100644 --- a/tests/amm/integration/ammLifecycle.test.ts +++ b/tests/amm/integration/ammLifecycle.test.ts @@ -1,68 +1,106 @@ -import { AmmClient, getAmmAddr, getAmmLpMintAddr } from "@metadaoproject/futarchy"; +import { + AmmClient, + getAmmAddr, + getAmmLpMintAddr, +} from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { createMint, createAssociatedTokenAccount, mintTo, getAccount, getMint } from "spl-token-bankrun"; +import { + createMint, + createAssociatedTokenAccount, + mintTo, + getAccount, + getMint, +} from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; import { expectError } from "../../utils"; -export default async function() { - let ammClient: AmmClient; - let META: PublicKey; - let USDC: PublicKey; - let amm: PublicKey; - - ammClient = this.ammClient; - META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); - USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); - - await this.createTokenAccount(META, this.payer.publicKey); - await this.createTokenAccount(USDC, this.payer.publicKey); - - await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); - await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); - - let proposal = Keypair.generate().publicKey; - amm = await ammClient.createAmm(proposal, META, USDC, 500); - - // 1. Initialize AMM - const initialAmm = await ammClient.getAmm(amm); - assert.isTrue(initialAmm.baseAmount.eqn(0)); - assert.isTrue(initialAmm.quoteAmount.eqn(0)); - - - - - // 2. Add initial liquidity - await ammClient.addLiquidity(amm, 1000, 2); - const ammAfterInitialLiquidity = await ammClient.getAmm(amm); - assert.isTrue(ammAfterInitialLiquidity.baseAmount.gt(new anchor.BN(0))); - assert.isTrue(ammAfterInitialLiquidity.quoteAmount.gt(new anchor.BN(0))); - - // 3. Perform swaps - await ammClient.swap(amm, { buy: {} }, 100, 0.1); - await ammClient.swap(amm, { sell: {} }, 0.1, 50); - - // 4. Add more liquidity - await ammClient.addLiquidity(amm, 500, 1); - - // 5. Remove some liquidity - let userLpAccount = token.getAssociatedTokenAddressSync( - getAmmLpMintAddr(ammClient.program.programId, amm)[0], - this.payer.publicKey - ); - let userLpBalance = (await getAccount(this.banksClient, userLpAccount)).amount; - await ammClient.removeLiquidityIx(amm, META, USDC, new anchor.BN(Number(userLpBalance) / 2), new anchor.BN(0), new anchor.BN(0)).rpc(); - - // 6. Perform more swaps - await ammClient.swap(amm, { buy: {} }, 200, 0.2); - await ammClient.swap(amm, { sell: {} }, 0.2, 100); - - // 7. Remove all remaining liquidity - userLpBalance = (await getAccount(this.banksClient, userLpAccount)).amount; - await ammClient.removeLiquidityIx(amm, META, USDC, new anchor.BN(userLpBalance), new anchor.BN(0), new anchor.BN(0)).rpc(); - - const finalAmm = await ammClient.getAmm(amm); - assert.isTrue(finalAmm.baseAmount.eqn(0)); - assert.isTrue(finalAmm.quoteAmount.eqn(0)); -} \ No newline at end of file +export default async function () { + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; + let amm: PublicKey; + + ammClient = this.ammClient; + META = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 9 + ); + USDC = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 6 + ); + + await this.createTokenAccount(META, this.payer.publicKey); + await this.createTokenAccount(USDC, this.payer.publicKey); + + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); + + let proposal = Keypair.generate().publicKey; + amm = await ammClient.createAmm(proposal, META, USDC, 500); + + // 1. Initialize AMM + const initialAmm = await ammClient.getAmm(amm); + assert.isTrue(initialAmm.baseAmount.eqn(0)); + assert.isTrue(initialAmm.quoteAmount.eqn(0)); + + // 2. Add initial liquidity + await ammClient.addLiquidity(amm, 1000, 2); + const ammAfterInitialLiquidity = await ammClient.getAmm(amm); + assert.isTrue(ammAfterInitialLiquidity.baseAmount.gt(new anchor.BN(0))); + assert.isTrue(ammAfterInitialLiquidity.quoteAmount.gt(new anchor.BN(0))); + + // 3. Perform swaps + await ammClient.swap(amm, { buy: {} }, 100, 0.1); + await ammClient.swap(amm, { sell: {} }, 0.1, 50); + + // 4. Add more liquidity + await ammClient.addLiquidity(amm, 500, 1); + + // 5. Remove some liquidity + let userLpAccount = token.getAssociatedTokenAddressSync( + getAmmLpMintAddr(ammClient.program.programId, amm)[0], + this.payer.publicKey + ); + let userLpBalance = (await getAccount(this.banksClient, userLpAccount)) + .amount; + await ammClient + .removeLiquidityIx( + amm, + META, + USDC, + new anchor.BN(Number(userLpBalance) / 2), + new anchor.BN(0), + new anchor.BN(0) + ) + .rpc(); + + // 6. Perform more swaps + await ammClient.swap(amm, { buy: {} }, 200, 0.2); + await ammClient.swap(amm, { sell: {} }, 0.2, 100); + + // 7. Remove all remaining liquidity + userLpBalance = (await getAccount(this.banksClient, userLpAccount)).amount; + await ammClient + .removeLiquidityIx( + amm, + META, + USDC, + new anchor.BN(userLpBalance), + new anchor.BN(0), + new anchor.BN(0) + ) + .rpc(); + + const finalAmm = await ammClient.getAmm(amm); + assert.isTrue(finalAmm.baseAmount.eqn(0)); + assert.isTrue(finalAmm.quoteAmount.eqn(0)); +} diff --git a/tests/amm/main.test.ts b/tests/amm/main.test.ts index c77a0c04..dc87a7fd 100644 --- a/tests/amm/main.test.ts +++ b/tests/amm/main.test.ts @@ -5,9 +5,9 @@ import removeLiquidity from "./unit/removeLiquidity.test"; import ammLifecycle from "./integration/ammLifecycle.test"; export default function suite() { - describe("#initialize_amm", initializeAmm); - describe("#add_liquidity", addLiquidity); - describe("#swap", swap); - describe("#remove_liquidity", removeLiquidity); - it("AMM lifecycle", ammLifecycle); -} \ No newline at end of file + describe("#initialize_amm", initializeAmm); + describe("#add_liquidity", addLiquidity); + describe("#swap", swap); + describe("#remove_liquidity", removeLiquidity); + it("AMM lifecycle", ammLifecycle); +} diff --git a/tests/amm/unit/addLiquidity.test.ts b/tests/amm/unit/addLiquidity.test.ts index a071fd3d..4abc370c 100644 --- a/tests/amm/unit/addLiquidity.test.ts +++ b/tests/amm/unit/addLiquidity.test.ts @@ -1,79 +1,103 @@ -import { AmmClient, getAmmAddr, getAmmLpMintAddr } from "@metadaoproject/futarchy"; +import { + AmmClient, + getAmmAddr, + getAmmLpMintAddr, +} from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { createMint, createAssociatedTokenAccount, mintTo, getAccount } from "spl-token-bankrun"; +import { + createMint, + createAssociatedTokenAccount, + mintTo, + getAccount, +} from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import { expectError } from "../../utils"; import * as token from "@solana/spl-token"; export default function suite() { - let ammClient: AmmClient; - let META: PublicKey; - let USDC: PublicKey; - let amm: PublicKey; + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; + let amm: PublicKey; - beforeEach(async function() { - ammClient = this.ammClient; - META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); - USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); - - let proposal = Keypair.generate().publicKey; - amm = await ammClient.createAmm(proposal, META, USDC, 500); + beforeEach(async function () { + ammClient = this.ammClient; + META = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 9 + ); + USDC = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 6 + ); - await this.createTokenAccount(META, this.payer.publicKey); - await this.createTokenAccount(USDC, this.payer.publicKey); + let proposal = Keypair.generate().publicKey; + amm = await ammClient.createAmm(proposal, META, USDC, 500); - await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); - await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); - }); + await this.createTokenAccount(META, this.payer.publicKey); + await this.createTokenAccount(USDC, this.payer.publicKey); - it("adds initial liquidity to an amm", async function() { - await ammClient - .addLiquidityIx( - amm, - META, - USDC, - new anchor.BN(5000 * 10 ** 6), - new anchor.BN(6 * 10 ** 9), - new anchor.BN(0) - ) - .rpc(); + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); + }); - const storedAmm = await ammClient.getAmm(amm); + it("adds initial liquidity to an amm", async function () { + await ammClient + .addLiquidityIx( + amm, + META, + USDC, + new anchor.BN(5000 * 10 ** 6), + new anchor.BN(6 * 10 ** 9), + new anchor.BN(0) + ) + .rpc(); - assert.isTrue(storedAmm.baseAmount.eq(new anchor.BN(6 * 10 ** 9))); - assert.isTrue(storedAmm.quoteAmount.eq(new anchor.BN(5000 * 10 ** 6))); + const storedAmm = await ammClient.getAmm(amm); - const lpMint = await getAccount( - this.banksClient, - token.getAssociatedTokenAddressSync(getAmmLpMintAddr(ammClient.program.programId, amm)[0], this.payer.publicKey), - ); + assert.isTrue(storedAmm.baseAmount.eq(new anchor.BN(6 * 10 ** 9))); + assert.isTrue(storedAmm.quoteAmount.eq(new anchor.BN(5000 * 10 ** 6))); - assert.equal(lpMint.amount.toString(), (5000 * 10 ** 6).toString()); - }); + const lpMint = await getAccount( + this.banksClient, + token.getAssociatedTokenAddressSync( + getAmmLpMintAddr(ammClient.program.programId, amm)[0], + this.payer.publicKey + ) + ); - it("adds liquidity after it's already been added", async function() { - await ammClient - .addLiquidityIx( - amm, - META, - USDC, - new anchor.BN(5000 * 10 ** 6), - new anchor.BN(6 * 10 ** 9), - new anchor.BN(0) - ) - .rpc(); + assert.equal(lpMint.amount.toString(), (5000 * 10 ** 6).toString()); + }); - const storedAmm = await ammClient.getAmm(amm); + it("adds liquidity after it's already been added", async function () { + await ammClient + .addLiquidityIx( + amm, + META, + USDC, + new anchor.BN(5000 * 10 ** 6), + new anchor.BN(6 * 10 ** 9), + new anchor.BN(0) + ) + .rpc(); - assert.isTrue(storedAmm.baseAmount.eq(new anchor.BN(6 * 10 ** 9))); - assert.isTrue(storedAmm.quoteAmount.eq(new anchor.BN(5000 * 10 ** 6))); + const storedAmm = await ammClient.getAmm(amm); - // const lpMint = await getAccount( - // this.banksClient, - // getAmmLpMintAddr(ammClient.program.programId, amm)[0] - // ); + assert.isTrue(storedAmm.baseAmount.eq(new anchor.BN(6 * 10 ** 9))); + assert.isTrue(storedAmm.quoteAmount.eq(new anchor.BN(5000 * 10 ** 6))); - // assert.equal(lpMint.amount.toString(), (7500 * 10 ** 6).toString()); - }); -} \ No newline at end of file + // const lpMint = await getAccount( + // this.banksClient, + // getAmmLpMintAddr(ammClient.program.programId, amm)[0] + // ); + + // assert.equal(lpMint.amount.toString(), (7500 * 10 ** 6).toString()); + }); +} diff --git a/tests/amm/unit/initializeAmm.test.ts b/tests/amm/unit/initializeAmm.test.ts index 6581f9ac..13fce2cd 100644 --- a/tests/amm/unit/initializeAmm.test.ts +++ b/tests/amm/unit/initializeAmm.test.ts @@ -1,4 +1,9 @@ -import { AmmClient, getAmmAddr, getAmmLpMintAddr, PriceMath } from "@metadaoproject/futarchy"; +import { + AmmClient, + getAmmAddr, + getAmmLpMintAddr, + PriceMath, +} from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { createMint } from "spl-token-bankrun"; @@ -6,76 +11,79 @@ import * as anchor from "@coral-xyz/anchor"; import { expectError } from "../../utils"; export default function suite() { - let ammClient: AmmClient; - let META: PublicKey; - let USDC: PublicKey; + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; - before(async function() { - ammClient = this.ammClient; - META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); - USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); - }); + before(async function () { + ammClient = this.ammClient; + META = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 9 + ); + USDC = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 6 + ); + }); - it("creates an amm", async function() { - let expectedInitialObservation = new anchor.BN(500_000_000_000); - let expectedMaxObservationChangePerUpdate = new anchor.BN(10_000_000_000); + it("creates an amm", async function () { + let expectedInitialObservation = new anchor.BN(500_000_000_000); + let expectedMaxObservationChangePerUpdate = new anchor.BN(10_000_000_000); - let bump: number; - let amm: PublicKey; - [amm, bump] = getAmmAddr( - ammClient.program.programId, - META, - USDC, - ); + let bump: number; + let amm: PublicKey; + [amm, bump] = getAmmAddr(ammClient.program.programId, META, USDC); - await ammClient.createAmm(Keypair.generate().publicKey, META, USDC, 500); + await ammClient.createAmm(Keypair.generate().publicKey, META, USDC, 500); - const ammAcc = await ammClient.getAmm(amm); + const ammAcc = await ammClient.getAmm(amm); - assert.equal(ammAcc.bump, bump); - assert.isTrue(ammAcc.createdAtSlot.eq(ammAcc.oracle.lastUpdatedSlot)); - assert.equal(ammAcc.baseMint.toBase58(), META.toBase58()); - assert.equal(ammAcc.quoteMint.toBase58(), USDC.toBase58()); - assert.equal(ammAcc.baseMintDecimals, 9); - assert.equal(ammAcc.quoteMintDecimals, 6); - assert.isTrue(ammAcc.baseAmount.eqn(0)); - assert.isTrue(ammAcc.quoteAmount.eqn(0)); - assert.isTrue( - ammAcc.oracle.lastObservation.eq(expectedInitialObservation) - ); - assert.isTrue(ammAcc.oracle.aggregator.eqn(0)); - assert.isTrue( - ammAcc.oracle.maxObservationChangePerUpdate.eq( - expectedMaxObservationChangePerUpdate - ) - ); - assert.isTrue( - ammAcc.oracle.initialObservation.eq(expectedInitialObservation) - ); - }); + assert.equal(ammAcc.bump, bump); + assert.isTrue(ammAcc.createdAtSlot.eq(ammAcc.oracle.lastUpdatedSlot)); + assert.equal(ammAcc.baseMint.toBase58(), META.toBase58()); + assert.equal(ammAcc.quoteMint.toBase58(), USDC.toBase58()); + assert.equal(ammAcc.baseMintDecimals, 9); + assert.equal(ammAcc.quoteMintDecimals, 6); + assert.isTrue(ammAcc.baseAmount.eqn(0)); + assert.isTrue(ammAcc.quoteAmount.eqn(0)); + assert.isTrue(ammAcc.oracle.lastObservation.eq(expectedInitialObservation)); + assert.isTrue(ammAcc.oracle.aggregator.eqn(0)); + assert.isTrue( + ammAcc.oracle.maxObservationChangePerUpdate.eq( + expectedMaxObservationChangePerUpdate + ) + ); + assert.isTrue( + ammAcc.oracle.initialObservation.eq(expectedInitialObservation) + ); + }); - it("fails to create an amm with two identical mints", async function() { - let [ - twapFirstObservationScaled, - twapMaxObservationChangePerUpdateScaled, - ] = PriceMath.getAmmPrices(9, 9, 100, 1); + it("fails to create an amm with two identical mints", async function () { + let [twapFirstObservationScaled, twapMaxObservationChangePerUpdateScaled] = + PriceMath.getAmmPrices(9, 9, 100, 1); - const callbacks = expectError( - "SameTokenMints", - "create AMM succeeded despite same token mints" - ); + const callbacks = expectError( + "SameTokenMints", + "create AMM succeeded despite same token mints" + ); - let proposal = Keypair.generate().publicKey; + let proposal = Keypair.generate().publicKey; - await ammClient - .createAmmIx( - META, - META, - twapFirstObservationScaled, - twapMaxObservationChangePerUpdateScaled, - ) - .rpc() - .then(callbacks[0], callbacks[1]); - }); + await ammClient + .createAmmIx( + META, + META, + twapFirstObservationScaled, + twapMaxObservationChangePerUpdateScaled + ) + .rpc() + .then(callbacks[0], callbacks[1]); + }); } - diff --git a/tests/amm/unit/removeLiquidity.test.ts b/tests/amm/unit/removeLiquidity.test.ts index 0ed1c747..48a1d05b 100644 --- a/tests/amm/unit/removeLiquidity.test.ts +++ b/tests/amm/unit/removeLiquidity.test.ts @@ -1,130 +1,165 @@ -import { AmmClient, getAmmAddr, getAmmLpMintAddr } from "@metadaoproject/futarchy"; +import { + AmmClient, + getAmmAddr, + getAmmLpMintAddr, +} from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { createMint, createAssociatedTokenAccount, mintTo, getAccount, getMint } from "spl-token-bankrun"; +import { + createMint, + createAssociatedTokenAccount, + mintTo, + getAccount, + getMint, +} from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; import { expectError } from "../../utils"; import { getAssociatedTokenAddressSync } from "@solana/spl-token"; export default function suite() { - let ammClient: AmmClient; - let META: PublicKey; - let USDC: PublicKey; - let amm: PublicKey; - - beforeEach(async function() { - ammClient = this.ammClient; - META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); - USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); - - await this.createTokenAccount(META, this.payer.publicKey); - await this.createTokenAccount(USDC, this.payer.publicKey); - - await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); - await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); - - let proposal = Keypair.generate().publicKey; - amm = await ammClient.createAmm(proposal, META, USDC, 500); - - await ammClient.addLiquidity(amm, 1000, 2); - }); - - it("can't remove 0 liquidity", async function() { - const callbacks = expectError( - "ZeroLiquidityRemove", - "was able to remove 0 liquidity" - ); - - await ammClient - .removeLiquidityIx(amm, META, USDC, new anchor.BN(0), new anchor.BN(0), new anchor.BN(0)) - .rpc() - .then(callbacks[0], callbacks[1]); - }); - - it("remove some liquidity from an amm position", async function() { - const ammStart = await ammClient.getAmm(amm); - - let userLpAccount = getAssociatedTokenAddressSync( - ammStart.lpMint, - this.payer.publicKey - ); - - const userLpAccountStart = await getAccount(this.banksClient, userLpAccount); - const lpMintStart = await getMint(this.banksClient, ammStart.lpMint); - - await ammClient - .removeLiquidityIx( - amm, - META, - USDC, - new anchor.BN(userLpAccountStart.amount.toString()).divn(2), - new anchor.BN(0), - new anchor.BN(0) - ) - .rpc(); - - const userLpAccountEnd = await getAccount(this.banksClient, userLpAccount); - const lpMintEnd = await getMint(this.banksClient, ammStart.lpMint); - - const ammEnd = await ammClient.getAmm(amm); - - assert.isBelow(Number(lpMintEnd.supply), Number(lpMintStart.supply)); - assert.isBelow( - Number(userLpAccountEnd.amount), - Number(userLpAccountStart.amount) - ); - - assert.isBelow( - ammEnd.baseAmount.toNumber(), - ammStart.baseAmount.toNumber() - ); - assert.isBelow( - ammEnd.quoteAmount.toNumber(), - ammStart.quoteAmount.toNumber() - ); - }); - - it("remove all liquidity from an amm position", async function() { - const ammStart = await ammClient.getAmm(amm); - - let userLpAccount = getAssociatedTokenAddressSync( - ammStart.lpMint, - this.payer.publicKey - ); - - const userLpAccountStart = await getAccount(this.banksClient, userLpAccount); - const lpMintStart = await getMint(this.banksClient, ammStart.lpMint); - - await ammClient - .removeLiquidityIx( - amm, - META, - USDC, - new anchor.BN(userLpAccountStart.amount.toString()), - new anchor.BN(1 * 10 ** 9), - new anchor.BN(10 * 10 ** 6) - ) - .rpc(); - - const userLpAccountEnd = await getAccount(this.banksClient, userLpAccount); - const lpMintEnd = await getMint(this.banksClient, ammStart.lpMint); - - assert.isBelow(Number(lpMintEnd.supply), Number(lpMintStart.supply)); - assert.isBelow( - Number(userLpAccountEnd.amount), - Number(userLpAccountStart.amount) - ); - - const ammEnd = await ammClient.getAmm(amm); - - assert.isBelow( - ammEnd.baseAmount.toNumber(), - ammStart.baseAmount.toNumber() - ); - assert.isBelow( - ammEnd.quoteAmount.toNumber(), - ammStart.quoteAmount.toNumber() - ); - }); -} \ No newline at end of file + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; + let amm: PublicKey; + + beforeEach(async function () { + ammClient = this.ammClient; + META = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 9 + ); + USDC = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 6 + ); + + await this.createTokenAccount(META, this.payer.publicKey); + await this.createTokenAccount(USDC, this.payer.publicKey); + + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); + + let proposal = Keypair.generate().publicKey; + amm = await ammClient.createAmm(proposal, META, USDC, 500); + + await ammClient.addLiquidity(amm, 1000, 2); + }); + + it("can't remove 0 liquidity", async function () { + const callbacks = expectError( + "ZeroLiquidityRemove", + "was able to remove 0 liquidity" + ); + + await ammClient + .removeLiquidityIx( + amm, + META, + USDC, + new anchor.BN(0), + new anchor.BN(0), + new anchor.BN(0) + ) + .rpc() + .then(callbacks[0], callbacks[1]); + }); + + it("remove some liquidity from an amm position", async function () { + const ammStart = await ammClient.getAmm(amm); + + let userLpAccount = getAssociatedTokenAddressSync( + ammStart.lpMint, + this.payer.publicKey + ); + + const userLpAccountStart = await getAccount( + this.banksClient, + userLpAccount + ); + const lpMintStart = await getMint(this.banksClient, ammStart.lpMint); + + await ammClient + .removeLiquidityIx( + amm, + META, + USDC, + new anchor.BN(userLpAccountStart.amount.toString()).divn(2), + new anchor.BN(0), + new anchor.BN(0) + ) + .rpc(); + + const userLpAccountEnd = await getAccount(this.banksClient, userLpAccount); + const lpMintEnd = await getMint(this.banksClient, ammStart.lpMint); + + const ammEnd = await ammClient.getAmm(amm); + + assert.isBelow(Number(lpMintEnd.supply), Number(lpMintStart.supply)); + assert.isBelow( + Number(userLpAccountEnd.amount), + Number(userLpAccountStart.amount) + ); + + assert.isBelow( + ammEnd.baseAmount.toNumber(), + ammStart.baseAmount.toNumber() + ); + assert.isBelow( + ammEnd.quoteAmount.toNumber(), + ammStart.quoteAmount.toNumber() + ); + }); + + it("remove all liquidity from an amm position", async function () { + const ammStart = await ammClient.getAmm(amm); + + let userLpAccount = getAssociatedTokenAddressSync( + ammStart.lpMint, + this.payer.publicKey + ); + + const userLpAccountStart = await getAccount( + this.banksClient, + userLpAccount + ); + const lpMintStart = await getMint(this.banksClient, ammStart.lpMint); + + await ammClient + .removeLiquidityIx( + amm, + META, + USDC, + new anchor.BN(userLpAccountStart.amount.toString()), + new anchor.BN(1 * 10 ** 9), + new anchor.BN(10 * 10 ** 6) + ) + .rpc(); + + const userLpAccountEnd = await getAccount(this.banksClient, userLpAccount); + const lpMintEnd = await getMint(this.banksClient, ammStart.lpMint); + + assert.isBelow(Number(lpMintEnd.supply), Number(lpMintStart.supply)); + assert.isBelow( + Number(userLpAccountEnd.amount), + Number(userLpAccountStart.amount) + ); + + const ammEnd = await ammClient.getAmm(amm); + + assert.isBelow( + ammEnd.baseAmount.toNumber(), + ammStart.baseAmount.toNumber() + ); + assert.isBelow( + ammEnd.quoteAmount.toNumber(), + ammStart.quoteAmount.toNumber() + ); + }); +} diff --git a/tests/amm/unit/swap.test.ts b/tests/amm/unit/swap.test.ts index 988bc4dc..0263a38c 100644 --- a/tests/amm/unit/swap.test.ts +++ b/tests/amm/unit/swap.test.ts @@ -1,248 +1,286 @@ -import { AmmClient, getAmmAddr, getAmmLpMintAddr } from "@metadaoproject/futarchy"; +import { + AmmClient, + getAmmAddr, + getAmmLpMintAddr, +} from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { createMint, createAssociatedTokenAccount, mintTo, getAccount, getMint } from "spl-token-bankrun"; +import { + createMint, + createAssociatedTokenAccount, + mintTo, + getAccount, + getMint, +} from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import { expectError } from "../../utils"; import { advanceBySlots } from "../../utils"; import { getAssociatedTokenAddressSync } from "@solana/spl-token"; export default function suite() { - let ammClient: AmmClient; - let META: PublicKey; - let USDC: PublicKey; - let amm: PublicKey; - - beforeEach(async function() { - ammClient = this.ammClient; - META = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 9); - USDC = await createMint(this.banksClient, this.payer, this.payer.publicKey, this.payer.publicKey, 6); - - await this.createTokenAccount(META, this.payer.publicKey); - await this.createTokenAccount(USDC, this.payer.publicKey); - - await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); - await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); - - let proposal = Keypair.generate().publicKey; - amm = await ammClient.createAmm(proposal, META, USDC, 500); - - await ammClient - .addLiquidityIx( - amm, - META, - USDC, - new anchor.BN(10_000 * 10 ** 6), - new anchor.BN(10 * 10 ** 9), - new anchor.BN(0) - ) - .rpc(); - }); - - it("fails when you have insufficient balance", async () => { - let callbacks = expectError( - "InsufficientBalance", - "we should have caught a user not having enough balance" - ); - - await ammClient - .swap(amm, { buy: {} }, 10_000_000, 1) - .then(callbacks[0], callbacks[1]); - - await ammClient - .swap(amm, { sell: {} }, 100_000, 1) - .then(callbacks[0], callbacks[1]); - }); - - it("buys", async function() { - const expectedOut = 0.098029507; - - // Ensure user has enough USDC - await this.mintTo(USDC, this.payer.publicKey, this.payer, 200 * 10 ** 6); - - const storedAmm = await ammClient.getAmm(amm); - let sim = ammClient.simulateSwap( - new anchor.BN(100 * 10 ** 6), - { buy: {} }, - storedAmm.baseAmount, - storedAmm.quoteAmount - ); - assert.equal( - sim.expectedOut.toString(), - new anchor.BN(expectedOut * 10 ** 9).toString() - ); - - let callbacks = expectError( - "SwapSlippageExceeded", - "we got back too many tokens from the AMM" - ); - - await ammClient - .swap(amm, { buy: {} }, 100, expectedOut + 0.000000001) - .then(callbacks[0], callbacks[1]); - - await ammClient.swap(amm, { buy: {} }, 100, expectedOut); - - await validateAmmState({ - banksClient: this.banksClient, - ammClient, - amm, - base: META, - quote: USDC, - expectedBaseAmount: (10 - expectedOut) * 10 ** 9, - expectedQuoteAmount: 10_100 * 10 ** 6, - expectedLpSupply: 10_000 * 10 ** 6, - }); - }); + let ammClient: AmmClient; + let META: PublicKey; + let USDC: PublicKey; + let amm: PublicKey; + + beforeEach(async function () { + ammClient = this.ammClient; + META = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 9 + ); + USDC = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + this.payer.publicKey, + 6 + ); - it("sells", async function() { - const expectedOut = 900.818926; - - let callbacks = expectError( - "SwapSlippageExceeded", - "we got back too many tokens from the AMM" - ); - - await ammClient - .swap(amm, { sell: {} }, 1, expectedOut + 0.000001) - .then(callbacks[0], callbacks[1]); - - await ammClient.swap(amm, { sell: {} }, 1, expectedOut); - - await validateAmmState({ - banksClient: this.banksClient, - ammClient, - amm, - base: META, - quote: USDC, - expectedBaseAmount: 11 * 10 ** 9, - expectedQuoteAmount: (10_000 - expectedOut) * 10 ** 6, - expectedLpSupply: 10_000 * 10 ** 6, - }); - }); + await this.createTokenAccount(META, this.payer.publicKey); + await this.createTokenAccount(USDC, this.payer.publicKey); + + await this.mintTo(META, this.payer.publicKey, this.payer, 100 * 10 ** 9); + await this.mintTo(USDC, this.payer.publicKey, this.payer, 10_000 * 10 ** 6); + + let proposal = Keypair.generate().publicKey; + amm = await ammClient.createAmm(proposal, META, USDC, 500); + + await ammClient + .addLiquidityIx( + amm, + META, + USDC, + new anchor.BN(10_000 * 10 ** 6), + new anchor.BN(10 * 10 ** 9), + new anchor.BN(0) + ) + .rpc(); + }); + + it("fails when you have insufficient balance", async () => { + let callbacks = expectError( + "InsufficientBalance", + "we should have caught a user not having enough balance" + ); - it("swap base to quote and back, should not be profitable", async function() { - const permissionlessAmmStart = await ammClient.program.account.amm.fetch(amm); - const ammEnd = await ammClient.getAmm(amm); - - let startingBaseSwapAmount = 1 * 10 ** 9; - - await ammClient - .swapIx( - amm, - META, - USDC, - { sell: {} }, - new anchor.BN(startingBaseSwapAmount), - new anchor.BN(1) - ) - .rpc(); - - await advanceBySlots(this.context, 1n); - - const ammMiddle = await ammClient.getAmm(amm); - let quoteReceived = - permissionlessAmmStart.quoteAmount.toNumber() - - ammMiddle.quoteAmount.toNumber(); - - await ammClient - .swapIx(amm, META, USDC, { buy: {} }, new anchor.BN(quoteReceived), new anchor.BN(1)) - .rpc(); - - const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(amm); - let baseReceived = - ammMiddle.baseAmount.toNumber() - - permissionlessAmmEnd.baseAmount.toNumber(); - - assert.isBelow(baseReceived, startingBaseSwapAmount); - assert.isAbove(baseReceived, startingBaseSwapAmount * 0.98); - }); + await ammClient + .swap(amm, { buy: {} }, 10_000_000, 1) + .then(callbacks[0], callbacks[1]); - it("swap quote to base and back, should not be profitable", async function() { - const ammStart = await ammClient.getAmm(amm); + await ammClient + .swap(amm, { sell: {} }, 100_000, 1) + .then(callbacks[0], callbacks[1]); + }); - let startingQuoteSwapAmount = 1 * 10 ** 6; + it("buys", async function () { + const expectedOut = 0.098029507; - // Ensure user has enough USDC - await this.mintTo(USDC, this.payer.publicKey, this.payer, 2 * 10 ** 6); + // Ensure user has enough USDC + await this.mintTo(USDC, this.payer.publicKey, this.payer, 200 * 10 ** 6); - await ammClient - .swapIx( - amm, - META, - USDC, - { buy: {} }, - new anchor.BN(startingQuoteSwapAmount), - new anchor.BN(1) - ) - .rpc(); + const storedAmm = await ammClient.getAmm(amm); + let sim = ammClient.simulateSwap( + new anchor.BN(100 * 10 ** 6), + { buy: {} }, + storedAmm.baseAmount, + storedAmm.quoteAmount + ); + assert.equal( + sim.expectedOut.toString(), + new anchor.BN(expectedOut * 10 ** 9).toString() + ); - await advanceBySlots(this.context, 1n); + let callbacks = expectError( + "SwapSlippageExceeded", + "we got back too many tokens from the AMM" + ); - const ammMiddle = await ammClient.getAmm(amm); - let baseReceived = - ammStart.baseAmount.toNumber() - ammMiddle.baseAmount.toNumber(); + await ammClient + .swap(amm, { buy: {} }, 100, expectedOut + 0.000000001) + .then(callbacks[0], callbacks[1]); + + await ammClient.swap(amm, { buy: {} }, 100, expectedOut); + + await validateAmmState({ + banksClient: this.banksClient, + ammClient, + amm, + base: META, + quote: USDC, + expectedBaseAmount: (10 - expectedOut) * 10 ** 9, + expectedQuoteAmount: 10_100 * 10 ** 6, + expectedLpSupply: 10_000 * 10 ** 6, + }); + }); - await ammClient - .swapIx(amm, META, USDC, { sell: {} }, new anchor.BN(baseReceived), new anchor.BN(1)) - .rpc(); + it("sells", async function () { + const expectedOut = 900.818926; - const ammEnd = await ammClient.getAmm(amm); - let quoteReceived = - ammMiddle.quoteAmount.toNumber() - ammEnd.quoteAmount.toNumber(); + let callbacks = expectError( + "SwapSlippageExceeded", + "we got back too many tokens from the AMM" + ); - assert.isBelow(quoteReceived, startingQuoteSwapAmount); - assert.isAbove(quoteReceived, startingQuoteSwapAmount * 0.98); + await ammClient + .swap(amm, { sell: {} }, 1, expectedOut + 0.000001) + .then(callbacks[0], callbacks[1]); + + await ammClient.swap(amm, { sell: {} }, 1, expectedOut); + + await validateAmmState({ + banksClient: this.banksClient, + ammClient, + amm, + base: META, + quote: USDC, + expectedBaseAmount: 11 * 10 ** 9, + expectedQuoteAmount: (10_000 - expectedOut) * 10 ** 6, + expectedLpSupply: 10_000 * 10 ** 6, }); + }); + + it("swap base to quote and back, should not be profitable", async function () { + const permissionlessAmmStart = await ammClient.program.account.amm.fetch( + amm + ); + const ammEnd = await ammClient.getAmm(amm); + + let startingBaseSwapAmount = 1 * 10 ** 9; + + await ammClient + .swapIx( + amm, + META, + USDC, + { sell: {} }, + new anchor.BN(startingBaseSwapAmount), + new anchor.BN(1) + ) + .rpc(); + + await advanceBySlots(this.context, 1n); + + const ammMiddle = await ammClient.getAmm(amm); + let quoteReceived = + permissionlessAmmStart.quoteAmount.toNumber() - + ammMiddle.quoteAmount.toNumber(); + + await ammClient + .swapIx( + amm, + META, + USDC, + { buy: {} }, + new anchor.BN(quoteReceived), + new anchor.BN(1) + ) + .rpc(); + + const permissionlessAmmEnd = await ammClient.program.account.amm.fetch(amm); + let baseReceived = + ammMiddle.baseAmount.toNumber() - + permissionlessAmmEnd.baseAmount.toNumber(); + + assert.isBelow(baseReceived, startingBaseSwapAmount); + assert.isAbove(baseReceived, startingBaseSwapAmount * 0.98); + }); + + it("swap quote to base and back, should not be profitable", async function () { + const ammStart = await ammClient.getAmm(amm); + + let startingQuoteSwapAmount = 1 * 10 ** 6; + + // Ensure user has enough USDC + await this.mintTo(USDC, this.payer.publicKey, this.payer, 2 * 10 ** 6); + + await ammClient + .swapIx( + amm, + META, + USDC, + { buy: {} }, + new anchor.BN(startingQuoteSwapAmount), + new anchor.BN(1) + ) + .rpc(); + + await advanceBySlots(this.context, 1n); + + const ammMiddle = await ammClient.getAmm(amm); + let baseReceived = + ammStart.baseAmount.toNumber() - ammMiddle.baseAmount.toNumber(); + + await ammClient + .swapIx( + amm, + META, + USDC, + { sell: {} }, + new anchor.BN(baseReceived), + new anchor.BN(1) + ) + .rpc(); + + const ammEnd = await ammClient.getAmm(amm); + let quoteReceived = + ammMiddle.quoteAmount.toNumber() - ammEnd.quoteAmount.toNumber(); + + assert.isBelow(quoteReceived, startingQuoteSwapAmount); + assert.isAbove(quoteReceived, startingQuoteSwapAmount * 0.98); + }); } async function validateAmmState({ - banksClient, - ammClient, - amm, - base, - quote, - expectedBaseAmount, - expectedQuoteAmount, - expectedLpSupply, + banksClient, + ammClient, + amm, + base, + quote, + expectedBaseAmount, + expectedQuoteAmount, + expectedLpSupply, }: { - banksClient: any; - ammClient: AmmClient; - amm: PublicKey; - base: PublicKey; - quote: PublicKey; - expectedBaseAmount: number; - expectedQuoteAmount: number; - expectedLpSupply: number; + banksClient: any; + ammClient: AmmClient; + amm: PublicKey; + base: PublicKey; + quote: PublicKey; + expectedBaseAmount: number; + expectedQuoteAmount: number; + expectedLpSupply: number; }) { - const storedAmm = await ammClient.getAmm(amm); - - assert.equal(storedAmm.baseAmount.toString(), expectedBaseAmount.toString()); - assert.equal( - storedAmm.quoteAmount.toString(), - expectedQuoteAmount.toString() - ); - - assert.equal( - ( - await getAccount( - banksClient, - getAssociatedTokenAddressSync(base, amm, true) - ) - ).amount, - BigInt(expectedBaseAmount) - ); - assert.equal( - ( - await getAccount( - banksClient, - getAssociatedTokenAddressSync(quote, amm, true) - ) - ).amount, - BigInt(expectedQuoteAmount) - ); - assert.equal( - (await getMint(banksClient, storedAmm.lpMint)).supply, - BigInt(expectedLpSupply) - ); -} \ No newline at end of file + const storedAmm = await ammClient.getAmm(amm); + + assert.equal(storedAmm.baseAmount.toString(), expectedBaseAmount.toString()); + assert.equal( + storedAmm.quoteAmount.toString(), + expectedQuoteAmount.toString() + ); + + assert.equal( + ( + await getAccount( + banksClient, + getAssociatedTokenAddressSync(base, amm, true) + ) + ).amount, + BigInt(expectedBaseAmount) + ); + assert.equal( + ( + await getAccount( + banksClient, + getAssociatedTokenAddressSync(quote, amm, true) + ) + ).amount, + BigInt(expectedQuoteAmount) + ); + assert.equal( + (await getMint(banksClient, storedAmm.lpMint)).supply, + BigInt(expectedLpSupply) + ); +} diff --git a/tests/autocrat/autocrat.ts b/tests/autocrat/autocrat.ts index 5ae4920d..09bbd90b 100644 --- a/tests/autocrat/autocrat.ts +++ b/tests/autocrat/autocrat.ts @@ -22,8 +22,14 @@ import { import { advanceBySlots, expectError } from "../utils"; import { Autocrat, IDL as AutocratIDL } from "../../target/types/autocrat"; -import { ConditionalVault, IDL as ConditionalVaultIDL } from "../../target/types/conditional_vault"; -import { AutocratMigrator, IDL as AutocratMigratorIDL } from "../../target/types/autocrat_migrator"; +import { + ConditionalVault, + IDL as ConditionalVaultIDL, +} from "../../target/types/conditional_vault"; +import { + AutocratMigrator, + IDL as AutocratMigratorIDL, +} from "../../target/types/autocrat_migrator"; const { PublicKey, Keypair } = anchor.web3; @@ -345,8 +351,18 @@ export default function suite() { USDC, dao ); - await vaultClient.splitTokensIx(question, baseVault, META, new BN(10 * 10**9), 2).rpc(); - await vaultClient.splitTokensIx(question, quoteVault, USDC, new BN(10_000 * 1_000_000), 2).rpc(); + await vaultClient + .splitTokensIx(question, baseVault, META, new BN(10 * 10 ** 9), 2) + .rpc(); + await vaultClient + .splitTokensIx( + question, + quoteVault, + USDC, + new BN(10_000 * 1_000_000), + 2 + ) + .rpc(); }); it("doesn't finalize proposals that are too young", async function () { @@ -463,13 +479,8 @@ export default function suite() { }); it("rejects proposals when pass price TWAP < fail price TWAP", async function () { - let { - passAmm, - failAmm, - failBaseMint, - failQuoteMint, - question, - } = autocratClient.getProposalPdas(proposal, META, USDC, dao); + let { passAmm, failAmm, failBaseMint, failQuoteMint, question } = + autocratClient.getProposalPdas(proposal, META, USDC, dao); // swap $500 in the fail market, make it fail await ammClient @@ -521,12 +532,17 @@ export default function suite() { assert.equal(storedQuestion.payoutDenominator, 1); assert.deepEqual(storedQuestion.payoutNumerators, [1, 0]); - }); }); describe("#execute_proposal", async function () { - let proposal, passAmm, failAmm, baseVault, quoteVault, question: PublicKey,instruction; + let proposal, + passAmm, + failAmm, + baseVault, + quoteVault, + question: PublicKey, + instruction; beforeEach(async function () { await mintToOverride(context, treasuryMetaAccount, 1_000_000_000n); @@ -573,9 +589,18 @@ export default function suite() { ({ baseVault, quoteVault, passAmm, failAmm, question } = await autocrat.account.proposal.fetch(proposal)); - - await vaultClient.splitTokensIx(question, baseVault, META, new BN(10 * 10**9), 2).rpc(); - await vaultClient.splitTokensIx(question, quoteVault, USDC, new BN(10_000 * 1_000_000), 2).rpc(); + await vaultClient + .splitTokensIx(question, baseVault, META, new BN(10 * 10 ** 9), 2) + .rpc(); + await vaultClient + .splitTokensIx( + question, + quoteVault, + USDC, + new BN(10_000 * 1_000_000), + 2 + ) + .rpc(); }); it("doesn't allow pending proposals to be executed", async function () { diff --git a/tests/autocratMigrator/migrator.ts b/tests/autocratMigrator/migrator.ts index 76cb16f6..3c18ab44 100644 --- a/tests/autocratMigrator/migrator.ts +++ b/tests/autocratMigrator/migrator.ts @@ -13,7 +13,10 @@ const AUTOCRAT_MIGRATOR_PROGRAM_ID = new PublicKey( "MigRDW6uxyNMDBD8fX2njCRyJC4YZk2Rx9pDUZiAESt" ); -import { AutocratMigrator, IDL as AutocratMigratorIDL } from "../../target/types/autocrat_migrator"; +import { + AutocratMigrator, + IDL as AutocratMigratorIDL, +} from "../../target/types/autocrat_migrator"; export type PublicKey = anchor.web3.PublicKey; export type Signer = anchor.web3.Signer; diff --git a/tests/conditionalVault/integration/binaryPredictionMarket.test.ts b/tests/conditionalVault/integration/binaryPredictionMarket.test.ts index 29e46843..48be6f42 100644 --- a/tests/conditionalVault/integration/binaryPredictionMarket.test.ts +++ b/tests/conditionalVault/integration/binaryPredictionMarket.test.ts @@ -2,80 +2,112 @@ import { ConditionalVaultClient, sha256 } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import BN from "bn.js"; import { assert } from "chai"; -import { createMint, getMint, mintTo, createAssociatedTokenAccount, transfer, getAccount } from "spl-token-bankrun"; +import { + createMint, + getMint, + mintTo, + createAssociatedTokenAccount, + transfer, + getAccount, +} from "spl-token-bankrun"; import * as token from "@solana/spl-token"; export default async function test() { - // A binary prediction market test. Alice, Bob, and Charlie are betting on - // who's going to win the next election: Trump or Harris. Alice and Bob each - // split 100 USDC into TRUMP and HARRIS tokens. Alice is a trump supporter - // and buys 30 TRUMP tokens in exchange for 40 HARRIS tokens from Bob. Charlie - // buys an additional 30 HARRIS tokens from Alice. - - // Alice should have 130 TRUMP and 30 HARRIS tokens. Bob should have 70 TRUMP - // and 140 HARRIS tokens. Charlie should have 30 HARRIS tokens. - - // Alice faces a cash crunch, and merges 20 TRUMP and 20 HARRIS tokens into 20 - // USDC. She should now have 20 USDC, 110 TRUMP, and 10 HARRIS tokens. - - // Bob becomes a trump supporter, and as a display of his newfound - // allegiance, he sends all of his HARRIS tokens to the vault (to burn them). - // Bob should now have 70 TRUMP tokens. - - // The market resolves in favor of Harris. When redeeming, Alice should get 15 - // USDC, Bob should get nothing, and Charlie should get 30 USDC. - - let vaultClient: ConditionalVaultClient = this.vaultClient; - - let alice: Keypair = Keypair.generate(); - let bob: Keypair = Keypair.generate(); - let charlie: Keypair = Keypair.generate(); - let operator: Keypair = Keypair.generate(); - - let question: PublicKey = await vaultClient.initializeQuestion(sha256(new TextEncoder().encode("Who's going to win the next election?/TRUMP/HARRIS")), operator.publicKey, 2); - - let USDC: PublicKey = await this.createMint(operator.publicKey, 6); - - await this.createTokenAccount(USDC, alice.publicKey); - await this.createTokenAccount(USDC, bob.publicKey); - await this.createTokenAccount(USDC, charlie.publicKey); - - - await this.mintTo(USDC, alice.publicKey, operator, 100); - await this.mintTo(USDC, bob.publicKey, operator, 100); - - const vault = await vaultClient.initializeNewVault(question, USDC, 2); - const storedVault = await vaultClient.fetchVault(vault); - - await vaultClient.splitTokensIx(question, vault, USDC, new BN(100), 2, alice.publicKey).signers([alice]).rpc(); - - await vaultClient.splitTokensIx(question, vault, USDC, new BN(100), 2, bob.publicKey).signers([bob]).rpc(); - - const TRUMP = storedVault.conditionalTokenMints[0]; - const HARRIS = storedVault.conditionalTokenMints[1]; - - await this.createTokenAccount(HARRIS, charlie.publicKey); - - await this.transfer(HARRIS, alice, bob.publicKey, 40); - await this.transfer(TRUMP, bob, alice.publicKey, 30); - await this.transfer(HARRIS, alice, charlie.publicKey, 30); - - await vaultClient.mergeTokensIx(question, vault, USDC, new BN(20), 2, alice.publicKey).signers([alice]).rpc(); - - await this.assertBalance(USDC, alice.publicKey, 20); - await this.assertBalance(TRUMP, alice.publicKey, 110); - await this.assertBalance(HARRIS, alice.publicKey, 10); - - await this.createTokenAccount(HARRIS, vault); - await this.transfer(HARRIS, bob, vault, 140); - - await vaultClient.resolveQuestionIx(question, operator, [0, 1]).rpc(); - - await vaultClient.redeemTokensIx(question, vault, USDC, 2, alice.publicKey).signers([alice]).rpc(); - await vaultClient.redeemTokensIx(question, vault, USDC, 2, bob.publicKey).signers([bob]).rpc(); - await vaultClient.redeemTokensIx(question, vault, USDC, 2, charlie.publicKey).signers([charlie]).rpc(); - - await this.assertBalance(USDC, alice.publicKey, 30); - await this.assertBalance(USDC, bob.publicKey, 0); - await this.assertBalance(USDC, charlie.publicKey, 30); -} \ No newline at end of file + // A binary prediction market test. Alice, Bob, and Charlie are betting on + // who's going to win the next election: Trump or Harris. Alice and Bob each + // split 100 USDC into TRUMP and HARRIS tokens. Alice is a trump supporter + // and buys 30 TRUMP tokens in exchange for 40 HARRIS tokens from Bob. Charlie + // buys an additional 30 HARRIS tokens from Alice. + + // Alice should have 130 TRUMP and 30 HARRIS tokens. Bob should have 70 TRUMP + // and 140 HARRIS tokens. Charlie should have 30 HARRIS tokens. + + // Alice faces a cash crunch, and merges 20 TRUMP and 20 HARRIS tokens into 20 + // USDC. She should now have 20 USDC, 110 TRUMP, and 10 HARRIS tokens. + + // Bob becomes a trump supporter, and as a display of his newfound + // allegiance, he sends all of his HARRIS tokens to the vault (to burn them). + // Bob should now have 70 TRUMP tokens. + + // The market resolves in favor of Harris. When redeeming, Alice should get 15 + // USDC, Bob should get nothing, and Charlie should get 30 USDC. + + let vaultClient: ConditionalVaultClient = this.vaultClient; + + let alice: Keypair = Keypair.generate(); + let bob: Keypair = Keypair.generate(); + let charlie: Keypair = Keypair.generate(); + let operator: Keypair = Keypair.generate(); + + let question: PublicKey = await vaultClient.initializeQuestion( + sha256( + new TextEncoder().encode( + "Who's going to win the next election?/TRUMP/HARRIS" + ) + ), + operator.publicKey, + 2 + ); + + let USDC: PublicKey = await this.createMint(operator.publicKey, 6); + + await this.createTokenAccount(USDC, alice.publicKey); + await this.createTokenAccount(USDC, bob.publicKey); + await this.createTokenAccount(USDC, charlie.publicKey); + + await this.mintTo(USDC, alice.publicKey, operator, 100); + await this.mintTo(USDC, bob.publicKey, operator, 100); + + const vault = await vaultClient.initializeVault(question, USDC, 2); + const storedVault = await vaultClient.fetchVault(vault); + + await vaultClient + .splitTokensIx(question, vault, USDC, new BN(100), 2, alice.publicKey) + .signers([alice]) + .rpc(); + + await vaultClient + .splitTokensIx(question, vault, USDC, new BN(100), 2, bob.publicKey) + .signers([bob]) + .rpc(); + + const TRUMP = storedVault.conditionalTokenMints[0]; + const HARRIS = storedVault.conditionalTokenMints[1]; + + await this.createTokenAccount(HARRIS, charlie.publicKey); + + await this.transfer(HARRIS, alice, bob.publicKey, 40); + await this.transfer(TRUMP, bob, alice.publicKey, 30); + await this.transfer(HARRIS, alice, charlie.publicKey, 30); + + await vaultClient + .mergeTokensIx(question, vault, USDC, new BN(20), 2, alice.publicKey) + .signers([alice]) + .rpc(); + + await this.assertBalance(USDC, alice.publicKey, 20); + await this.assertBalance(TRUMP, alice.publicKey, 110); + await this.assertBalance(HARRIS, alice.publicKey, 10); + + await this.createTokenAccount(HARRIS, vault); + await this.transfer(HARRIS, bob, vault, 140); + + await vaultClient.resolveQuestionIx(question, operator, [0, 1]).rpc(); + + await vaultClient + .redeemTokensIx(question, vault, USDC, 2, alice.publicKey) + .signers([alice]) + .rpc(); + await vaultClient + .redeemTokensIx(question, vault, USDC, 2, bob.publicKey) + .signers([bob]) + .rpc(); + await vaultClient + .redeemTokensIx(question, vault, USDC, 2, charlie.publicKey) + .signers([charlie]) + .rpc(); + + await this.assertBalance(USDC, alice.publicKey, 30); + await this.assertBalance(USDC, bob.publicKey, 0); + await this.assertBalance(USDC, charlie.publicKey, 30); +} diff --git a/tests/conditionalVault/integration/scalarGrantMarket.test.ts b/tests/conditionalVault/integration/scalarGrantMarket.test.ts index 2fb48a0b..e0e996e8 100644 --- a/tests/conditionalVault/integration/scalarGrantMarket.test.ts +++ b/tests/conditionalVault/integration/scalarGrantMarket.test.ts @@ -2,51 +2,71 @@ import { ConditionalVaultClient, sha256 } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import BN from "bn.js"; import { assert } from "chai"; -import { createMint, getMint, mintTo, createAssociatedTokenAccount, transfer, getAccount } from "spl-token-bankrun"; +import { + createMint, + getMint, + mintTo, + createAssociatedTokenAccount, + transfer, + getAccount, +} from "spl-token-bankrun"; import * as token from "@solana/spl-token"; export default async function test() { - // A scalar grant market test. Alice splits 100 USDC into E-UP and E-DOWN tokens. - // She sends 30 E-UPs to Bob. The grant committee resolves the question with 60% effectiveness. - // Alice and Bob redeem their tokens. + // A scalar grant market test. Alice splits 100 USDC into E-UP and E-DOWN tokens. + // She sends 30 E-UPs to Bob. The grant committee resolves the question with 60% effectiveness. + // Alice and Bob redeem their tokens. - let vaultClient: ConditionalVaultClient = this.vaultClient; + let vaultClient: ConditionalVaultClient = this.vaultClient; - let alice: Keypair = Keypair.generate(); - let bob: Keypair = Keypair.generate(); - let grantCommittee: Keypair = Keypair.generate(); + let alice: Keypair = Keypair.generate(); + let bob: Keypair = Keypair.generate(); + let grantCommittee: Keypair = Keypair.generate(); - let question: PublicKey = await vaultClient.initializeQuestion( - sha256(new TextEncoder().encode("What is the effectiveness of the grant?/E-UP/E-DOWN")), - grantCommittee.publicKey, - 2 - ); + let question: PublicKey = await vaultClient.initializeQuestion( + sha256( + new TextEncoder().encode( + "What is the effectiveness of the grant?/E-UP/E-DOWN" + ) + ), + grantCommittee.publicKey, + 2 + ); - let USDC: PublicKey = await this.createMint(this.payer.publicKey, 6); + let USDC: PublicKey = await this.createMint(this.payer.publicKey, 6); - await this.createTokenAccount(USDC, alice.publicKey); - await this.createTokenAccount(USDC, bob.publicKey); + await this.createTokenAccount(USDC, alice.publicKey); + await this.createTokenAccount(USDC, bob.publicKey); - await this.mintTo(USDC, alice.publicKey, this.payer, 100); + await this.mintTo(USDC, alice.publicKey, this.payer, 100); - const vault = await vaultClient.initializeNewVault(question, USDC, 2); - const storedVault = await vaultClient.fetchVault(vault); + const vault = await vaultClient.initializeVault(question, USDC, 2); + const storedVault = await vaultClient.fetchVault(vault); - await vaultClient.splitTokensIx(question, vault, USDC, new BN(100), 2, alice.publicKey).signers([alice]).rpc(); + await vaultClient + .splitTokensIx(question, vault, USDC, new BN(100), 2, alice.publicKey) + .signers([alice]) + .rpc(); - const E_UP = storedVault.conditionalTokenMints[0]; - const E_DOWN = storedVault.conditionalTokenMints[1]; + const E_UP = storedVault.conditionalTokenMints[0]; + const E_DOWN = storedVault.conditionalTokenMints[1]; - await this.createTokenAccount(E_UP, bob.publicKey); + await this.createTokenAccount(E_UP, bob.publicKey); - await this.transfer(E_UP, alice, bob.publicKey, 30); + await this.transfer(E_UP, alice, bob.publicKey, 30); - // Grant committee resolves the question with 60% effectiveness - await vaultClient.resolveQuestionIx(question, grantCommittee, [6, 4]).rpc(); + // Grant committee resolves the question with 60% effectiveness + await vaultClient.resolveQuestionIx(question, grantCommittee, [6, 4]).rpc(); - await vaultClient.redeemTokensIx(question, vault, USDC, 2, alice.publicKey).signers([alice]).rpc(); - await vaultClient.redeemTokensIx(question, vault, USDC, 2, bob.publicKey).signers([bob]).rpc(); + await vaultClient + .redeemTokensIx(question, vault, USDC, 2, alice.publicKey) + .signers([alice]) + .rpc(); + await vaultClient + .redeemTokensIx(question, vault, USDC, 2, bob.publicKey) + .signers([bob]) + .rpc(); - await this.assertBalance(USDC, bob.publicKey, 18); - await this.assertBalance(USDC, alice.publicKey, 82); + await this.assertBalance(USDC, bob.publicKey, 18); + await this.assertBalance(USDC, alice.publicKey, 82); } diff --git a/tests/conditionalVault/main.test.ts b/tests/conditionalVault/main.test.ts index 487e813f..67cc857a 100644 --- a/tests/conditionalVault/main.test.ts +++ b/tests/conditionalVault/main.test.ts @@ -8,12 +8,12 @@ import binaryPredictionMarket from "./integration/binaryPredictionMarket.test"; import scalarGrantMarket from "./integration/scalarGrantMarket.test"; export default function suite() { - it("binary prediction market", binaryPredictionMarket); - it("scalar grant market", scalarGrantMarket); - describe("#initialize_question", initializeQuestion); - describe("#initialize_conditional_vault", initializeConditionalVault); - describe("#resolve_question", resolveQuestion); - describe("#split_tokens", splitTokens); - describe("#merge_tokens", mergeTokens); - describe("#redeem_tokens", redeemTokens); -} \ No newline at end of file + it("binary prediction market", binaryPredictionMarket); + it("scalar grant market", scalarGrantMarket); + describe("#initialize_question", initializeQuestion); + describe("#initialize_conditional_vault", initializeConditionalVault); + describe("#resolve_question", resolveQuestion); + describe("#split_tokens", splitTokens); + describe("#merge_tokens", mergeTokens); + describe("#redeem_tokens", redeemTokens); +} diff --git a/tests/conditionalVault/unit.ts b/tests/conditionalVault/unit.ts index 45ed0e78..9c0a9835 100644 --- a/tests/conditionalVault/unit.ts +++ b/tests/conditionalVault/unit.ts @@ -34,7 +34,10 @@ import { const { PublicKey, Keypair } = web3; -import { ConditionalVault, IDL as ConditionalVaultIDL } from "../../target/types/conditional_vault"; +import { + ConditionalVault, + IDL as ConditionalVaultIDL, +} from "../../target/types/conditional_vault"; import { expectError } from "../utils"; import { CONDITIONAL_VAULT_PROGRAM_ID, @@ -114,7 +117,9 @@ describe("conditional_vault", async function () { provider ); - vaultClient = ConditionalVaultClient.createClient({ provider: provider as any }); + vaultClient = ConditionalVaultClient.createClient({ + provider: provider as any, + }); payer = vaultProgram.provider.wallet.payer; alice = anchor.web3.Keypair.generate(); @@ -190,7 +195,7 @@ describe("conditional_vault", async function () { it("initializes vaults correctly", async function () { await vaultClient - .initializeNewVaultIx(question, underlyingTokenMint, outcomes) + .initializeVaultIx(question, underlyingTokenMint, outcomes) .rpc(); const [vault, pdaBump] = getVaultAddr( @@ -282,7 +287,7 @@ describe("conditional_vault", async function () { settlementAuthority.publicKey, 2 ); - vault = await vaultClient.initializeNewVault( + vault = await vaultClient.initializeVault( question, underlyingTokenMint, 2 @@ -343,7 +348,7 @@ describe("conditional_vault", async function () { settlementAuthority.publicKey, 2 ); - vault = await vaultClient.initializeNewVault( + vault = await vaultClient.initializeVault( question, underlyingTokenMint, 2 @@ -352,15 +357,26 @@ describe("conditional_vault", async function () { await vaultClient .splitTokensIx(question, vault, underlyingTokenMint, new BN(1000), 2) .rpc(); - }); it("merges tokens", async function () { - const balanceBefore = await getAccount(banksClient, token.getAssociatedTokenAddressSync(underlyingTokenMint, payer.publicKey)).then(acc => acc.amount); + const balanceBefore = await getAccount( + banksClient, + token.getAssociatedTokenAddressSync( + underlyingTokenMint, + payer.publicKey + ) + ).then((acc) => acc.amount); await vaultClient .mergeTokensIx(question, vault, underlyingTokenMint, new BN(600), 2) .rpc(); - const balanceAfter = await getAccount(banksClient, token.getAssociatedTokenAddressSync(underlyingTokenMint, payer.publicKey)).then(acc => acc.amount); + const balanceAfter = await getAccount( + banksClient, + token.getAssociatedTokenAddressSync( + underlyingTokenMint, + payer.publicKey + ) + ).then((acc) => acc.amount); assert.isTrue(balanceAfter > balanceBefore); assert.equal(balanceAfter - balanceBefore, 600); @@ -379,7 +395,7 @@ describe("conditional_vault", async function () { settlementAuthority.publicKey, 2 ); - vault = await vaultClient.initializeNewVault( + vault = await vaultClient.initializeVault( question, underlyingTokenMint, 2 @@ -436,24 +452,40 @@ describe("conditional_vault", async function () { const outcome0Tokens = storedVault.conditionalTokenMints[0]; let burne = Keypair.generate(); - await createAssociatedTokenAccount(banksClient, payer, outcome0Tokens, burne.publicKey); + await createAssociatedTokenAccount( + banksClient, + payer, + outcome0Tokens, + burne.publicKey + ); - await transfer(banksClient, payer, token.getAssociatedTokenAddressSync(outcome0Tokens, payer.publicKey), token.getAssociatedTokenAddressSync(outcome0Tokens, burne.publicKey), payer, 1000n); + await transfer( + banksClient, + payer, + token.getAssociatedTokenAddressSync(outcome0Tokens, payer.publicKey), + token.getAssociatedTokenAddressSync(outcome0Tokens, burne.publicKey), + payer, + 1000n + ); const underlyingTokenAccount = await token.getAssociatedTokenAddress( underlyingTokenMint, payer.publicKey ); - const balanceBefore = await getAccount(banksClient, underlyingTokenAccount) - .then(acc => acc.amount); + const balanceBefore = await getAccount( + banksClient, + underlyingTokenAccount + ).then((acc) => acc.amount); await vaultClient .redeemTokensIx(question, vault, underlyingTokenMint, new BN(600), 2) .rpc(); - const balanceAfter = await getAccount(banksClient, underlyingTokenAccount) - .then(acc => acc.amount); + const balanceAfter = await getAccount( + banksClient, + underlyingTokenAccount + ).then((acc) => acc.amount); assert.isTrue(balanceAfter == balanceBefore); }); diff --git a/tests/conditionalVault/unit/initializeConditionalVault.test.ts b/tests/conditionalVault/unit/initializeConditionalVault.test.ts index b04c9ce9..e123f0dd 100644 --- a/tests/conditionalVault/unit/initializeConditionalVault.test.ts +++ b/tests/conditionalVault/unit/initializeConditionalVault.test.ts @@ -1,94 +1,93 @@ -import { sha256, ConditionalVaultClient, getVaultAddr, getConditionalTokenMintAddr} from "@metadaoproject/futarchy"; +import { + sha256, + ConditionalVaultClient, + getVaultAddr, + getConditionalTokenMintAddr, +} from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { createMint, getMint } from "spl-token-bankrun" -import * as anchor from "@coral-xyz/anchor" -import * as token from "@solana/spl-token" - +import { createMint, getMint } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import * as token from "@solana/spl-token"; export default function suite() { - let vaultClient: ConditionalVaultClient; - let underlyingTokenMint: PublicKey; + let vaultClient: ConditionalVaultClient; + let underlyingTokenMint: PublicKey; - before(async function() { - vaultClient = this.vaultClient; - underlyingTokenMint = await createMint( - this.banksClient, + before(async function () { + vaultClient = this.vaultClient; + underlyingTokenMint = await createMint( + this.banksClient, this.payer as Keypair, Keypair.generate().publicKey, - null, - 8 - ); - }); + null, + 8 + ); + }); - const testCases = [ - { name: "2-outcome question", idArray: [3, 2, 1], outcomes: 2 }, - { name: "3-outcome question", idArray: [4, 5, 6], outcomes: 3 }, - { name: "4-outcome question", idArray: [7, 8, 9], outcomes: 4 }, - ]; + const testCases = [ + { name: "2-outcome question", idArray: [3, 2, 1], outcomes: 2 }, + { name: "3-outcome question", idArray: [4, 5, 6], outcomes: 3 }, + { name: "4-outcome question", idArray: [7, 8, 9], outcomes: 4 }, + ]; - testCases.forEach(({ name, idArray, outcomes }) => { - describe(name, function () { - let question: PublicKey; - let oracle: Keypair = Keypair.generate(); + testCases.forEach(({ name, idArray, outcomes }) => { + describe(name, function () { + let question: PublicKey; + let oracle: Keypair = Keypair.generate(); - beforeEach(async function () { - let questionId = sha256(new Uint8Array(idArray)); - question = await vaultClient.initializeQuestion( - questionId, - oracle.publicKey, - outcomes - ); - }); + beforeEach(async function () { + let questionId = sha256(new Uint8Array(idArray)); + question = await vaultClient.initializeQuestion( + questionId, + oracle.publicKey, + outcomes + ); + }); - it("initializes vaults correctly", async function () { - await vaultClient - .initializeNewVaultIx(question, underlyingTokenMint, outcomes) - .rpc(); + it("initializes vaults correctly", async function () { + await vaultClient + .initializeVaultIx(question, underlyingTokenMint, outcomes) + .rpc(); - const [vault, pdaBump] = getVaultAddr( - vaultClient.vaultProgram.programId, - question, - underlyingTokenMint - ); + const [vault, pdaBump] = getVaultAddr( + vaultClient.vaultProgram.programId, + question, + underlyingTokenMint + ); - const storedVault = await vaultClient.fetchVault(vault); - assert.ok(storedVault.question.equals(question)); - assert.ok( - storedVault.underlyingTokenMint.equals(underlyingTokenMint) - ); + const storedVault = await vaultClient.fetchVault(vault); + assert.ok(storedVault.question.equals(question)); + assert.ok(storedVault.underlyingTokenMint.equals(underlyingTokenMint)); - const vaultUnderlyingTokenAccount = - token.getAssociatedTokenAddressSync( - underlyingTokenMint, - vault, - true - ); - assert.ok( - storedVault.underlyingTokenAccount.equals( - vaultUnderlyingTokenAccount - ) + const vaultUnderlyingTokenAccount = token.getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ); + assert.ok( + storedVault.underlyingTokenAccount.equals(vaultUnderlyingTokenAccount) + ); + const storedConditionalTokenMints = storedVault.conditionalTokenMints; + storedConditionalTokenMints.forEach((mint, i) => { + const [expectedMint] = getConditionalTokenMintAddr( + vaultClient.vaultProgram.programId, + vault, + i ); - const storedConditionalTokenMints = storedVault.conditionalTokenMints; - storedConditionalTokenMints.forEach((mint, i) => { - const [expectedMint] = getConditionalTokenMintAddr( - vaultClient.vaultProgram.programId, - vault, - i - ); - assert.ok(mint.equals(expectedMint)); - }); - assert.equal(storedVault.pdaBump, pdaBump); - assert.equal(storedVault.decimals, 8); - - for (let mint of storedConditionalTokenMints) { - const storedMint = await getMint(this.banksClient, mint); - assert.ok(storedMint.mintAuthority.equals(vault)); - assert.equal(storedMint.supply.toString(), "0"); - assert.equal(storedMint.decimals, 8); - assert.isNull(storedMint.freezeAuthority); - } + assert.ok(mint.equals(expectedMint)); }); + assert.equal(storedVault.pdaBump, pdaBump); + assert.equal(storedVault.decimals, 8); + + for (let mint of storedConditionalTokenMints) { + const storedMint = await getMint(this.banksClient, mint); + assert.ok(storedMint.mintAuthority.equals(vault)); + assert.equal(storedMint.supply.toString(), "0"); + assert.equal(storedMint.decimals, 8); + assert.isNull(storedMint.freezeAuthority); + } }); }); -} \ No newline at end of file + }); +} diff --git a/tests/conditionalVault/unit/initializeQuestion.test.ts b/tests/conditionalVault/unit/initializeQuestion.test.ts index 42273fa8..fb4c037b 100644 --- a/tests/conditionalVault/unit/initializeQuestion.test.ts +++ b/tests/conditionalVault/unit/initializeQuestion.test.ts @@ -1,33 +1,37 @@ -import { sha256, ConditionalVaultClient, getQuestionAddr } from "@metadaoproject/futarchy"; +import { + sha256, + ConditionalVaultClient, + getQuestionAddr, +} from "@metadaoproject/futarchy"; import { Keypair } from "@solana/web3.js"; import { assert } from "chai"; export default function suite() { - let vaultClient: ConditionalVaultClient; - before(function () { - vaultClient = this.vaultClient; - }); + let vaultClient: ConditionalVaultClient; + before(function () { + vaultClient = this.vaultClient; + }); - it("initializes 2-outcome questions", async function () { - let questionId = sha256(new Uint8Array([1, 2, 3])); + it("initializes 2-outcome questions", async function () { + let questionId = sha256(new Uint8Array([1, 2, 3])); - let oracle = Keypair.generate(); + let oracle = Keypair.generate(); - await vaultClient - .initializeQuestionIx(questionId, oracle.publicKey, 2) - .rpc(); + await vaultClient + .initializeQuestionIx(questionId, oracle.publicKey, 2) + .rpc(); - let [question] = getQuestionAddr( - vaultClient.vaultProgram.programId, - questionId, - oracle.publicKey, - 2 - ); + let [question] = getQuestionAddr( + vaultClient.vaultProgram.programId, + questionId, + oracle.publicKey, + 2 + ); - const storedQuestion = await vaultClient.fetchQuestion(question); - assert.deepEqual(storedQuestion.questionId, Array.from(questionId)); - assert.ok(storedQuestion.oracle.equals(oracle.publicKey)); - assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); - assert.equal(storedQuestion.payoutDenominator, 0); - }); -} \ No newline at end of file + const storedQuestion = await vaultClient.fetchQuestion(question); + assert.deepEqual(storedQuestion.questionId, Array.from(questionId)); + assert.ok(storedQuestion.oracle.equals(oracle.publicKey)); + assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); + assert.equal(storedQuestion.payoutDenominator, 0); + }); +} diff --git a/tests/conditionalVault/unit/mergeTokens.test.ts b/tests/conditionalVault/unit/mergeTokens.test.ts index 471cb639..b34d9e29 100644 --- a/tests/conditionalVault/unit/mergeTokens.test.ts +++ b/tests/conditionalVault/unit/mergeTokens.test.ts @@ -1,75 +1,99 @@ import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { createAssociatedTokenAccount, createMint, getAccount, mintTo } from "spl-token-bankrun"; +import { + createAssociatedTokenAccount, + createMint, + getAccount, + mintTo, +} from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; export default function suite() { - let vaultClient: ConditionalVaultClient; - let question: PublicKey; - let vault: PublicKey; - let underlyingTokenMint: PublicKey; - let userUnderlyingTokenAccount: PublicKey; - before(function () { - vaultClient = this.vaultClient; - }); + let vaultClient: ConditionalVaultClient; + let question: PublicKey; + let vault: PublicKey; + let underlyingTokenMint: PublicKey; + let userUnderlyingTokenAccount: PublicKey; + before(function () { + vaultClient = this.vaultClient; + }); - beforeEach(async function () { - let questionId = sha256(new Uint8Array([9, 2, 1])); - let oracle = Keypair.generate(); + beforeEach(async function () { + let questionId = sha256(new Uint8Array([9, 2, 1])); + let oracle = Keypair.generate(); - question = await vaultClient.initializeQuestion( - questionId, - oracle.publicKey, - 2 - ); + question = await vaultClient.initializeQuestion( + questionId, + oracle.publicKey, + 2 + ); - underlyingTokenMint = await createMint( - this.banksClient, - this.payer, - this.payer.publicKey, - null, - 8 - ); + underlyingTokenMint = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + null, + 8 + ); - vault = await vaultClient.initializeNewVault( - question, - underlyingTokenMint, - 2 - ); + vault = await vaultClient.initializeVault(question, underlyingTokenMint, 2); - userUnderlyingTokenAccount = await createAssociatedTokenAccount( - this.banksClient, - this.payer, - underlyingTokenMint, - this.payer.publicKey - ); + userUnderlyingTokenAccount = await createAssociatedTokenAccount( + this.banksClient, + this.payer, + underlyingTokenMint, + this.payer.publicKey + ); - // Mint some underlying tokens to the user's account - await mintTo( - this.banksClient, - this.payer, - underlyingTokenMint, - userUnderlyingTokenAccount, - this.payer, - 1000 - ); + // Mint some underlying tokens to the user's account + await mintTo( + this.banksClient, + this.payer, + underlyingTokenMint, + userUnderlyingTokenAccount, + this.payer, + 1000 + ); + await vaultClient + .splitTokensIx( + question, + vault, + underlyingTokenMint, + new anchor.BN(1000), + 2 + ) + .rpc(); + }); - await vaultClient - .splitTokensIx(question, vault, underlyingTokenMint, new anchor.BN(1000), 2) - .rpc(); - }); + it("merges tokens", async function () { + const balanceBefore = await getAccount( + this.banksClient, + token.getAssociatedTokenAddressSync( + underlyingTokenMint, + this.payer.publicKey + ) + ).then((acc) => acc.amount); + await vaultClient + .mergeTokensIx( + question, + vault, + underlyingTokenMint, + new anchor.BN(600), + 2 + ) + .rpc(); + const balanceAfter = await getAccount( + this.banksClient, + token.getAssociatedTokenAddressSync( + underlyingTokenMint, + this.payer.publicKey + ) + ).then((acc) => acc.amount); - it("merges tokens", async function () { - const balanceBefore = await getAccount(this.banksClient, token.getAssociatedTokenAddressSync(underlyingTokenMint, this.payer.publicKey)).then(acc => acc.amount); - await vaultClient - .mergeTokensIx(question, vault, underlyingTokenMint, new anchor.BN(600), 2) - .rpc(); - const balanceAfter = await getAccount(this.banksClient, token.getAssociatedTokenAddressSync(underlyingTokenMint, this.payer.publicKey)).then(acc => acc.amount); - - assert.isTrue(balanceAfter > balanceBefore); - assert.equal(balanceAfter - balanceBefore, 600); - }); -} \ No newline at end of file + assert.isTrue(balanceAfter > balanceBefore); + assert.equal(balanceAfter - balanceBefore, 600); + }); +} diff --git a/tests/conditionalVault/unit/redeemTokens.test.ts b/tests/conditionalVault/unit/redeemTokens.test.ts index 4b32b512..afaa80f0 100644 --- a/tests/conditionalVault/unit/redeemTokens.test.ts +++ b/tests/conditionalVault/unit/redeemTokens.test.ts @@ -1,100 +1,111 @@ import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { createAssociatedTokenAccount, createMint, getAccount, mintTo } from "spl-token-bankrun"; +import { + createAssociatedTokenAccount, + createMint, + getAccount, + mintTo, +} from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; export default function suite() { - let vaultClient: ConditionalVaultClient; - let question: PublicKey; - let vault: PublicKey; - let underlyingTokenMint: PublicKey; - let settlementAuthority: Keypair; - let userUnderlyingTokenAccount: PublicKey; - - before(function () { - vaultClient = this.vaultClient; - }); - - beforeEach(async function () { - let questionId = sha256(new Uint8Array([9, 28, 2, 1])); - settlementAuthority = Keypair.generate(); - - question = await vaultClient.initializeQuestion( - questionId, - settlementAuthority.publicKey, - 2 - ); - - underlyingTokenMint = await createMint( - this.banksClient, - this.payer, - this.payer.publicKey, - null, - 8 - ); - - vault = await vaultClient.initializeNewVault( - question, - underlyingTokenMint, - 2 - ); - - userUnderlyingTokenAccount = await createAssociatedTokenAccount( - this.banksClient, - this.payer, - underlyingTokenMint, - this.payer.publicKey - ); - - // Mint some underlying tokens to the user's account - await mintTo( - this.banksClient, - this.payer, - underlyingTokenMint, - userUnderlyingTokenAccount, - this.payer, - 1000 - ); - - await vaultClient - .splitTokensIx(question, vault, underlyingTokenMint, new anchor.BN(1000), 2) - .rpc(); - }); - - it("can't redeem tokens when question is not resolved", async function () { - try { - await vaultClient - .redeemTokensIx(question, vault, underlyingTokenMint, 2) - .rpc(); - assert.fail("Should have thrown an error"); - } catch (error) { - assert.include(error.message, "CantRedeemConditionalTokens"); - } - }); - - it("can redeem tokens when question is resolved", async function () { - await vaultClient - .resolveQuestionIx(question, settlementAuthority, [1, 0]) - .rpc(); - - const underlyingTokenAccount = await token.getAssociatedTokenAddress( - underlyingTokenMint, - this.payer.publicKey - ); - - const balanceBefore = await getAccount(this.banksClient, underlyingTokenAccount) - .then(acc => acc.amount); - - await vaultClient - .redeemTokensIx(question, vault, underlyingTokenMint, 2) - .rpc(); - - const balanceAfter = await getAccount(this.banksClient, underlyingTokenAccount) - .then(acc => acc.amount); - - assert.isTrue(balanceAfter > balanceBefore); - assert.equal(balanceAfter - balanceBefore, 1000); - }); + let vaultClient: ConditionalVaultClient; + let question: PublicKey; + let vault: PublicKey; + let underlyingTokenMint: PublicKey; + let settlementAuthority: Keypair; + let userUnderlyingTokenAccount: PublicKey; + + before(function () { + vaultClient = this.vaultClient; + }); + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([9, 28, 2, 1])); + settlementAuthority = Keypair.generate(); + + question = await vaultClient.initializeQuestion( + questionId, + settlementAuthority.publicKey, + 2 + ); + + underlyingTokenMint = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + null, + 8 + ); + + vault = await vaultClient.initializeVault(question, underlyingTokenMint, 2); + + userUnderlyingTokenAccount = await createAssociatedTokenAccount( + this.banksClient, + this.payer, + underlyingTokenMint, + this.payer.publicKey + ); + + // Mint some underlying tokens to the user's account + await mintTo( + this.banksClient, + this.payer, + underlyingTokenMint, + userUnderlyingTokenAccount, + this.payer, + 1000 + ); + + await vaultClient + .splitTokensIx( + question, + vault, + underlyingTokenMint, + new anchor.BN(1000), + 2 + ) + .rpc(); + }); + + it("can't redeem tokens when question is not resolved", async function () { + try { + await vaultClient + .redeemTokensIx(question, vault, underlyingTokenMint, 2) + .rpc(); + assert.fail("Should have thrown an error"); + } catch (error) { + assert.include(error.message, "CantRedeemConditionalTokens"); + } + }); + + it("can redeem tokens when question is resolved", async function () { + await vaultClient + .resolveQuestionIx(question, settlementAuthority, [1, 0]) + .rpc(); + + const underlyingTokenAccount = await token.getAssociatedTokenAddress( + underlyingTokenMint, + this.payer.publicKey + ); + + const balanceBefore = await getAccount( + this.banksClient, + underlyingTokenAccount + ).then((acc) => acc.amount); + + await vaultClient + .redeemTokensIx(question, vault, underlyingTokenMint, 2) + .rpc(); + + const balanceAfter = await getAccount( + this.banksClient, + underlyingTokenAccount + ).then((acc) => acc.amount); + + assert.isTrue(balanceAfter > balanceBefore); + assert.equal(balanceAfter - balanceBefore, 1000); + }); } diff --git a/tests/conditionalVault/unit/resolveQuestion.test.ts b/tests/conditionalVault/unit/resolveQuestion.test.ts index 0f790f14..e9a135da 100644 --- a/tests/conditionalVault/unit/resolveQuestion.test.ts +++ b/tests/conditionalVault/unit/resolveQuestion.test.ts @@ -3,37 +3,37 @@ import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; export default function suite() { - let vaultClient: ConditionalVaultClient; - let question: PublicKey; - let settlementAuthority: Keypair; - - before(function () { - vaultClient = this.vaultClient; - }); - - beforeEach(async function () { - let questionId = sha256(new Uint8Array([4, 2, 1])); - settlementAuthority = Keypair.generate(); - question = await vaultClient.initializeQuestion( - questionId, - settlementAuthority.publicKey, - 2 - ); - }); - - it("resolves questions", async function () { - let storedQuestion = await vaultClient.fetchQuestion(question); - - assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); - assert.equal(storedQuestion.payoutDenominator, 0); - - await vaultClient - .resolveQuestionIx(question, settlementAuthority, [1, 0]) - .rpc(); - - storedQuestion = await vaultClient.fetchQuestion(question); - - assert.deepEqual(storedQuestion.payoutNumerators, [1, 0]); - assert.equal(storedQuestion.payoutDenominator, 1); - }); -} \ No newline at end of file + let vaultClient: ConditionalVaultClient; + let question: PublicKey; + let settlementAuthority: Keypair; + + before(function () { + vaultClient = this.vaultClient; + }); + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([4, 2, 1])); + settlementAuthority = Keypair.generate(); + question = await vaultClient.initializeQuestion( + questionId, + settlementAuthority.publicKey, + 2 + ); + }); + + it("resolves questions", async function () { + let storedQuestion = await vaultClient.fetchQuestion(question); + + assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); + assert.equal(storedQuestion.payoutDenominator, 0); + + await vaultClient + .resolveQuestionIx(question, settlementAuthority, [1, 0]) + .rpc(); + + storedQuestion = await vaultClient.fetchQuestion(question); + + assert.deepEqual(storedQuestion.payoutNumerators, [1, 0]); + assert.equal(storedQuestion.payoutDenominator, 1); + }); +} diff --git a/tests/conditionalVault/unit/splitTokens.test.ts b/tests/conditionalVault/unit/splitTokens.test.ts index 43deb5d5..bf6e8170 100644 --- a/tests/conditionalVault/unit/splitTokens.test.ts +++ b/tests/conditionalVault/unit/splitTokens.test.ts @@ -1,155 +1,154 @@ import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { createAssociatedTokenAccount, createMint, getAccount, getMint, mintTo } from "spl-token-bankrun"; +import { + createAssociatedTokenAccount, + createMint, + getAccount, + getMint, + mintTo, +} from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; import { expectError } from "../../utils"; export default function suite() { - let vaultClient: ConditionalVaultClient; - let question: PublicKey; - let vault: PublicKey; - let underlyingTokenMint: PublicKey; - - before(function () { - vaultClient = this.vaultClient; - }); - - beforeEach(async function () { - let questionId = sha256(new Uint8Array([5, 2, 1])); - let oracle = Keypair.generate(); - - question = await vaultClient.initializeQuestion( - questionId, - oracle.publicKey, - 2 - ); - - underlyingTokenMint = await this.createMint( - this.payer.publicKey, - 8 - ); - - vault = await vaultClient.initializeNewVault( - question, - underlyingTokenMint, - 2 - ); - - await this.createTokenAccount( - underlyingTokenMint, - this.payer.publicKey - ); - - await this.mintTo( - underlyingTokenMint, - this.payer.publicKey, - this.payer, - 10_000_000_000n - ); - }); - - it("splits tokens", async function () { - await vaultClient - .splitTokensIx(question, vault, underlyingTokenMint, new anchor.BN(1000), 2) - .rpc(); - - const storedVault = await vaultClient.fetchVault(vault); - - this.assertBalance(underlyingTokenMint, vault, 1000); - - const storedConditionalTokenMints = storedVault.conditionalTokenMints; - for (let mint of storedConditionalTokenMints) { - let storedMint = await getMint( - this.banksClient, - mint - ); - assert.equal(storedMint.supply.toString(), "1000"); - await this.assertBalance(mint, this.payer.publicKey, 1000); - } - }); - - it("throws error if conditional token accounts don't exist", async function () { - let { remainingAccounts } = vaultClient.getConditionalTokenAccountsAndInstructions( - vault, - 2, - this.payer.publicKey - ); - - const callbacks = expectError( - "BadConditionalTokenAccount", - "split succeeded despite conditional token account not existing" - ); - - await vaultClient.vaultProgram.methods - .splitTokens(new anchor.BN(1000)) - .accounts({ - question, - authority: this.payer.publicKey, - vault, - vaultUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( - underlyingTokenMint, - vault, - true - ), - userUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( - underlyingTokenMint, - this.payer.publicKey, - true - ), - }) - .remainingAccounts(remainingAccounts) - .rpc() - .then(callbacks[0], callbacks[1]); - }); - - it("throws error if the conditional mints are wrong", async function () { - const callbacks = expectError( - "ConditionalMintMismatch", - "split succeeded despite conditional mint not being in vault" - ); - - const fakeConditionalMint1 = await this.createMint( - vault, - 8 - ); - - const fakeConditionalMint2 = await this.createMint( - vault, - 8 - ); - - const fakeConditionalTokenAccount1 = await this.createTokenAccount( - fakeConditionalMint1, - this.payer.publicKey - ); - - const fakeConditionalTokenAccount2 = await this.createTokenAccount( - fakeConditionalMint2, - this.payer.publicKey - ); - - // Attempt to split tokens using the original vault but with the malicious vault's conditional token accounts - await vaultClient.vaultProgram.methods - .splitTokens(new anchor.BN(1000)) - .accounts({ - question, - authority: this.payer.publicKey, - vault, - vaultUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( - underlyingTokenMint, - vault, - true - ), - userUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( - underlyingTokenMint, - this.payer.publicKey, - true - ), - }) - .remainingAccounts(vaultClient.getRemainingAccounts([fakeConditionalMint1, fakeConditionalMint2], [fakeConditionalTokenAccount1, fakeConditionalTokenAccount2])) - .rpc() - .then(callbacks[0], callbacks[1]); - }); -} \ No newline at end of file + let vaultClient: ConditionalVaultClient; + let question: PublicKey; + let vault: PublicKey; + let underlyingTokenMint: PublicKey; + + before(function () { + vaultClient = this.vaultClient; + }); + + beforeEach(async function () { + let questionId = sha256(new Uint8Array([5, 2, 1])); + let oracle = Keypair.generate(); + + question = await vaultClient.initializeQuestion( + questionId, + oracle.publicKey, + 2 + ); + + underlyingTokenMint = await this.createMint(this.payer.publicKey, 8); + + vault = await vaultClient.initializeVault(question, underlyingTokenMint, 2); + + await this.createTokenAccount(underlyingTokenMint, this.payer.publicKey); + + await this.mintTo( + underlyingTokenMint, + this.payer.publicKey, + this.payer, + 10_000_000_000n + ); + }); + + it("splits tokens", async function () { + await vaultClient + .splitTokensIx( + question, + vault, + underlyingTokenMint, + new anchor.BN(1000), + 2 + ) + .rpc(); + + const storedVault = await vaultClient.fetchVault(vault); + + this.assertBalance(underlyingTokenMint, vault, 1000); + + const storedConditionalTokenMints = storedVault.conditionalTokenMints; + for (let mint of storedConditionalTokenMints) { + let storedMint = await getMint(this.banksClient, mint); + assert.equal(storedMint.supply.toString(), "1000"); + await this.assertBalance(mint, this.payer.publicKey, 1000); + } + }); + + it("throws error if conditional token accounts don't exist", async function () { + let { remainingAccounts } = + vaultClient.getConditionalTokenAccountsAndInstructions( + vault, + 2, + this.payer.publicKey + ); + + const callbacks = expectError( + "BadConditionalTokenAccount", + "split succeeded despite conditional token account not existing" + ); + + await vaultClient.vaultProgram.methods + .splitTokens(new anchor.BN(1000)) + .accounts({ + question, + authority: this.payer.publicKey, + vault, + vaultUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + this.payer.publicKey, + true + ), + }) + .remainingAccounts(remainingAccounts) + .rpc() + .then(callbacks[0], callbacks[1]); + }); + + it("throws error if the conditional mints are wrong", async function () { + const callbacks = expectError( + "ConditionalMintMismatch", + "split succeeded despite conditional mint not being in vault" + ); + + const fakeConditionalMint1 = await this.createMint(vault, 8); + + const fakeConditionalMint2 = await this.createMint(vault, 8); + + const fakeConditionalTokenAccount1 = await this.createTokenAccount( + fakeConditionalMint1, + this.payer.publicKey + ); + + const fakeConditionalTokenAccount2 = await this.createTokenAccount( + fakeConditionalMint2, + this.payer.publicKey + ); + + // Attempt to split tokens using the original vault but with the malicious vault's conditional token accounts + await vaultClient.vaultProgram.methods + .splitTokens(new anchor.BN(1000)) + .accounts({ + question, + authority: this.payer.publicKey, + vault, + vaultUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + this.payer.publicKey, + true + ), + }) + .remainingAccounts( + vaultClient.getRemainingAccounts( + [fakeConditionalMint1, fakeConditionalMint2], + [fakeConditionalTokenAccount1, fakeConditionalTokenAccount2] + ) + ) + .rpc() + .then(callbacks[0], callbacks[1]); + }); +} diff --git a/tests/main.test.ts b/tests/main.test.ts index 79bc5186..9079fe9b 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -5,73 +5,120 @@ import autocrat from "./autocrat/autocrat"; import { startAnchor } from "solana-bankrun"; import { BankrunProvider } from "anchor-bankrun"; import * as anchor from "@coral-xyz/anchor"; -import { AmmClient, AutocratClient, ConditionalVaultClient } from "@metadaoproject/futarchy"; -import { PublicKey, Keypair} from "@solana/web3.js"; -import { createAssociatedTokenAccount, createMint, mintTo, getAccount, transfer } from "spl-token-bankrun"; +import { + AmmClient, + AutocratClient, + ConditionalVaultClient, +} from "@metadaoproject/futarchy"; +import { PublicKey, Keypair } from "@solana/web3.js"; +import { + createAssociatedTokenAccount, + createMint, + mintTo, + getAccount, + transfer, +} from "spl-token-bankrun"; import * as token from "@solana/spl-token"; import { assert } from "chai"; before(async function () { - this.context = await startAnchor( - "./", - [], - // [ - // // even though the program is loaded into the test validator, we need - // // to tell banks test client to load it as well - // { - // name: "mpl_token_metadata", - // programId: MPL_TOKEN_METADATA_PROGRAM_ID, - // }, - // ], - [] - ); - this.banksClient = this.context.banksClient; - let provider = new BankrunProvider(this.context); - anchor.setProvider(provider); - - // umi = createUmi(anchor.AnchorProvider.env().connection); + this.context = await startAnchor( + "./", + [], + // [ + // // even though the program is loaded into the test validator, we need + // // to tell banks test client to load it as well + // { + // name: "mpl_token_metadata", + // programId: MPL_TOKEN_METADATA_PROGRAM_ID, + // }, + // ], + [] + ); + this.banksClient = this.context.banksClient; + let provider = new BankrunProvider(this.context); + anchor.setProvider(provider); - // vaultProgram = new Program( - // ConditionalVaultIDL, - // CONDITIONAL_VAULT_PROGRAM_ID, - // provider - // ); + // umi = createUmi(anchor.AnchorProvider.env().connection); - this.vaultClient = ConditionalVaultClient.createClient({ provider: provider as any }); - this.autocratClient = AutocratClient.createClient({ provider: provider as any }); - this.ammClient = AmmClient.createClient({ provider: provider as any }); - this.payer = provider.wallet.payer; + // vaultProgram = new Program( + // ConditionalVaultIDL, + // CONDITIONAL_VAULT_PROGRAM_ID, + // provider + // ); + this.vaultClient = ConditionalVaultClient.createClient({ + provider: provider as any, + }); + this.autocratClient = AutocratClient.createClient({ + provider: provider as any, + }); + this.ammClient = AmmClient.createClient({ provider: provider as any }); + this.payer = provider.wallet.payer; - this.createTokenAccount = async (mint: PublicKey, owner: PublicKey) => { - return await createAssociatedTokenAccount( - this.banksClient, - this.payer, - mint, - owner - ); - } + this.createTokenAccount = async (mint: PublicKey, owner: PublicKey) => { + return await createAssociatedTokenAccount( + this.banksClient, + this.payer, + mint, + owner + ); + }; - this.createMint = async (mintAuthority: PublicKey, decimals: number) => { - return await createMint(this.banksClient, this.payer, mintAuthority, null, decimals); - } + this.createMint = async (mintAuthority: PublicKey, decimals: number) => { + return await createMint( + this.banksClient, + this.payer, + mintAuthority, + null, + decimals + ); + }; - this.mintTo = async (mint: PublicKey, to: PublicKey, mintAuthority: Keypair, amount: number) => { - const tokenAccount = token.getAssociatedTokenAddressSync(mint, to, true); - return await mintTo(this.banksClient, this.payer, mint, tokenAccount, mintAuthority, amount); - } + this.mintTo = async ( + mint: PublicKey, + to: PublicKey, + mintAuthority: Keypair, + amount: number + ) => { + const tokenAccount = token.getAssociatedTokenAddressSync(mint, to, true); + return await mintTo( + this.banksClient, + this.payer, + mint, + tokenAccount, + mintAuthority, + amount + ); + }; - this.assertBalance = async (mint: PublicKey, owner: PublicKey, amount: number) => { - const tokenAccount = token.getAssociatedTokenAddressSync(mint, owner, true); - const storedTokenAccount = await getAccount(this.banksClient, tokenAccount); - assert.equal(storedTokenAccount.amount.toString(), amount.toString()); - } + this.assertBalance = async ( + mint: PublicKey, + owner: PublicKey, + amount: number + ) => { + const tokenAccount = token.getAssociatedTokenAddressSync(mint, owner, true); + const storedTokenAccount = await getAccount(this.banksClient, tokenAccount); + assert.equal(storedTokenAccount.amount.toString(), amount.toString()); + }; - this.transfer = async (mint: PublicKey, from: Keypair, to: PublicKey, amount: number) => { - return await transfer(this.banksClient, this.payer, token.getAssociatedTokenAddressSync(mint, from.publicKey, true), token.getAssociatedTokenAddressSync(mint, to, true), from, amount); - } + this.transfer = async ( + mint: PublicKey, + from: Keypair, + to: PublicKey, + amount: number + ) => { + return await transfer( + this.banksClient, + this.payer, + token.getAssociatedTokenAddressSync(mint, from.publicKey, true), + token.getAssociatedTokenAddressSync(mint, to, true), + from, + amount + ); + }; }); describe("conditional_vault", conditionalVault); describe("amm", amm); -describe("autocrat", autocrat); \ No newline at end of file +describe("autocrat", autocrat); diff --git a/tests/optimisticTimelock/timelock.ts b/tests/optimisticTimelock/timelock.ts index ccf4ecb4..b0b7cb2c 100644 --- a/tests/optimisticTimelock/timelock.ts +++ b/tests/optimisticTimelock/timelock.ts @@ -13,7 +13,10 @@ const TIMELOCK_PROGRAM_ID = new PublicKey( "tiME1hz9F5C5ZecbvE5z6Msjy8PKfTqo1UuRYXfndKF" ); -import { OptimisticTimelock, IDL as OptimisticTimelockIDL } from "../../target/types/optimistic_timelock"; +import { + OptimisticTimelock, + IDL as OptimisticTimelockIDL, +} from "../../target/types/optimistic_timelock"; import { ComputeBudgetProgram, Connection } from "@solana/web3.js"; import { printArgs } from "@metaplex-foundation/mpl-token-metadata"; // const OptimisticTimelockIDL: OptimisticTimelock = require("../target/idl/optimistic_timelock.json"); From 69668612b32b5fd6dd7ae6139dbc1040890ed2eb Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 39/52] v0.3.0-alpha.15 --- sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/package.json b/sdk/package.json index e2abb5ed..2afba20e 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@metadaoproject/futarchy", - "version": "0.3.0-alpha.14", + "version": "0.3.0-alpha.15", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ From daf9404d36f5891820ca54eed6a687e0bf58f825 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Sat, 24 Aug 2024 00:00:00 +0000 Subject: [PATCH 40/52] Update to v15 SDK --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9f1472b8..290a130d 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ }, "dependencies": { "@coral-xyz/anchor": "0.29.0", - "@metadaoproject/futarchy": "0.3.0-alpha.14", + "@metadaoproject/futarchy": "0.3.0-alpha.15", "@metaplex-foundation/mpl-token-metadata": "^3.2.0", "@metaplex-foundation/umi": "^0.9.1", "@metaplex-foundation/umi-bundle-defaults": "^0.9.1", diff --git a/yarn.lock b/yarn.lock index 96a62240..624988b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -629,10 +629,10 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@metadaoproject/futarchy@0.3.0-alpha.14": - version "0.3.0-alpha.14" - resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.14.tgz#ed6640cef72986f72835f6bbef12854e5ef3a404" - integrity sha512-ut3IAhe0zS10ySzDUdQlaL1GHue/rm3Xcp9JlLu8bKGu0F10Tj/l1OEwaJNoL1eBsvjZbq3uzClOV1cs5gjnBQ== +"@metadaoproject/futarchy@0.3.0-alpha.15": + version "0.3.0-alpha.15" + resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.15.tgz#48832c6cfd803d42ebfd9545600a0ae24225babf" + integrity sha512-1O/Zvtc9Iyz2UzGP3a5CuftRXRr2a+pmAQAODpNxARlU5qDcdmiKVGRpYF90ief8cRHMKrGvSio48Lhq/DrhNQ== dependencies: "@coral-xyz/anchor" "^0.29.0" "@noble/hashes" "^1.4.0" From 0640a136f96f5a0c55f2f0e4cb74427f90698692 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Mon, 26 Aug 2024 00:00:00 +0000 Subject: [PATCH 41/52] Start with metadata --- .../add_metadata_to_conditional_tokens.rs | 115 ++++++--------- .../src/instructions/common.rs | 6 +- .../src/instructions/merge_tokens.rs | 2 +- .../conditional_vault/src/instructions/mod.rs | 4 +- .../src/instructions/redeem_tokens.rs | 2 +- .../src/instructions/split_tokens.rs | 2 +- programs/conditional_vault/src/lib.rs | 26 ++-- sdk/src/ConditionalVaultClient.ts | 88 ++++++------ sdk/src/types/conditional_vault.ts | 136 ++++++++++++++++++ tests/main.test.ts | 28 ++-- 10 files changed, 262 insertions(+), 147 deletions(-) diff --git a/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs b/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs index 3b40008d..2e1e7eb3 100644 --- a/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs +++ b/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs @@ -8,43 +8,28 @@ pub mod proph3t_deployer { #[derive(AnchorSerialize, AnchorDeserialize)] pub struct AddMetadataToConditionalTokensArgs { - pub proposal_number: u64, - pub on_finalize_uri: String, - pub on_revert_uri: String, + // pub uri: String, + pub name: String, + pub symbol: String, + pub image: String, } #[derive(Accounts)] pub struct AddMetadataToConditionalTokens<'info> { #[account(mut)] pub payer: Signer<'info>, - #[account( - mut, - has_one = underlying_token_mint, - )] - pub vault: Account<'info, ConditionalVault>, #[account(mut)] - pub underlying_token_mint: Account<'info, Mint>, - pub underlying_token_metadata: Account<'info, MetadataAccount>, - #[account( - mut, - mint::authority = vault, - mint::freeze_authority = vault, - mint::decimals = underlying_token_mint.decimals - )] - pub conditional_on_finalize_token_mint: Account<'info, Mint>, + pub vault: Account<'info, ConditionalVault>, + // pub underlying_token_metadata: Account<'info, MetadataAccount>, #[account( mut, mint::authority = vault, mint::freeze_authority = vault, - mint::decimals = underlying_token_mint.decimals )] - pub conditional_on_revert_token_mint: Account<'info, Mint>, + pub conditional_token_mint: Account<'info, Mint>, /// CHECK: verified via cpi into token metadata #[account(mut)] - pub conditional_on_finalize_token_metadata: AccountInfo<'info>, - /// CHECK: verified via cpi into token metadata - #[account(mut)] - pub conditional_on_revert_token_metadata: AccountInfo<'info>, + pub conditional_token_metadata: AccountInfo<'info>, pub token_metadata_program: Program<'info, Metadata>, pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, @@ -52,10 +37,10 @@ pub struct AddMetadataToConditionalTokens<'info> { impl AddMetadataToConditionalTokens<'_> { pub fn validate(&self) -> Result<()> { - require!( - self.vault.status == VaultStatus::Active, - VaultError::VaultAlreadySettled - ); + // require!( + // self.vault.status == VaultStatus::Active, + // VaultError::VaultAlreadySettled + // ); #[cfg(feature = "production")] require_eq!( @@ -69,55 +54,39 @@ impl AddMetadataToConditionalTokens<'_> { let seeds = generate_vault_seeds!(ctx.accounts.vault); let signer_seeds = &[&seeds[..]]; - // there are null bytes we must trim from string, otherwise string value is longer than we want - let underlying_token_symbol_raw = ctx.accounts.underlying_token_metadata.symbol.clone(); - let underlying_token_symbol = underlying_token_symbol_raw.trim_matches(char::from(0)); - - let on_finalize_token_symbol = format!("p{}", underlying_token_symbol); - let on_revert_token_symbol = format!("f{}", underlying_token_symbol); + // let underlying_token_symbol_raw = ctx.accounts.underlying_token_metadata.symbol.clone(); + // let underlying_token_symbol = underlying_token_symbol_raw.trim_matches(char::from(0)); - for (symbol, uri, metadata, mint) in [ - ( - on_finalize_token_symbol, - args.on_finalize_uri, - &ctx.accounts.conditional_on_finalize_token_metadata, - &ctx.accounts.conditional_on_finalize_token_mint, - ), - ( - on_revert_token_symbol, - args.on_revert_uri, - &ctx.accounts.conditional_on_revert_token_metadata, - &ctx.accounts.conditional_on_revert_token_mint, - ), - ] { - let cpi_program = ctx.accounts.token_metadata_program.to_account_info(); + let cpi_program = ctx.accounts.token_metadata_program.to_account_info(); - let cpi_accounts = CreateMetadataAccountsV3 { - metadata: metadata.to_account_info(), - mint: mint.to_account_info(), - mint_authority: ctx.accounts.vault.to_account_info(), - payer: ctx.accounts.payer.to_account_info(), - update_authority: ctx.accounts.vault.to_account_info(), - system_program: ctx.accounts.system_program.to_account_info(), - rent: ctx.accounts.rent.to_account_info(), - }; + let cpi_accounts = CreateMetadataAccountsV3 { + metadata: ctx.accounts.conditional_token_metadata.to_account_info(), + mint: ctx.accounts.conditional_token_mint.to_account_info(), + mint_authority: ctx.accounts.vault.to_account_info(), + payer: ctx.accounts.payer.to_account_info(), + update_authority: ctx.accounts.vault.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + rent: ctx.accounts.rent.to_account_info(), + }; - create_metadata_accounts_v3( - CpiContext::new(cpi_program, cpi_accounts).with_signer(signer_seeds), - DataV2 { - name: format!("Proposal {}: {}", args.proposal_number, symbol), - symbol, - uri, - seller_fee_basis_points: 0, - creators: None, - collection: None, - uses: None, - }, - false, - true, - None, - )?; - } + create_metadata_accounts_v3( + CpiContext::new(cpi_program, cpi_accounts).with_signer(signer_seeds), + DataV2 { + name: args.name.clone(), + symbol: args.symbol.clone(), + uri: format!( + "data:,{{\"name\":\"{}\",\"symbol\":\"{}\",\"image\":\"{}\"}}", + args.name, args.symbol, args.image + ), + seller_fee_basis_points: 0, + creators: None, + collection: None, + uses: None, + }, + false, + true, + None, + )?; Ok(()) } diff --git a/programs/conditional_vault/src/instructions/common.rs b/programs/conditional_vault/src/instructions/common.rs index b4050407..af31b9e9 100644 --- a/programs/conditional_vault/src/instructions/common.rs +++ b/programs/conditional_vault/src/instructions/common.rs @@ -1,7 +1,7 @@ use super::*; #[derive(Accounts)] -pub struct InteractWithNewVault<'info> { +pub struct InteractWithVault<'info> { pub question: Account<'info, Question>, #[account(has_one = question)] pub vault: Account<'info, ConditionalVault>, @@ -20,11 +20,11 @@ pub struct InteractWithNewVault<'info> { pub token_program: Program<'info, Token>, } -impl<'info, 'c: 'info> InteractWithNewVault<'info> { +impl<'info, 'c: 'info> InteractWithVault<'info> { pub fn get_mints_and_user_token_accounts( ctx: &Context<'_, '_, 'c, 'info, Self>, ) -> Result<(Vec>, Vec>)> { - msg!("HelloWorld"); + // msg!("HelloWorld"); let remaining_accs = &mut ctx.remaining_accounts.iter(); // msg!("{:?}", remaining_accs); diff --git a/programs/conditional_vault/src/instructions/merge_tokens.rs b/programs/conditional_vault/src/instructions/merge_tokens.rs index ebc0fffb..5e86ebdc 100644 --- a/programs/conditional_vault/src/instructions/merge_tokens.rs +++ b/programs/conditional_vault/src/instructions/merge_tokens.rs @@ -1,6 +1,6 @@ use super::*; -impl<'info, 'c: 'info> InteractWithNewVault<'info> { +impl<'info, 'c: 'info> InteractWithVault<'info> { pub fn handle_merge_tokens(ctx: Context<'_, '_, 'c, 'info, Self>, amount: u64) -> Result<()> { let accs = &ctx.accounts; diff --git a/programs/conditional_vault/src/instructions/mod.rs b/programs/conditional_vault/src/instructions/mod.rs index 2b0df556..a8ba7368 100644 --- a/programs/conditional_vault/src/instructions/mod.rs +++ b/programs/conditional_vault/src/instructions/mod.rs @@ -1,6 +1,6 @@ use super::*; -// pub mod add_metadata_to_conditional_tokens; +pub mod add_metadata_to_conditional_tokens; pub mod common; pub mod initialize_conditional_vault; pub mod initialize_question; @@ -10,7 +10,7 @@ pub mod resolve_question; pub mod split_tokens; pub use initialize_question::*; -// pub use add_metadata_to_conditional_tokens::*; +pub use add_metadata_to_conditional_tokens::*; pub use common::*; pub use initialize_conditional_vault::*; pub use resolve_question::*; diff --git a/programs/conditional_vault/src/instructions/redeem_tokens.rs b/programs/conditional_vault/src/instructions/redeem_tokens.rs index 4b5731fc..389a0a86 100644 --- a/programs/conditional_vault/src/instructions/redeem_tokens.rs +++ b/programs/conditional_vault/src/instructions/redeem_tokens.rs @@ -1,6 +1,6 @@ use super::*; -impl<'info, 'c: 'info> InteractWithNewVault<'info> { +impl<'info, 'c: 'info> InteractWithVault<'info> { pub fn validate_redeem_tokens(&self) -> Result<()> { require!( self.question.is_resolved(), diff --git a/programs/conditional_vault/src/instructions/split_tokens.rs b/programs/conditional_vault/src/instructions/split_tokens.rs index 472bc9d1..26e80762 100644 --- a/programs/conditional_vault/src/instructions/split_tokens.rs +++ b/programs/conditional_vault/src/instructions/split_tokens.rs @@ -1,6 +1,6 @@ use super::*; -impl<'info, 'c: 'info> InteractWithNewVault<'info> { +impl<'info, 'c: 'info> InteractWithVault<'info> { pub fn handle_split_tokens(ctx: Context<'_, '_, 'c, 'info, Self>, amount: u64) -> Result<()> { let accs = &ctx.accounts; diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index bdb043aa..1c3c3a14 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -59,31 +59,31 @@ pub mod conditional_vault { } pub fn split_tokens<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, InteractWithNewVault<'info>>, + ctx: Context<'_, '_, 'c, 'info, InteractWithVault<'info>>, amount: u64, ) -> Result<()> { - InteractWithNewVault::handle_split_tokens(ctx, amount) + InteractWithVault::handle_split_tokens(ctx, amount) } pub fn merge_tokens<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, InteractWithNewVault<'info>>, + ctx: Context<'_, '_, 'c, 'info, InteractWithVault<'info>>, amount: u64, ) -> Result<()> { - InteractWithNewVault::handle_merge_tokens(ctx, amount) + InteractWithVault::handle_merge_tokens(ctx, amount) } #[access_control(ctx.accounts.validate_redeem_tokens())] pub fn redeem_tokens<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, InteractWithNewVault<'info>>, + ctx: Context<'_, '_, 'c, 'info, InteractWithVault<'info>>, ) -> Result<()> { - InteractWithNewVault::handle_redeem_tokens(ctx) + InteractWithVault::handle_redeem_tokens(ctx) } - // #[access_control(ctx.accounts.validate())] - // pub fn add_metadata_to_conditional_tokens( - // ctx: Context, - // args: AddMetadataToConditionalTokensArgs, - // ) -> Result<()> { - // AddMetadataToConditionalTokens::handle(ctx, args) - // } + #[access_control(ctx.accounts.validate())] + pub fn add_metadata_to_conditional_tokens( + ctx: Context, + args: AddMetadataToConditionalTokensArgs, + ) -> Result<()> { + AddMetadataToConditionalTokens::handle(ctx, args) + } } diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index 50d50f5e..c8ef7cdd 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -420,48 +420,48 @@ export class ConditionalVaultClient { return ix; } - // addMetadataToConditionalTokensIx( - // vault: PublicKey, - // underlyingTokenMint: PublicKey, - // proposalNumber: number, - // onFinalizeUri: string, - // onRevertUri: string - // ) { - // const [underlyingTokenMetadata] = getMetadataAddr(underlyingTokenMint); - - // const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( - // this.vaultProgram.programId, - // vault - // ); - // const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( - // this.vaultProgram.programId, - // vault - // ); - - // const [conditionalOnFinalizeTokenMetadata] = getMetadataAddr( - // conditionalOnFinalizeTokenMint - // ); - - // const [conditionalOnRevertTokenMetadata] = getMetadataAddr( - // conditionalOnRevertTokenMint - // ); - - // return this.vaultProgram.methods - // .addMetadataToConditionalTokens({ - // proposalNumber: new BN(proposalNumber), - // onFinalizeUri, - // onRevertUri, - // }) - // .accounts({ - // payer: this.provider.publicKey, - // vault, - // underlyingTokenMint, - // underlyingTokenMetadata, - // conditionalOnFinalizeTokenMint, - // conditionalOnRevertTokenMint, - // conditionalOnFinalizeTokenMetadata, - // conditionalOnRevertTokenMetadata, - // tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, - // }); - // } + addMetadataToConditionalTokensIx( + vault: PublicKey, + underlyingTokenMint: PublicKey, + proposalNumber: number, + onFinalizeUri: string, + onRevertUri: string + ) { + const [underlyingTokenMetadata] = getMetadataAddr(underlyingTokenMint); + + const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( + this.vaultProgram.programId, + vault + ); + const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + this.vaultProgram.programId, + vault + ); + + const [conditionalOnFinalizeTokenMetadata] = getMetadataAddr( + conditionalOnFinalizeTokenMint + ); + + const [conditionalOnRevertTokenMetadata] = getMetadataAddr( + conditionalOnRevertTokenMint + ); + + return this.vaultProgram.methods + .addMetadataToConditionalTokens({ + proposalNumber: new BN(proposalNumber), + onFinalizeUri, + onRevertUri, + }) + .accounts({ + payer: this.provider.publicKey, + vault, + underlyingTokenMint, + underlyingTokenMetadata, + conditionalOnFinalizeTokenMint, + conditionalOnRevertTokenMint, + conditionalOnFinalizeTokenMetadata, + conditionalOnRevertTokenMetadata, + tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, + }); + } } diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index 2a3a3a71..a0e38449 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -216,6 +216,54 @@ export type ConditionalVault = { } ]; args: []; + }, + { + name: "addMetadataToConditionalTokens"; + accounts: [ + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "vault"; + isMut: true; + isSigner: false; + }, + { + name: "conditionalTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "conditionalTokenMetadata"; + isMut: true; + isSigner: false; + }, + { + name: "tokenMetadataProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "rent"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "args"; + type: { + defined: "AddMetadataToConditionalTokensArgs"; + }; + } + ]; } ]; accounts: [ @@ -305,6 +353,26 @@ export type ConditionalVault = { } ]; types: [ + { + name: "AddMetadataToConditionalTokensArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "name"; + type: "string"; + }, + { + name: "symbol"; + type: "string"; + }, + { + name: "image"; + type: "string"; + } + ]; + }; + }, { name: "InitializeQuestionArgs"; type: { @@ -652,6 +720,54 @@ export const IDL: ConditionalVault = { ], args: [], }, + { + name: "addMetadataToConditionalTokens", + accounts: [ + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "vault", + isMut: true, + isSigner: false, + }, + { + name: "conditionalTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "conditionalTokenMetadata", + isMut: true, + isSigner: false, + }, + { + name: "tokenMetadataProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "rent", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "AddMetadataToConditionalTokensArgs", + }, + }, + ], + }, ], accounts: [ { @@ -740,6 +856,26 @@ export const IDL: ConditionalVault = { }, ], types: [ + { + name: "AddMetadataToConditionalTokensArgs", + type: { + kind: "struct", + fields: [ + { + name: "name", + type: "string", + }, + { + name: "symbol", + type: "string", + }, + { + name: "image", + type: "string", + }, + ], + }, + }, { name: "InitializeQuestionArgs", type: { diff --git a/tests/main.test.ts b/tests/main.test.ts index 9079fe9b..80862dfe 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -20,19 +20,29 @@ import { } from "spl-token-bankrun"; import * as token from "@solana/spl-token"; import { assert } from "chai"; +import { + MPL_TOKEN_METADATA_PROGRAM_ID as UMI_MPL_TOKEN_METADATA_PROGRAM_ID, +} from "@metaplex-foundation/mpl-token-metadata"; +import { + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; + +const MPL_TOKEN_METADATA_PROGRAM_ID = toWeb3JsPublicKey( + UMI_MPL_TOKEN_METADATA_PROGRAM_ID +); before(async function () { this.context = await startAnchor( "./", - [], - // [ - // // even though the program is loaded into the test validator, we need - // // to tell banks test client to load it as well - // { - // name: "mpl_token_metadata", - // programId: MPL_TOKEN_METADATA_PROGRAM_ID, - // }, - // ], + // [], + [ + // even though the program is loaded into the test validator, we need + // to tell banks test client to load it as well + { + name: "mpl_token_metadata", + programId: MPL_TOKEN_METADATA_PROGRAM_ID, + }, + ], [] ); this.banksClient = this.context.banksClient; From d0bf35fb93860facb8b1ccaea1723696dd1e87ad Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Mon, 26 Aug 2024 00:00:00 +0000 Subject: [PATCH 42/52] Complete the adding of metadata --- programs/conditional_vault/src/error.rs | 2 + .../add_metadata_to_conditional_tokens.rs | 7 +- programs/conditional_vault/src/lib.rs | 2 +- sdk/src/ConditionalVaultClient.ts | 56 ++++----- sdk/src/types/conditional_vault.ts | 10 ++ .../binaryPredictionMarket.test.ts | 3 + tests/conditionalVault/main.test.ts | 2 + .../addMetadataToConditionalTokens.test.ts | 109 ++++++++++++++++++ 8 files changed, 162 insertions(+), 29 deletions(-) create mode 100644 tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index bf0d04c7..7e5ea5bf 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -30,4 +30,6 @@ pub enum VaultError { ConditionalTokenMintMismatch, #[msg("Payouts must sum to 1 or more")] PayoutZero, + #[msg("Conditional token metadata already set")] + ConditionalTokenMetadataAlreadySet, } diff --git a/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs b/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs index 2e1e7eb3..dd603f23 100644 --- a/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs +++ b/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs @@ -8,7 +8,6 @@ pub mod proph3t_deployer { #[derive(AnchorSerialize, AnchorDeserialize)] pub struct AddMetadataToConditionalTokensArgs { - // pub uri: String, pub name: String, pub symbol: String, pub image: String, @@ -24,7 +23,6 @@ pub struct AddMetadataToConditionalTokens<'info> { #[account( mut, mint::authority = vault, - mint::freeze_authority = vault, )] pub conditional_token_mint: Account<'info, Mint>, /// CHECK: verified via cpi into token metadata @@ -42,6 +40,11 @@ impl AddMetadataToConditionalTokens<'_> { // VaultError::VaultAlreadySettled // ); + require!( + self.conditional_token_metadata.data_is_empty(), + VaultError::ConditionalTokenMetadataAlreadySet + ); + #[cfg(feature = "production")] require_eq!( self.payer.key(), proph3t_deployer::ID diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index 1c3c3a14..839bcb74 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::*; use anchor_spl::metadata::{ create_metadata_accounts_v3, mpl_token_metadata::types::DataV2, CreateMetadataAccountsV3, - Metadata, MetadataAccount, + Metadata, }; use anchor_spl::{ associated_token::AssociatedToken, diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/ConditionalVaultClient.ts index c8ef7cdd..d1464ea6 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/ConditionalVaultClient.ts @@ -422,45 +422,49 @@ export class ConditionalVaultClient { addMetadataToConditionalTokensIx( vault: PublicKey, - underlyingTokenMint: PublicKey, - proposalNumber: number, - onFinalizeUri: string, - onRevertUri: string + index: number, + name: string, + symbol: string, + image: string + // underlyingTokenMint: PublicKey, + // proposalNumber: number, + // onFinalizeUri: string, + // onRevertUri: string ) { - const [underlyingTokenMetadata] = getMetadataAddr(underlyingTokenMint); + // const [underlyingTokenMetadata] = getMetadataAddr(underlyingTokenMint); - const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( - this.vaultProgram.programId, - vault - ); - const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + const [conditionalTokenMint] = getConditionalTokenMintAddr( this.vaultProgram.programId, - vault + vault, + index ); - const [conditionalOnFinalizeTokenMetadata] = getMetadataAddr( - conditionalOnFinalizeTokenMint - ); + // const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( + // this.vaultProgram.programId, + // vault + // ); + // const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + // this.vaultProgram.programId, + // vault + // ); - const [conditionalOnRevertTokenMetadata] = getMetadataAddr( - conditionalOnRevertTokenMint - ); + const [conditionalTokenMetadata] = getMetadataAddr(conditionalTokenMint); + + // const [conditionalOnRevertTokenMetadata] = getMetadataAddr( + // conditionalOnRevertTokenMint + // ); return this.vaultProgram.methods .addMetadataToConditionalTokens({ - proposalNumber: new BN(proposalNumber), - onFinalizeUri, - onRevertUri, + name, + symbol, + image, }) .accounts({ payer: this.provider.publicKey, vault, - underlyingTokenMint, - underlyingTokenMetadata, - conditionalOnFinalizeTokenMint, - conditionalOnRevertTokenMint, - conditionalOnFinalizeTokenMetadata, - conditionalOnRevertTokenMetadata, + conditionalTokenMint, + conditionalTokenMetadata, tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, }); } diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index a0e38449..fd19a968 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -497,6 +497,11 @@ export type ConditionalVault = { code: 6013; name: "PayoutZero"; msg: "Payouts must sum to 1 or more"; + }, + { + code: 6014; + name: "ConditionalTokenMetadataAlreadySet"; + msg: "Conditional token metadata already set"; } ]; }; @@ -1001,5 +1006,10 @@ export const IDL: ConditionalVault = { name: "PayoutZero", msg: "Payouts must sum to 1 or more", }, + { + code: 6014, + name: "ConditionalTokenMetadataAlreadySet", + msg: "Conditional token metadata already set", + }, ], }; diff --git a/tests/conditionalVault/integration/binaryPredictionMarket.test.ts b/tests/conditionalVault/integration/binaryPredictionMarket.test.ts index 48be6f42..985adc18 100644 --- a/tests/conditionalVault/integration/binaryPredictionMarket.test.ts +++ b/tests/conditionalVault/integration/binaryPredictionMarket.test.ts @@ -61,6 +61,9 @@ export default async function test() { const vault = await vaultClient.initializeVault(question, USDC, 2); const storedVault = await vaultClient.fetchVault(vault); + await vaultClient.addMetadataToConditionalTokensIx(vault, 0, "Trump Share", "TRUMP", "https://example.com/trump.png").rpc(); + await vaultClient.addMetadataToConditionalTokensIx(vault, 1, "Harris Share", "HARRIS", "https://example.com/harris.png").rpc(); + await vaultClient .splitTokensIx(question, vault, USDC, new BN(100), 2, alice.publicKey) .signers([alice]) diff --git a/tests/conditionalVault/main.test.ts b/tests/conditionalVault/main.test.ts index 67cc857a..10240843 100644 --- a/tests/conditionalVault/main.test.ts +++ b/tests/conditionalVault/main.test.ts @@ -4,6 +4,7 @@ import resolveQuestion from "./unit/resolveQuestion.test"; import splitTokens from "./unit/splitTokens.test"; import mergeTokens from "./unit/mergeTokens.test"; import redeemTokens from "./unit/redeemTokens.test"; +import addMetadataToConditionalTokens from "./unit/addMetadataToConditionalTokens.test"; import binaryPredictionMarket from "./integration/binaryPredictionMarket.test"; import scalarGrantMarket from "./integration/scalarGrantMarket.test"; @@ -16,4 +17,5 @@ export default function suite() { describe("#split_tokens", splitTokens); describe("#merge_tokens", mergeTokens); describe("#redeem_tokens", redeemTokens); + describe("#add_metadata_to_conditional_tokens", addMetadataToConditionalTokens); } diff --git a/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts b/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts new file mode 100644 index 00000000..4730fb83 --- /dev/null +++ b/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts @@ -0,0 +1,109 @@ +import { sha256, ConditionalVaultClient, getConditionalTokenMintAddr, getMetadataAddr } from "@metadaoproject/futarchy"; +import { Keypair, PublicKey } from "@solana/web3.js"; +import { assert } from "chai"; +import { createMint } from "spl-token-bankrun"; +import * as anchor from "@coral-xyz/anchor"; +import { expectError } from "../../utils"; +import { Metadata, deserializeMetadata, getMetadataAccountDataSerializer } from "@metaplex-foundation/mpl-token-metadata"; + +export default function suite() { + let vaultClient: ConditionalVaultClient; + let question: PublicKey; + let vault: PublicKey; + let underlyingTokenMint: PublicKey; + + const metadataSerializer = getMetadataAccountDataSerializer(); + + before(function () { + vaultClient = this.vaultClient; + }); + + async function setupVault(outcomes: number) { + let questionId = sha256(new Uint8Array([1, 2, 3])); + let oracle = Keypair.generate(); + + question = await vaultClient.initializeQuestion( + questionId, + oracle.publicKey, + outcomes + ); + + underlyingTokenMint = await createMint( + this.banksClient, + this.payer, + this.payer.publicKey, + null, + 8 + ); + + vault = await vaultClient.initializeVault(question, underlyingTokenMint, outcomes); + } + + async function addMetadataToConditionalTokens(outcomes: number) { + for (let i = 0; i < outcomes; i++) { + await vaultClient.addMetadataToConditionalTokensIx( + vault, + i, + `Outcome ${i}`, + `OUT${i}`, + `https://example.com/image${i}.png` + ).rpc(); + } + } + + async function verifyMetadata(outcomes: number) { + for (let i = 0; i < outcomes; i++) { + const [conditionalTokenMint] = getConditionalTokenMintAddr( + vaultClient.vaultProgram.programId, + vault, + i + ); + + const storedMetadata = await this.banksClient.getAccount(getMetadataAddr(conditionalTokenMint)[0]); + assert.isNotNull(storedMetadata); + const metadata = metadataSerializer.deserialize(storedMetadata.data)[0]; + assert.equal(metadata.name, `Outcome ${i}`); + assert.equal(metadata.symbol, `OUT${i}`); + const expectedUri = `data:,{"name":"${metadata.name}","symbol":"${metadata.symbol}","image":"https://example.com/image${i}.png"}`; + assert.equal(metadata.uri, expectedUri); + } + } + + it("adds metadata to 2-token vault", async function () { + await setupVault.call(this, 2); + await addMetadataToConditionalTokens(2); + await verifyMetadata.call(this, 2); + }); + + it("adds metadata to 3-token vault", async function () { + await setupVault.call(this, 3); + await addMetadataToConditionalTokens(3); + await verifyMetadata.call(this, 3); + }); + + it("adds metadata to 10-token vault", async function () { + await setupVault.call(this, 10); + await addMetadataToConditionalTokens(10); + await verifyMetadata.call(this, 10); + }); + + it("cannot add metadata twice for the same conditional token", async function () { + await setupVault.call(this, 2); + await addMetadataToConditionalTokens(2); + + const callbacks = expectError( + "ConditionalTokenMetadataAlreadySet", + "added metadata to a conditional token that already had metadata" + ); + + await vaultClient.addMetadataToConditionalTokensIx( + vault, + 0, + "New Outcome", + "NEW", + "https://example.com/new.png" + ) + .rpc() + .then(callbacks[0], callbacks[1]); + }); +} \ No newline at end of file From 5eaa0a9cfbf068b09eee661fea2dff0fc4880e22 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Mon, 26 Aug 2024 00:00:00 +0000 Subject: [PATCH 43/52] v0.3.0-alpha.16 --- sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/package.json b/sdk/package.json index 2afba20e..6ed79c12 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@metadaoproject/futarchy", - "version": "0.3.0-alpha.15", + "version": "0.3.0-alpha.16", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ From 64f1e780bfca9ae6d936cb54b0a9c8855a9885b8 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Mon, 26 Aug 2024 00:00:00 +0000 Subject: [PATCH 44/52] Update sdk version in deps --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 290a130d..c70d9216 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ }, "dependencies": { "@coral-xyz/anchor": "0.29.0", - "@metadaoproject/futarchy": "0.3.0-alpha.15", + "@metadaoproject/futarchy": "0.3.0-alpha.16", "@metaplex-foundation/mpl-token-metadata": "^3.2.0", "@metaplex-foundation/umi": "^0.9.1", "@metaplex-foundation/umi-bundle-defaults": "^0.9.1", diff --git a/yarn.lock b/yarn.lock index 624988b3..463564e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -629,10 +629,10 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@metadaoproject/futarchy@0.3.0-alpha.15": - version "0.3.0-alpha.15" - resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.15.tgz#48832c6cfd803d42ebfd9545600a0ae24225babf" - integrity sha512-1O/Zvtc9Iyz2UzGP3a5CuftRXRr2a+pmAQAODpNxARlU5qDcdmiKVGRpYF90ief8cRHMKrGvSio48Lhq/DrhNQ== +"@metadaoproject/futarchy@0.3.0-alpha.16": + version "0.3.0-alpha.16" + resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.16.tgz#f1f49e4f9d13976c922a55133627023168624728" + integrity sha512-5YDXUcdrr09KboWdX6E35MXGyHCvEqm7WHdaBo4s1HaFdN2HegddjONuvDbgwaa3e3XwkEzYw7ueSeAH27g06g== dependencies: "@coral-xyz/anchor" "^0.29.0" "@noble/hashes" "^1.4.0" From 4304a04b9aa9e9c3f60e5de62bd3968f0d77f107 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Mon, 26 Aug 2024 00:00:00 +0000 Subject: [PATCH 45/52] Test all conditional vault failure cases --- programs/conditional_vault/src/error.rs | 2 - .../src/instructions/common.rs | 1 + sdk/src/types/conditional_vault.ts | 42 ++++++-------- .../unit/initializeQuestion.test.ts | 13 +++++ .../conditionalVault/unit/mergeTokens.test.ts | 13 +++++ .../unit/resolveQuestion.test.ts | 25 +++++++++ .../conditionalVault/unit/splitTokens.test.ts | 55 +++++++++++++++++++ 7 files changed, 123 insertions(+), 28 deletions(-) diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index 7e5ea5bf..a7dab727 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -12,8 +12,6 @@ pub enum VaultError { InvalidConditionalTokenMint, #[msg("Question needs to be resolved before users can redeem conditional tokens for underlying tokens")] CantRedeemConditionalTokens, - #[msg("Once a vault has been settled, its status as either finalized or reverted cannot be changed")] - VaultAlreadySettled, #[msg("Questions need 2 or more conditions")] InsufficientNumConditions, #[msg("Invalid number of payout numerators")] diff --git a/programs/conditional_vault/src/instructions/common.rs b/programs/conditional_vault/src/instructions/common.rs index af31b9e9..f6b71c78 100644 --- a/programs/conditional_vault/src/instructions/common.rs +++ b/programs/conditional_vault/src/instructions/common.rs @@ -46,6 +46,7 @@ impl<'info, 'c: 'info> InteractWithVault<'info> { VaultError::ConditionalMintMismatch ); + // really, this should never fail because we initialize mints when we initialize the vault conditional_token_mints.push( Account::::try_from(conditional_token_mint) .or(Err(VaultError::BadConditionalMint))?, diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/types/conditional_vault.ts index fd19a968..7bdf16f6 100644 --- a/sdk/src/types/conditional_vault.ts +++ b/sdk/src/types/conditional_vault.ts @@ -455,51 +455,46 @@ export type ConditionalVault = { }, { code: 6005; - name: "VaultAlreadySettled"; - msg: "Once a vault has been settled, its status as either finalized or reverted cannot be changed"; - }, - { - code: 6006; name: "InsufficientNumConditions"; msg: "Questions need 2 or more conditions"; }, { - code: 6007; + code: 6006; name: "InvalidNumPayoutNumerators"; msg: "Invalid number of payout numerators"; }, { - code: 6008; + code: 6007; name: "InvalidConditionals"; msg: "Client needs to pass in the list of conditional mints for a vault followed by the user's token accounts for those tokens"; }, { - code: 6009; + code: 6008; name: "ConditionalMintMismatch"; msg: "Conditional mint not in vault"; }, { - code: 6010; + code: 6009; name: "BadConditionalMint"; msg: "Unable to deserialize a conditional token mint"; }, { - code: 6011; + code: 6010; name: "BadConditionalTokenAccount"; msg: "Unable to deserialize a conditional token account"; }, { - code: 6012; + code: 6011; name: "ConditionalTokenMintMismatch"; msg: "User conditional token account mint does not match conditional mint"; }, { - code: 6013; + code: 6012; name: "PayoutZero"; msg: "Payouts must sum to 1 or more"; }, { - code: 6014; + code: 6013; name: "ConditionalTokenMetadataAlreadySet"; msg: "Conditional token metadata already set"; } @@ -963,51 +958,46 @@ export const IDL: ConditionalVault = { }, { code: 6005, - name: "VaultAlreadySettled", - msg: "Once a vault has been settled, its status as either finalized or reverted cannot be changed", - }, - { - code: 6006, name: "InsufficientNumConditions", msg: "Questions need 2 or more conditions", }, { - code: 6007, + code: 6006, name: "InvalidNumPayoutNumerators", msg: "Invalid number of payout numerators", }, { - code: 6008, + code: 6007, name: "InvalidConditionals", msg: "Client needs to pass in the list of conditional mints for a vault followed by the user's token accounts for those tokens", }, { - code: 6009, + code: 6008, name: "ConditionalMintMismatch", msg: "Conditional mint not in vault", }, { - code: 6010, + code: 6009, name: "BadConditionalMint", msg: "Unable to deserialize a conditional token mint", }, { - code: 6011, + code: 6010, name: "BadConditionalTokenAccount", msg: "Unable to deserialize a conditional token account", }, { - code: 6012, + code: 6011, name: "ConditionalTokenMintMismatch", msg: "User conditional token account mint does not match conditional mint", }, { - code: 6013, + code: 6012, name: "PayoutZero", msg: "Payouts must sum to 1 or more", }, { - code: 6014, + code: 6013, name: "ConditionalTokenMetadataAlreadySet", msg: "Conditional token metadata already set", }, diff --git a/tests/conditionalVault/unit/initializeQuestion.test.ts b/tests/conditionalVault/unit/initializeQuestion.test.ts index fb4c037b..f107306d 100644 --- a/tests/conditionalVault/unit/initializeQuestion.test.ts +++ b/tests/conditionalVault/unit/initializeQuestion.test.ts @@ -5,6 +5,7 @@ import { } from "@metadaoproject/futarchy"; import { Keypair } from "@solana/web3.js"; import { assert } from "chai"; +import { expectError } from "../../utils"; export default function suite() { let vaultClient: ConditionalVaultClient; @@ -34,4 +35,16 @@ export default function suite() { assert.deepEqual(storedQuestion.payoutNumerators, [0, 0]); assert.equal(storedQuestion.payoutDenominator, 0); }); + + it("throws error when initializing a question with insufficient conditions", async function () { + const callbacks = expectError( + "InsufficientNumConditions", + "question initialization succeeded despite insufficient conditions" + ); + + await vaultClient + .initializeQuestionIx(sha256(new Uint8Array([4, 5, 6])), Keypair.generate().publicKey, 1) + .rpc() + .then(callbacks[0], callbacks[1]); + }); } diff --git a/tests/conditionalVault/unit/mergeTokens.test.ts b/tests/conditionalVault/unit/mergeTokens.test.ts index b34d9e29..507ccaf5 100644 --- a/tests/conditionalVault/unit/mergeTokens.test.ts +++ b/tests/conditionalVault/unit/mergeTokens.test.ts @@ -9,6 +9,7 @@ import { } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; +import { expectError } from "../../utils"; export default function suite() { let vaultClient: ConditionalVaultClient; @@ -96,4 +97,16 @@ export default function suite() { assert.isTrue(balanceAfter > balanceBefore); assert.equal(balanceAfter - balanceBefore, 600); }); + + it("throws error when trying to merge more tokens than available", async function () { + const callbacks = expectError( + "InsufficientConditionalTokens", + "merge succeeded despite insufficient conditional tokens" + ); + + await vaultClient + .mergeTokensIx(question, vault, underlyingTokenMint, new anchor.BN(2000), 2) + .rpc() + .then(callbacks[0], callbacks[1]); + }); } diff --git a/tests/conditionalVault/unit/resolveQuestion.test.ts b/tests/conditionalVault/unit/resolveQuestion.test.ts index e9a135da..1c15cd79 100644 --- a/tests/conditionalVault/unit/resolveQuestion.test.ts +++ b/tests/conditionalVault/unit/resolveQuestion.test.ts @@ -1,6 +1,7 @@ import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; +import { expectError } from "../../utils"; export default function suite() { let vaultClient: ConditionalVaultClient; @@ -36,4 +37,28 @@ export default function suite() { assert.deepEqual(storedQuestion.payoutNumerators, [1, 0]); assert.equal(storedQuestion.payoutDenominator, 1); }); + + it("throws error when resolving a question with invalid number of payout numerators", async function () { + const callbacks = expectError( + "InvalidNumPayoutNumerators", + "question resolution succeeded despite invalid number of payout numerators" + ); + + await vaultClient + .resolveQuestionIx(question, settlementAuthority, [1, 0, 1]) + .rpc() + .then(callbacks[0], callbacks[1]); + }); + + it("throws error when resolving a question with zero payout", async function () { + const callbacks = expectError( + "PayoutZero", + "question resolution succeeded despite zero payout" + ); + + await vaultClient + .resolveQuestionIx(question, settlementAuthority, [0, 0]) + .rpc() + .then(callbacks[0], callbacks[1]); + }); } diff --git a/tests/conditionalVault/unit/splitTokens.test.ts b/tests/conditionalVault/unit/splitTokens.test.ts index bf6e8170..078b92fb 100644 --- a/tests/conditionalVault/unit/splitTokens.test.ts +++ b/tests/conditionalVault/unit/splitTokens.test.ts @@ -151,4 +151,59 @@ export default function suite() { .rpc() .then(callbacks[0], callbacks[1]); }); + + it("throws error when using an invalid vault underlying token account", async function () { + const fakeUnderlyingTokenMint = await this.createMint(vault, 8); + const invalidVaultUnderlyingTokenAccount = await this.createTokenAccount( + fakeUnderlyingTokenMint, + vault + ); + + const callbacks = expectError( + "InvalidVaultUnderlyingTokenAccount", + "split succeeded despite invalid vault underlying token account" + ); + + await vaultClient + .splitTokensIx(question, vault, underlyingTokenMint, new anchor.BN(1000), 2) + .accounts({ + vaultUnderlyingTokenAccount: invalidVaultUnderlyingTokenAccount, + }) + .rpc() + .then(callbacks[0], callbacks[1]); + }); + + it("throws error when providing invalid number of conditional accounts", async function () { + const callbacks = expectError( + "InvalidConditionals", + "split succeeded despite invalid number of conditional accounts" + ); + + const { remainingAccounts } = vaultClient.getConditionalTokenAccountsAndInstructions( + vault, + 1, // Incorrect number of outcomes + this.payer.publicKey + ); + + await vaultClient.vaultProgram.methods + .splitTokens(new anchor.BN(1000)) + .accounts({ + question, + authority: this.payer.publicKey, + vault, + vaultUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: token.getAssociatedTokenAddressSync( + underlyingTokenMint, + this.payer.publicKey, + true + ), + }) + .remainingAccounts(remainingAccounts) + .rpc() + .then(callbacks[0], callbacks[1]); + }); } From 6a49ff72a9bffb8a92ca954447771126cbeed790 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Wed, 28 Aug 2024 00:00:00 +0000 Subject: [PATCH 46/52] First stab at separating the SDK into verions --- Anchor.toml | 2 +- package.json | 1 + sdk/package.json | 12 +- sdk/src/InstructionHandler.ts | 149 -- sdk/src/index.ts | 26 +- sdk/src/{ => v0.3}/AmmClient.ts | 0 sdk/src/v0.3/AutocratClient.ts | 724 ++++++++++ sdk/src/v0.3/ConditionalVaultClient.ts | 351 +++++ sdk/src/{ => v0.3}/constants.ts | 0 sdk/src/v0.3/index.ts | 6 + sdk/src/{ => v0.3}/types/amm.ts | 0 sdk/src/v0.3/types/autocrat.ts | 1263 +++++++++++++++++ sdk/src/{ => v0.3}/types/autocrat_migrator.ts | 0 sdk/src/v0.3/types/conditional_vault.ts | 895 ++++++++++++ sdk/src/{ => v0.3}/types/index.ts | 0 .../{ => v0.3}/types/optimistic_timelock.ts | 0 sdk/src/{ => v0.3}/types/utils.ts | 0 sdk/src/v0.3/utils/cu.ts | 11 + sdk/src/{ => v0.3}/utils/filters.ts | 0 sdk/src/{ => v0.3}/utils/index.ts | 0 sdk/src/{ => v0.3}/utils/instruction.ts | 0 sdk/src/{ => v0.3}/utils/metadata.ts | 0 sdk/src/v0.3/utils/pda.ts | 110 ++ sdk/src/{ => v0.3}/utils/priceMath.ts | 0 sdk/src/v0.4/AmmClient.ts | 526 +++++++ sdk/src/{ => v0.4}/AutocratClient.ts | 21 +- sdk/src/{ => v0.4}/ConditionalVaultClient.ts | 9 +- sdk/src/v0.4/constants.ts | 26 + sdk/src/v0.4/index.ts | 6 + sdk/src/v0.4/types/amm.ts | 1083 ++++++++++++++ sdk/src/{ => v0.4}/types/autocrat.ts | 0 sdk/src/v0.4/types/autocrat_migrator.ts | 237 ++++ sdk/src/{ => v0.4}/types/conditional_vault.ts | 0 sdk/src/v0.4/types/index.ts | 34 + sdk/src/v0.4/types/optimistic_timelock.ts | 1023 +++++++++++++ sdk/src/v0.4/types/utils.ts | 3 + sdk/src/{ => v0.4}/utils/cu.ts | 0 sdk/src/v0.4/utils/filters.ts | 21 + sdk/src/v0.4/utils/index.ts | 40 + sdk/src/v0.4/utils/instruction.ts | 16 + sdk/src/v0.4/utils/metadata.ts | 35 + sdk/src/{ => v0.4}/utils/pda.ts | 2 +- sdk/src/v0.4/utils/priceMath.ts | 85 ++ sdk/tsconfig.json | 8 +- sdk/yarn.lock | 2 +- tests/conditionalVault/main.test.ts | 10 +- .../unit/initializeConditionalVault.test.ts | 4 +- .../unit/initializeQuestion.test.ts | 7 +- tests/main.test.ts | 22 +- tsconfig.json | 3 +- 50 files changed, 6578 insertions(+), 195 deletions(-) delete mode 100644 sdk/src/InstructionHandler.ts rename sdk/src/{ => v0.3}/AmmClient.ts (100%) create mode 100644 sdk/src/v0.3/AutocratClient.ts create mode 100644 sdk/src/v0.3/ConditionalVaultClient.ts rename sdk/src/{ => v0.3}/constants.ts (100%) create mode 100644 sdk/src/v0.3/index.ts rename sdk/src/{ => v0.3}/types/amm.ts (100%) create mode 100644 sdk/src/v0.3/types/autocrat.ts rename sdk/src/{ => v0.3}/types/autocrat_migrator.ts (100%) create mode 100644 sdk/src/v0.3/types/conditional_vault.ts rename sdk/src/{ => v0.3}/types/index.ts (100%) rename sdk/src/{ => v0.3}/types/optimistic_timelock.ts (100%) rename sdk/src/{ => v0.3}/types/utils.ts (100%) create mode 100644 sdk/src/v0.3/utils/cu.ts rename sdk/src/{ => v0.3}/utils/filters.ts (100%) rename sdk/src/{ => v0.3}/utils/index.ts (100%) rename sdk/src/{ => v0.3}/utils/instruction.ts (100%) rename sdk/src/{ => v0.3}/utils/metadata.ts (100%) create mode 100644 sdk/src/v0.3/utils/pda.ts rename sdk/src/{ => v0.3}/utils/priceMath.ts (100%) create mode 100644 sdk/src/v0.4/AmmClient.ts rename sdk/src/{ => v0.4}/AutocratClient.ts (97%) rename sdk/src/{ => v0.4}/ConditionalVaultClient.ts (98%) create mode 100644 sdk/src/v0.4/constants.ts create mode 100644 sdk/src/v0.4/index.ts create mode 100644 sdk/src/v0.4/types/amm.ts rename sdk/src/{ => v0.4}/types/autocrat.ts (100%) create mode 100644 sdk/src/v0.4/types/autocrat_migrator.ts rename sdk/src/{ => v0.4}/types/conditional_vault.ts (100%) create mode 100644 sdk/src/v0.4/types/index.ts create mode 100644 sdk/src/v0.4/types/optimistic_timelock.ts create mode 100644 sdk/src/v0.4/types/utils.ts rename sdk/src/{ => v0.4}/utils/cu.ts (100%) create mode 100644 sdk/src/v0.4/utils/filters.ts create mode 100644 sdk/src/v0.4/utils/index.ts create mode 100644 sdk/src/v0.4/utils/instruction.ts create mode 100644 sdk/src/v0.4/utils/metadata.ts rename sdk/src/{ => v0.4}/utils/pda.ts (98%) create mode 100644 sdk/src/v0.4/utils/priceMath.ts diff --git a/Anchor.toml b/Anchor.toml index 3468eba8..150a4335 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -1,5 +1,5 @@ [scripts] -test = "npx mocha --node-option require=ts-node/register --extension ts -t 10000000 tests/main.test.ts" +test = "npx mocha --import=tsx tests/main.test.ts" propose = "yarn run ts-node scripts/initializeProposal.ts" initialize-dao = "yarn run ts-node scripts/initializeDao.ts" finalize = "yarn run ts-node scripts/finalizeProposal.ts" diff --git a/package.json b/package.json index c70d9216..b45c2a99 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "type": "module", "scripts": { "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" diff --git a/sdk/package.json b/sdk/package.json index 6ed79c12..ae1b2bdf 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,15 +1,21 @@ { "name": "@metadaoproject/futarchy", - "version": "0.3.0-alpha.16", + "version": "0.4.0-alpha.0", + "type": "module", "main": "dist/index.js", + "module": "dist/index.js", "types": "dist/index.d.ts", "files": [ "/dist" ], + "exports": { + ".": "./dist/index.js", + "./v0.4": "./dist/v0.4/index.js" + }, "scripts": { "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check", - "build": "rm -rf ./dist && cp ../target/types/* ./src/types && yarn lint:fix && yarn tsc" + "build": "rm -rf ./dist && cp ../target/types/* ./src/v0.4/types && yarn lint:fix && yarn tsc" }, "dependencies": { "@coral-xyz/anchor": "^0.29.0", @@ -30,6 +36,6 @@ "solana-bankrun": "^0.2.0", "spl-token-bankrun": "0.2.3", "ts-mocha": "^10.0.0", - "typescript": "^4.3.5" + "typescript": "^4.9.5" } } diff --git a/sdk/src/InstructionHandler.ts b/sdk/src/InstructionHandler.ts deleted file mode 100644 index 243d07bf..00000000 --- a/sdk/src/InstructionHandler.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { - AddressLookupTableAccount, - Blockhash, - ConfirmOptions, - Keypair, - Signer, - Transaction, - TransactionInstruction, - TransactionMessage, - VersionedTransaction, -} from "@solana/web3.js"; -import { BanksClient } from "solana-bankrun"; -import { addComputeUnits, addPriorityFee } from "./utils"; -import { AmmClient } from "./AmmClient"; -import { AnchorProvider, Program } from "@coral-xyz/anchor"; - -export type SignerOrKeypair = Signer | Keypair; - -interface Client { - provider: AnchorProvider; - program: ProgramType; - luts: AddressLookupTableAccount[]; -} - -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, - ]; - } - - 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; - } - - 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; - } - } -} diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 3f35a889..0e363be3 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -1,7 +1,21 @@ -export * from "./types"; -export * from "./utils"; -export * from "./constants"; -export * from "./AmmClient"; -export * from "./AutocratClient"; -export * from "./ConditionalVaultClient"; +// export * from "./v0.3"; +// export * from "./v0.4"; + +// import * as v0_3 from "./v0.3"; +// import * as v0_4 from "./v0.4"; + +// export const versions = { +// "0.3": v0_3, +// "0.4": v0_4, +// } as const; + +// export type VersionKey = keyof typeof versions; + +// export function getVersion(version: VersionKey) { +// return versions[version]; +// } + export { sha256 } from "@noble/hashes/sha256"; +// export { v0_3, v0_4 }; + +// export * from "./common/utils"; diff --git a/sdk/src/AmmClient.ts b/sdk/src/v0.3/AmmClient.ts similarity index 100% rename from sdk/src/AmmClient.ts rename to sdk/src/v0.3/AmmClient.ts diff --git a/sdk/src/v0.3/AutocratClient.ts b/sdk/src/v0.3/AutocratClient.ts new file mode 100644 index 00000000..a285330f --- /dev/null +++ b/sdk/src/v0.3/AutocratClient.ts @@ -0,0 +1,724 @@ +import { AnchorProvider, IdlTypes, Program } from "@coral-xyz/anchor"; +import { + AccountMeta, + AddressLookupTableAccount, + ComputeBudgetProgram, + Connection, + Keypair, + PublicKey, + Transaction, + TransactionInstruction, +} from "@solana/web3.js"; +import { PriceMath } from "./utils/priceMath"; +import { ProposalInstruction, InitializeDaoParams } from "./types"; + +import { Autocrat, IDL as AutocratIDL } from "./types/autocrat"; +import { + ConditionalVault, + IDL as ConditionalVaultIDL, +} from "./types/conditional_vault"; + +import BN from "bn.js"; +import { + AMM_PROGRAM_ID, + AUTOCRAT_PROGRAM_ID, + CONDITIONAL_VAULT_PROGRAM_ID, + MAINNET_USDC, + USDC_DECIMALS, +} from "./constants"; +import { + DEFAULT_CU_PRICE, + InstructionUtils, + MaxCUs, + getAmmAddr, + getAmmLpMintAddr, + getDaoTreasuryAddr, + getProposalAddr, + getVaultAddr, + getVaultFinalizeMintAddr, + getVaultRevertMintAddr, +} from "./utils"; +import { ConditionalVaultClient } from "./ConditionalVaultClient"; +import { AmmClient } from "./AmmClient"; +import { + createAssociatedTokenAccountIdempotentInstruction, + getAssociatedTokenAddressSync, + unpackMint, +} from "@solana/spl-token"; + +export type CreateClientParams = { + provider: AnchorProvider; + autocratProgramId?: PublicKey; + conditionalVaultProgramId?: PublicKey; + ammProgramId?: PublicKey; +}; + +export type ProposalVaults = { + baseVault: PublicKey; + quoteVault: PublicKey; +}; + +export class AutocratClient { + public readonly provider: AnchorProvider; + public readonly autocrat: Program; + public readonly vaultClient: ConditionalVaultClient; + public readonly ammClient: AmmClient; + public readonly luts: AddressLookupTableAccount[]; + + constructor( + provider: AnchorProvider, + autocratProgramId: PublicKey, + conditionalVaultProgramId: PublicKey, + ammProgramId: PublicKey, + luts: AddressLookupTableAccount[] + ) { + this.provider = provider; + this.autocrat = new Program( + AutocratIDL, + autocratProgramId, + provider + ); + this.vaultClient = ConditionalVaultClient.createClient({ + provider, + conditionalVaultProgramId, + }); + this.ammClient = AmmClient.createClient({ provider, ammProgramId }); + this.luts = luts; + } + + public static createClient( + createAutocratClientParams: CreateClientParams + ): AutocratClient { + let { + provider, + autocratProgramId, + conditionalVaultProgramId, + ammProgramId, + } = createAutocratClientParams; + + const luts: AddressLookupTableAccount[] = []; + + return new AutocratClient( + provider, + autocratProgramId || AUTOCRAT_PROGRAM_ID, + conditionalVaultProgramId || CONDITIONAL_VAULT_PROGRAM_ID, + ammProgramId || AMM_PROGRAM_ID, + luts + ); + } + + async getProposal(proposal: PublicKey) { + return this.autocrat.account.proposal.fetch(proposal); + } + + async getDao(dao: PublicKey) { + return this.autocrat.account.dao.fetch(dao); + } + + getProposalPdas( + proposal: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + dao: PublicKey + ): { + baseVault: PublicKey; + quoteVault: PublicKey; + passBaseMint: PublicKey; + passQuoteMint: PublicKey; + failBaseMint: PublicKey; + failQuoteMint: PublicKey; + passAmm: PublicKey; + failAmm: PublicKey; + passLp: PublicKey; + failLp: PublicKey; + } { + let vaultProgramId = this.vaultClient.vaultProgram.programId; + const [daoTreasury] = getDaoTreasuryAddr(this.autocrat.programId, dao); + const [baseVault] = getVaultAddr( + this.vaultClient.vaultProgram.programId, + proposal, + baseMint + ); + const [quoteVault] = getVaultAddr( + this.vaultClient.vaultProgram.programId, + proposal, + quoteMint + ); + + const [passBaseMint] = getVaultFinalizeMintAddr(vaultProgramId, baseVault); + const [passQuoteMint] = getVaultFinalizeMintAddr( + vaultProgramId, + quoteVault + ); + + const [failBaseMint] = getVaultRevertMintAddr(vaultProgramId, baseVault); + const [failQuoteMint] = getVaultRevertMintAddr(vaultProgramId, quoteVault); + + const [passAmm] = getAmmAddr( + this.ammClient.program.programId, + passBaseMint, + passQuoteMint + ); + const [failAmm] = getAmmAddr( + this.ammClient.program.programId, + failBaseMint, + failQuoteMint + ); + + const [passLp] = getAmmLpMintAddr( + this.ammClient.program.programId, + passAmm + ); + const [failLp] = getAmmLpMintAddr( + this.ammClient.program.programId, + failAmm + ); + + return { + baseVault, + quoteVault, + passBaseMint, + passQuoteMint, + failBaseMint, + failQuoteMint, + passAmm, + failAmm, + passLp, + failLp, + }; + } + + async initializeDao( + tokenMint: PublicKey, + tokenPriceUiAmount: number, + minBaseFutarchicLiquidity: number, + minQuoteFutarchicLiquidity: number, + usdcMint: PublicKey = MAINNET_USDC, + daoKeypair: Keypair = Keypair.generate() + ): Promise { + let tokenDecimals = unpackMint( + tokenMint, + await this.provider.connection.getAccountInfo(tokenMint) + ).decimals; + + let scaledPrice = PriceMath.getAmmPrice( + tokenPriceUiAmount, + tokenDecimals, + USDC_DECIMALS + ); + + console.log( + PriceMath.getHumanPrice(scaledPrice, tokenDecimals, USDC_DECIMALS) + ); + + await this.initializeDaoIx( + daoKeypair, + tokenMint, + { + twapInitialObservation: scaledPrice, + twapMaxObservationChangePerUpdate: scaledPrice.divn(50), + minQuoteFutarchicLiquidity: new BN(minQuoteFutarchicLiquidity).mul( + new BN(10).pow(new BN(USDC_DECIMALS)) + ), + minBaseFutarchicLiquidity: new BN(minBaseFutarchicLiquidity).mul( + new BN(10).pow(new BN(tokenDecimals)) + ), + passThresholdBps: null, + slotsPerProposal: null, + }, + usdcMint + ) + .postInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ + units: MaxCUs.initializeDao, + }), + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: DEFAULT_CU_PRICE, + }), + ]) + .rpc({ maxRetries: 5 }); + + return daoKeypair.publicKey; + } + + initializeDaoIx( + daoKeypair: Keypair, + tokenMint: PublicKey, + params: InitializeDaoParams, + usdcMint: PublicKey = MAINNET_USDC + ) { + return this.autocrat.methods + .initializeDao(params) + .accounts({ + dao: daoKeypair.publicKey, + tokenMint, + usdcMint, + }) + .signers([daoKeypair]); + } + + async initializeProposal( + dao: PublicKey, + descriptionUrl: string, + instruction: ProposalInstruction, + baseTokensToLP: BN, + quoteTokensToLP: BN + ): Promise { + const storedDao = await this.getDao(dao); + + const nonce = new BN(Math.random() * 2 ** 50); + + let [proposal] = getProposalAddr( + this.autocrat.programId, + this.provider.publicKey, + nonce + ); + + const { + baseVault, + quoteVault, + passAmm, + failAmm, + passBaseMint, + passQuoteMint, + failBaseMint, + failQuoteMint, + } = this.getProposalPdas( + proposal, + storedDao.tokenMint, + storedDao.usdcMint, + dao + ); + + // it's important that these happen in a single atomic transaction + await this.vaultClient + .initializeVaultIx(proposal, storedDao.tokenMint) + .postInstructions( + await InstructionUtils.getInstructions( + this.vaultClient.initializeVaultIx(proposal, storedDao.usdcMint), + this.ammClient.createAmmIx( + passBaseMint, + passQuoteMint, + storedDao.twapInitialObservation, + storedDao.twapMaxObservationChangePerUpdate + ), + this.ammClient.createAmmIx( + failBaseMint, + failQuoteMint, + storedDao.twapInitialObservation, + storedDao.twapMaxObservationChangePerUpdate + ) + ) + ) + .rpc(); + + await this.vaultClient + .mintConditionalTokensIx(baseVault, storedDao.tokenMint, baseTokensToLP) + .postInstructions( + await InstructionUtils.getInstructions( + this.vaultClient.mintConditionalTokensIx( + quoteVault, + storedDao.usdcMint, + quoteTokensToLP + ) + ) + ) + .rpc(); + + await this.ammClient + .addLiquidityIx( + passAmm, + passBaseMint, + passQuoteMint, + quoteTokensToLP, + baseTokensToLP, + new BN(0) + ) + .postInstructions( + await InstructionUtils.getInstructions( + this.ammClient.addLiquidityIx( + failAmm, + failBaseMint, + failQuoteMint, + quoteTokensToLP, + baseTokensToLP, + new BN(0) + ) + ) + ) + .rpc(); + + // this is how many original tokens are created + const lpTokens = quoteTokensToLP; + + await this.initializeProposalIx( + descriptionUrl, + instruction, + dao, + storedDao.tokenMint, + storedDao.usdcMint, + lpTokens, + lpTokens, + nonce + ).rpc(); + + return proposal; + } + + async createProposalTxAndPDAs( + dao: PublicKey, + descriptionUrl: string, + instruction: ProposalInstruction, + baseTokensToLP: BN, + quoteTokensToLP: BN + ): Promise< + [ + Transaction[], + { + proposalAcct: PublicKey; + baseCondVaultAcct: PublicKey; + quoteCondVaultAcct: PublicKey; + passMarketAcct: PublicKey; + failMarketAcct: PublicKey; + } + ] + > { + const storedDao = await this.getDao(dao); + + const nonce = new BN(Math.random() * 2 ** 50); + + let [proposal] = getProposalAddr( + this.autocrat.programId, + this.provider.publicKey, + nonce + ); + + const { + baseVault, + quoteVault, + passAmm, + failAmm, + passBaseMint, + passQuoteMint, + failBaseMint, + failQuoteMint, + } = this.getProposalPdas( + proposal, + storedDao.tokenMint, + storedDao.usdcMint, + dao + ); + + // it's important that these happen in a single atomic transaction + const initVaultTx = await this.vaultClient + .initializeVaultIx(proposal, storedDao.tokenMint) + .postInstructions( + await InstructionUtils.getInstructions( + this.vaultClient.initializeVaultIx(proposal, storedDao.usdcMint), + this.ammClient.createAmmIx( + passBaseMint, + passQuoteMint, + storedDao.twapInitialObservation, + storedDao.twapMaxObservationChangePerUpdate + ), + this.ammClient.createAmmIx( + failBaseMint, + failQuoteMint, + storedDao.twapInitialObservation, + storedDao.twapMaxObservationChangePerUpdate + ) + ) + ) + .transaction(); + + const mintConditionalTokensTx = await this.vaultClient + .mintConditionalTokensIx(baseVault, storedDao.tokenMint, baseTokensToLP) + .postInstructions( + await InstructionUtils.getInstructions( + this.vaultClient.mintConditionalTokensIx( + quoteVault, + storedDao.usdcMint, + quoteTokensToLP + ) + ) + ) + .transaction(); + + const addLiquidityTx = await this.ammClient + .addLiquidityIx( + passAmm, + passBaseMint, + passQuoteMint, + quoteTokensToLP, + baseTokensToLP, + new BN(0) + ) + .postInstructions( + await InstructionUtils.getInstructions( + this.ammClient.addLiquidityIx( + failAmm, + failBaseMint, + failQuoteMint, + quoteTokensToLP, + baseTokensToLP, + new BN(0) + ) + ) + ) + .transaction(); + + // this is how many original tokens are created + const lpTokens = quoteTokensToLP; + + const initTx = await this.initializeProposalIx( + descriptionUrl, + instruction, + dao, + storedDao.tokenMint, + storedDao.usdcMint, + lpTokens, + lpTokens, + nonce + ).transaction(); + + return [ + [initVaultTx, mintConditionalTokensTx, addLiquidityTx, initTx], + { + baseCondVaultAcct: baseVault, + quoteCondVaultAcct: quoteVault, + failMarketAcct: failAmm, + passMarketAcct: passAmm, + proposalAcct: proposal, + }, + ]; + } + + initializeProposalIx( + descriptionUrl: string, + instruction: ProposalInstruction, + dao: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + passLpTokensToLock: BN, + failLpTokensToLock: BN, + nonce: BN + ) { + let [proposal] = getProposalAddr( + this.autocrat.programId, + this.provider.publicKey, + nonce + ); + const [daoTreasury] = getDaoTreasuryAddr(this.autocrat.programId, dao); + const { baseVault, quoteVault, passAmm, failAmm } = this.getProposalPdas( + proposal, + baseMint, + quoteMint, + dao + ); + + const [passLp] = getAmmLpMintAddr( + this.ammClient.program.programId, + passAmm + ); + const [failLp] = getAmmLpMintAddr( + this.ammClient.program.programId, + failAmm + ); + + const passLpVaultAccount = getAssociatedTokenAddressSync( + passLp, + daoTreasury, + true + ); + const failLpVaultAccount = getAssociatedTokenAddressSync( + failLp, + daoTreasury, + true + ); + + return this.autocrat.methods + .initializeProposal({ + descriptionUrl, + instruction, + passLpTokensToLock, + failLpTokensToLock, + nonce, + }) + .accounts({ + proposal, + dao, + baseVault, + quoteVault, + passAmm, + failAmm, + passLpMint: passLp, + failLpMint: failLp, + passLpUserAccount: getAssociatedTokenAddressSync( + passLp, + this.provider.publicKey + ), + failLpUserAccount: getAssociatedTokenAddressSync( + failLp, + this.provider.publicKey + ), + passLpVaultAccount, + failLpVaultAccount, + proposer: this.provider.publicKey, + }) + .preInstructions([ + createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + passLpVaultAccount, + daoTreasury, + passLp + ), + createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + failLpVaultAccount, + daoTreasury, + failLp + ), + ]); + } + + async finalizeProposal(proposal: PublicKey) { + let storedProposal = await this.getProposal(proposal); + let storedDao = await this.getDao(storedProposal.dao); + + return this.finalizeProposalIx( + proposal, + storedProposal.instruction, + storedProposal.dao, + storedDao.tokenMint, + storedDao.usdcMint, + storedProposal.proposer + ).rpc(); + } + + finalizeProposalIx( + proposal: PublicKey, + instruction: any, + dao: PublicKey, + daoToken: PublicKey, + usdc: PublicKey, + proposer: PublicKey + ) { + let vaultProgramId = this.vaultClient.vaultProgram.programId; + + const [daoTreasury] = getDaoTreasuryAddr(this.autocrat.programId, dao); + const { baseVault, quoteVault, passAmm, failAmm } = this.getProposalPdas( + proposal, + daoToken, + usdc, + dao + ); + + const [passLp] = getAmmLpMintAddr( + this.ammClient.program.programId, + passAmm + ); + const [failLp] = getAmmLpMintAddr( + this.ammClient.program.programId, + failAmm + ); + + return this.autocrat.methods.finalizeProposal().accounts({ + proposal, + passAmm, + failAmm, + dao, + baseVault, + quoteVault, + passLpUserAccount: getAssociatedTokenAddressSync(passLp, proposer), + failLpUserAccount: getAssociatedTokenAddressSync(failLp, proposer), + passLpVaultAccount: getAssociatedTokenAddressSync( + passLp, + daoTreasury, + true + ), + failLpVaultAccount: getAssociatedTokenAddressSync( + failLp, + daoTreasury, + true + ), + vaultProgram: this.vaultClient.vaultProgram.programId, + treasury: daoTreasury, + }); + } + + async executeProposal(proposal: PublicKey) { + let storedProposal = await this.getProposal(proposal); + + return this.executeProposalIx( + proposal, + storedProposal.dao, + storedProposal.instruction + ).rpc(); + } + + executeProposalIx(proposal: PublicKey, dao: PublicKey, instruction: any) { + const [daoTreasury] = getDaoTreasuryAddr(this.autocrat.programId, dao); + return this.autocrat.methods + .executeProposal() + .accounts({ + proposal, + dao, + // daoTreasury, + }) + .remainingAccounts( + instruction.accounts + .concat({ + pubkey: instruction.programId, + isWritable: false, + isSigner: false, + }) + .map((meta: AccountMeta) => + meta.pubkey.equals(daoTreasury) + ? { ...meta, isSigner: false } + : meta + ) + ); + } + + // cranks the TWAPs of multiple proposals' markets. there's a limit on the + // number of proposals you can pass in, which I can't determine rn because + // there aren't enough proposals on devnet + async crankProposalMarkets( + proposals: PublicKey[], + priorityFeeMicroLamports: number + ) { + const amms: PublicKey[] = []; + + for (const proposal of proposals) { + const storedProposal = await this.getProposal(proposal); + amms.push(storedProposal.passAmm); + amms.push(storedProposal.failAmm); + } + + while (true) { + let ixs: TransactionInstruction[] = []; + + for (const amm of amms) { + ixs.push(await this.ammClient.crankThatTwapIx(amm).instruction()); + } + + let tx = new Transaction(); + tx.add( + ComputeBudgetProgram.setComputeUnitLimit({ units: 4_000 * ixs.length }) + ); + tx.add( + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: priorityFeeMicroLamports, + }) + ); + tx.add(...ixs); + try { + await this.provider.sendAndConfirm(tx); + } catch (err) { + console.log("err", err); + } + + await new Promise((resolve) => setTimeout(resolve, 65 * 1000)); // 65,000 milliseconds = 1 minute and 5 seconds + } + } +} diff --git a/sdk/src/v0.3/ConditionalVaultClient.ts b/sdk/src/v0.3/ConditionalVaultClient.ts new file mode 100644 index 00000000..e9feb0c5 --- /dev/null +++ b/sdk/src/v0.3/ConditionalVaultClient.ts @@ -0,0 +1,351 @@ +import { AnchorProvider, Program } from "@coral-xyz/anchor"; +import { AddressLookupTableAccount, Keypair, PublicKey } from "@solana/web3.js"; + +import { + ConditionalVault, + IDL as ConditionalVaultIDL, +} from "./types/conditional_vault"; + +import BN from "bn.js"; +import { + CONDITIONAL_VAULT_PROGRAM_ID, + MPL_TOKEN_METADATA_PROGRAM_ID, +} from "./constants"; +import { + getMetadataAddr, + getVaultAddr, + getVaultFinalizeMintAddr, + getVaultRevertMintAddr, +} from "./utils"; +import { MethodsBuilder } from "@coral-xyz/anchor/dist/cjs/program/namespace/methods"; +import { + createAssociatedTokenAccountIdempotentInstruction, + createAssociatedTokenAccountInstruction, + getAssociatedTokenAddressSync, +} from "@solana/spl-token"; + +export type CreateVaultClientParams = { + provider: AnchorProvider; + conditionalVaultProgramId?: PublicKey; +}; + +export class ConditionalVaultClient { + public readonly provider: AnchorProvider; + public readonly vaultProgram: Program; + public readonly luts: AddressLookupTableAccount[]; + + constructor( + provider: AnchorProvider, + conditionalVaultProgramId: PublicKey, + luts: AddressLookupTableAccount[] + ) { + this.provider = provider; + this.vaultProgram = new Program( + ConditionalVaultIDL, + conditionalVaultProgramId, + provider + ); + this.luts = luts; + } + + public static createClient( + createVaultClientParams: CreateVaultClientParams + ): ConditionalVaultClient { + let { provider, conditionalVaultProgramId } = createVaultClientParams; + + const luts: AddressLookupTableAccount[] = []; + + return new ConditionalVaultClient( + provider, + conditionalVaultProgramId || CONDITIONAL_VAULT_PROGRAM_ID, + luts + ); + } + + async getVault(vault: PublicKey) { + return this.vaultProgram.account.conditionalVault.fetch(vault); + } + + async mintConditionalTokens( + vault: PublicKey, + uiAmount: number, + user?: PublicKey | Keypair + ) { + const storedVault = await this.getVault(vault); + + return ( + this.mintConditionalTokensIx( + vault, + storedVault.underlyingTokenMint, + new BN(uiAmount).mul(new BN(10).pow(new BN(storedVault.decimals))), + user + ) + // .preInstructions([ + // createAssociatedTokenAccountIdempotentInstruction(this.provider.publicKey, ) + // ]) + .rpc() + ); + } + + mintConditionalTokensIx( + vault: PublicKey, + underlyingTokenMint: PublicKey, + amount: BN, + user?: PublicKey | Keypair + ) { + let userPubkey; + if (!user) { + userPubkey = this.provider.publicKey; + } else if (user instanceof Keypair) { + userPubkey = user.publicKey; + } else { + userPubkey = user; + } + + const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( + this.vaultProgram.programId, + vault + ); + const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + this.vaultProgram.programId, + vault + ); + + let userConditionalOnFinalizeTokenAccount = getAssociatedTokenAddressSync( + conditionalOnFinalizeTokenMint, + userPubkey + ); + + let userConditionalOnRevertTokenAccount = getAssociatedTokenAddressSync( + conditionalOnRevertTokenMint, + userPubkey + ); + + let ix = this.vaultProgram.methods + .mintConditionalTokens(amount) + .accounts({ + authority: userPubkey, + vault, + vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + userPubkey, + true + ), + conditionalOnFinalizeTokenMint, + userConditionalOnFinalizeTokenAccount, + conditionalOnRevertTokenMint, + userConditionalOnRevertTokenAccount, + }) + .preInstructions([ + createAssociatedTokenAccountIdempotentInstruction( + userPubkey, + userConditionalOnFinalizeTokenAccount, + userPubkey, + conditionalOnFinalizeTokenMint + ), + createAssociatedTokenAccountIdempotentInstruction( + userPubkey, + userConditionalOnRevertTokenAccount, + userPubkey, + conditionalOnRevertTokenMint + ), + ]); + if (user instanceof Keypair) { + ix = ix.signers([user]); + } + + return ix; + } + + initializeVaultIx( + settlementAuthority: PublicKey, + underlyingTokenMint: PublicKey + ): MethodsBuilder { + const [vault] = getVaultAddr( + this.vaultProgram.programId, + settlementAuthority, + underlyingTokenMint + ); + + const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( + this.vaultProgram.programId, + vault + ); + const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + this.vaultProgram.programId, + vault + ); + + const vaultUnderlyingTokenAccount = getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ); + + return this.vaultProgram.methods + .initializeConditionalVault({ settlementAuthority }) + .accounts({ + vault, + underlyingTokenMint, + vaultUnderlyingTokenAccount, + conditionalOnFinalizeTokenMint, + conditionalOnRevertTokenMint, + }) + .preInstructions([ + createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + vaultUnderlyingTokenAccount, + vault, + underlyingTokenMint + ), + ]); + } + + addMetadataToConditionalTokensIx( + vault: PublicKey, + underlyingTokenMint: PublicKey, + proposalNumber: number, + onFinalizeUri: string, + onRevertUri: string + ) { + const [underlyingTokenMetadata] = getMetadataAddr(underlyingTokenMint); + + const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( + this.vaultProgram.programId, + vault + ); + const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + this.vaultProgram.programId, + vault + ); + + const [conditionalOnFinalizeTokenMetadata] = getMetadataAddr( + conditionalOnFinalizeTokenMint + ); + + const [conditionalOnRevertTokenMetadata] = getMetadataAddr( + conditionalOnRevertTokenMint + ); + + return this.vaultProgram.methods + .addMetadataToConditionalTokens({ + proposalNumber: new BN(proposalNumber), + onFinalizeUri, + onRevertUri, + }) + .accounts({ + payer: this.provider.publicKey, + vault, + underlyingTokenMint, + underlyingTokenMetadata, + conditionalOnFinalizeTokenMint, + conditionalOnRevertTokenMint, + conditionalOnFinalizeTokenMetadata, + conditionalOnRevertTokenMetadata, + tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID, + }); + } + + redeemConditionalTokensIx(vault: PublicKey, underlyingTokenMint: PublicKey) { + const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( + this.vaultProgram.programId, + vault + ); + const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + this.vaultProgram.programId, + vault + ); + + return this.vaultProgram.methods + .redeemConditionalTokensForUnderlyingTokens() + .accounts({ + authority: this.provider.publicKey, + vault, + vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + this.provider.publicKey, + true + ), + conditionalOnFinalizeTokenMint, + userConditionalOnFinalizeTokenAccount: getAssociatedTokenAddressSync( + conditionalOnFinalizeTokenMint, + this.provider.publicKey + ), + conditionalOnRevertTokenMint, + userConditionalOnRevertTokenAccount: getAssociatedTokenAddressSync( + conditionalOnRevertTokenMint, + this.provider.publicKey + ), + }); + } + + mergeConditionalTokensIx( + vault: PublicKey, + underlyingTokenMint: PublicKey, + amount: BN + ) { + const [conditionalOnFinalizeTokenMint] = getVaultFinalizeMintAddr( + this.vaultProgram.programId, + vault + ); + const [conditionalOnRevertTokenMint] = getVaultRevertMintAddr( + this.vaultProgram.programId, + vault + ); + + return this.vaultProgram.methods + .mergeConditionalTokensForUnderlyingTokens(amount) + .accounts({ + authority: this.provider.publicKey, + vault, + vaultUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + vault, + true + ), + userUnderlyingTokenAccount: getAssociatedTokenAddressSync( + underlyingTokenMint, + this.provider.publicKey, + true + ), + conditionalOnFinalizeTokenMint, + userConditionalOnFinalizeTokenAccount: getAssociatedTokenAddressSync( + conditionalOnFinalizeTokenMint, + this.provider.publicKey + ), + conditionalOnRevertTokenMint, + userConditionalOnRevertTokenAccount: getAssociatedTokenAddressSync( + conditionalOnRevertTokenMint, + this.provider.publicKey + ), + }); + } + + async initializeVault( + settlementAuthority: PublicKey, + underlyingTokenMint: PublicKey + ): Promise { + const [vault] = getVaultAddr( + this.vaultProgram.programId, + settlementAuthority, + underlyingTokenMint + ); + + await this.initializeVaultIx( + settlementAuthority, + underlyingTokenMint + ).rpc(); + + return vault; + } +} diff --git a/sdk/src/constants.ts b/sdk/src/v0.3/constants.ts similarity index 100% rename from sdk/src/constants.ts rename to sdk/src/v0.3/constants.ts diff --git a/sdk/src/v0.3/index.ts b/sdk/src/v0.3/index.ts new file mode 100644 index 00000000..20730eed --- /dev/null +++ b/sdk/src/v0.3/index.ts @@ -0,0 +1,6 @@ +export * from "./types"; +export * from "./utils"; +export * from "./constants"; +export * from "./AmmClient"; +export * from "./AutocratClient"; +export * from "./ConditionalVaultClient"; diff --git a/sdk/src/types/amm.ts b/sdk/src/v0.3/types/amm.ts similarity index 100% rename from sdk/src/types/amm.ts rename to sdk/src/v0.3/types/amm.ts diff --git a/sdk/src/v0.3/types/autocrat.ts b/sdk/src/v0.3/types/autocrat.ts new file mode 100644 index 00000000..2265d9b1 --- /dev/null +++ b/sdk/src/v0.3/types/autocrat.ts @@ -0,0 +1,1263 @@ +export type Autocrat = { + version: "0.3.0"; + name: "autocrat"; + instructions: [ + { + name: "initializeDao"; + accounts: [ + { + name: "dao"; + isMut: true; + isSigner: true; + }, + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "tokenMint"; + isMut: false; + isSigner: false; + }, + { + name: "usdcMint"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "params"; + type: { + defined: "InitializeDaoParams"; + }; + } + ]; + }, + { + name: "initializeProposal"; + accounts: [ + { + name: "proposal"; + isMut: true; + isSigner: false; + }, + { + name: "dao"; + isMut: true; + isSigner: false; + }, + { + name: "quoteVault"; + isMut: false; + isSigner: false; + }, + { + name: "baseVault"; + isMut: false; + isSigner: false; + }, + { + name: "passAmm"; + isMut: false; + isSigner: false; + }, + { + name: "passLpMint"; + isMut: false; + isSigner: false; + }, + { + name: "failLpMint"; + isMut: false; + isSigner: false; + }, + { + name: "failAmm"; + isMut: false; + isSigner: false; + }, + { + name: "passLpUserAccount"; + isMut: true; + isSigner: false; + }, + { + name: "failLpUserAccount"; + isMut: true; + isSigner: false; + }, + { + name: "passLpVaultAccount"; + isMut: true; + isSigner: false; + }, + { + name: "failLpVaultAccount"; + isMut: true; + isSigner: false; + }, + { + name: "proposer"; + isMut: true; + isSigner: true; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "params"; + type: { + defined: "InitializeProposalParams"; + }; + } + ]; + }, + { + name: "finalizeProposal"; + accounts: [ + { + name: "proposal"; + isMut: true; + isSigner: false; + }, + { + name: "passAmm"; + isMut: false; + isSigner: false; + }, + { + name: "failAmm"; + isMut: false; + isSigner: false; + }, + { + name: "dao"; + isMut: false; + isSigner: false; + }, + { + name: "baseVault"; + isMut: true; + isSigner: false; + }, + { + name: "quoteVault"; + isMut: true; + isSigner: false; + }, + { + name: "treasury"; + isMut: false; + isSigner: false; + }, + { + name: "passLpUserAccount"; + isMut: true; + isSigner: false; + }, + { + name: "failLpUserAccount"; + isMut: true; + isSigner: false; + }, + { + name: "passLpVaultAccount"; + isMut: true; + isSigner: false; + }, + { + name: "failLpVaultAccount"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "vaultProgram"; + isMut: false; + isSigner: false; + } + ]; + args: []; + }, + { + name: "executeProposal"; + accounts: [ + { + name: "proposal"; + isMut: true; + isSigner: false; + }, + { + name: "dao"; + isMut: false; + isSigner: false; + } + ]; + args: []; + }, + { + name: "updateDao"; + accounts: [ + { + name: "dao"; + isMut: true; + isSigner: false; + }, + { + name: "treasury"; + isMut: false; + isSigner: true; + } + ]; + args: [ + { + name: "daoParams"; + type: { + defined: "UpdateDaoParams"; + }; + } + ]; + } + ]; + accounts: [ + { + name: "dao"; + type: { + kind: "struct"; + fields: [ + { + name: "treasuryPdaBump"; + type: "u8"; + }, + { + name: "treasury"; + type: "publicKey"; + }, + { + name: "tokenMint"; + type: "publicKey"; + }, + { + name: "usdcMint"; + type: "publicKey"; + }, + { + name: "proposalCount"; + type: "u32"; + }, + { + name: "passThresholdBps"; + type: "u16"; + }, + { + name: "slotsPerProposal"; + type: "u64"; + }, + { + name: "twapInitialObservation"; + docs: [ + "For manipulation-resistance the TWAP is a time-weighted average observation,", + "where observation tries to approximate price but can only move by", + "`twap_max_observation_change_per_update` per update. Because it can only move", + "a little bit per update, you need to check that it has a good initial observation.", + "Otherwise, an attacker could create a very high initial observation in the pass", + "market and a very low one in the fail market to force the proposal to pass.", + "", + "We recommend setting an initial observation around the spot price of the token,", + "and max observation change per update around 2% the spot price of the token.", + "For example, if the spot price of META is $400, we'd recommend setting an initial", + "observation of 400 (converted into the AMM prices) and a max observation change per", + "update of 8 (also converted into the AMM prices). Observations can be updated once", + "a minute, so 2% allows the proposal market to reach double the spot price or 0", + "in 50 minutes." + ]; + type: "u128"; + }, + { + name: "twapMaxObservationChangePerUpdate"; + type: "u128"; + }, + { + name: "minQuoteFutarchicLiquidity"; + docs: [ + "As an anti-spam measure and to help liquidity, you need to lock up some liquidity", + "in both futarchic markets in order to create a proposal.", + "", + "For example, for META, we can use a `min_quote_futarchic_liquidity` of", + "5000 * 1_000_000 (5000 USDC) and a `min_base_futarchic_liquidity` of", + "10 * 1_000_000_000 (10 META)." + ]; + type: "u64"; + }, + { + name: "minBaseFutarchicLiquidity"; + type: "u64"; + } + ]; + }; + }, + { + name: "proposal"; + type: { + kind: "struct"; + fields: [ + { + name: "number"; + type: "u32"; + }, + { + name: "proposer"; + type: "publicKey"; + }, + { + name: "descriptionUrl"; + type: "string"; + }, + { + name: "slotEnqueued"; + type: "u64"; + }, + { + name: "state"; + type: { + defined: "ProposalState"; + }; + }, + { + name: "instruction"; + type: { + defined: "ProposalInstruction"; + }; + }, + { + name: "passAmm"; + type: "publicKey"; + }, + { + name: "failAmm"; + type: "publicKey"; + }, + { + name: "baseVault"; + type: "publicKey"; + }, + { + name: "quoteVault"; + type: "publicKey"; + }, + { + name: "dao"; + type: "publicKey"; + }, + { + name: "passLpTokensLocked"; + type: "u64"; + }, + { + name: "failLpTokensLocked"; + type: "u64"; + }, + { + name: "nonce"; + docs: [ + "We need to include a per-proposer nonce to prevent some weird proposal", + "front-running edge cases. Using a `u64` means that proposers are unlikely", + "to run into collisions, even if they generate nonces randomly - I've run", + "the math :D" + ]; + type: "u64"; + }, + { + name: "pdaBump"; + type: "u8"; + } + ]; + }; + } + ]; + types: [ + { + name: "InitializeDaoParams"; + type: { + kind: "struct"; + fields: [ + { + name: "twapInitialObservation"; + type: "u128"; + }, + { + name: "twapMaxObservationChangePerUpdate"; + type: "u128"; + }, + { + name: "minQuoteFutarchicLiquidity"; + type: "u64"; + }, + { + name: "minBaseFutarchicLiquidity"; + type: "u64"; + }, + { + name: "passThresholdBps"; + type: { + option: "u16"; + }; + }, + { + name: "slotsPerProposal"; + type: { + option: "u64"; + }; + } + ]; + }; + }, + { + name: "InitializeProposalParams"; + type: { + kind: "struct"; + fields: [ + { + name: "descriptionUrl"; + type: "string"; + }, + { + name: "instruction"; + type: { + defined: "ProposalInstruction"; + }; + }, + { + name: "passLpTokensToLock"; + type: "u64"; + }, + { + name: "failLpTokensToLock"; + type: "u64"; + }, + { + name: "nonce"; + type: "u64"; + } + ]; + }; + }, + { + name: "UpdateDaoParams"; + type: { + kind: "struct"; + fields: [ + { + name: "passThresholdBps"; + type: { + option: "u16"; + }; + }, + { + name: "slotsPerProposal"; + type: { + option: "u64"; + }; + }, + { + name: "twapInitialObservation"; + type: { + option: "u128"; + }; + }, + { + name: "twapMaxObservationChangePerUpdate"; + type: { + option: "u128"; + }; + }, + { + name: "minQuoteFutarchicLiquidity"; + type: { + option: "u64"; + }; + }, + { + name: "minBaseFutarchicLiquidity"; + type: { + option: "u64"; + }; + } + ]; + }; + }, + { + name: "ProposalAccount"; + type: { + kind: "struct"; + fields: [ + { + name: "pubkey"; + type: "publicKey"; + }, + { + name: "isSigner"; + type: "bool"; + }, + { + name: "isWritable"; + type: "bool"; + } + ]; + }; + }, + { + name: "ProposalInstruction"; + type: { + kind: "struct"; + fields: [ + { + name: "programId"; + type: "publicKey"; + }, + { + name: "accounts"; + type: { + vec: { + defined: "ProposalAccount"; + }; + }; + }, + { + name: "data"; + type: "bytes"; + } + ]; + }; + }, + { + name: "ProposalState"; + type: { + kind: "enum"; + variants: [ + { + name: "Pending"; + }, + { + name: "Passed"; + }, + { + name: "Failed"; + }, + { + name: "Executed"; + } + ]; + }; + } + ]; + errors: [ + { + code: 6000; + name: "AmmTooOld"; + msg: "Amms must have been created within 5 minutes (counted in slots) of proposal initialization"; + }, + { + code: 6001; + name: "InvalidInitialObservation"; + msg: "An amm has an `initial_observation` that doesn't match the `dao`'s config"; + }, + { + code: 6002; + name: "InvalidMaxObservationChange"; + msg: "An amm has a `max_observation_change_per_update` that doesn't match the `dao`'s config"; + }, + { + code: 6003; + name: "InvalidSettlementAuthority"; + msg: "One of the vaults has an invalid `settlement_authority`"; + }, + { + code: 6004; + name: "ProposalTooYoung"; + msg: "Proposal is too young to be executed or rejected"; + }, + { + code: 6005; + name: "MarketsTooYoung"; + msg: "Markets too young for proposal to be finalized. TWAP might need to be cranked"; + }, + { + code: 6006; + name: "ProposalAlreadyFinalized"; + msg: "This proposal has already been finalized"; + }, + { + code: 6007; + name: "InvalidVaultNonce"; + msg: "A conditional vault has an invalid nonce. A nonce should encode the proposal number"; + }, + { + code: 6008; + name: "ProposalNotPassed"; + msg: "This proposal can't be executed because it isn't in the passed state"; + }, + { + code: 6009; + name: "InsufficientLpTokenBalance"; + msg: "The proposer has fewer pass or fail LP tokens than they requested to lock"; + }, + { + code: 6010; + name: "InsufficientLpTokenLock"; + msg: "The LP tokens passed in have less liquidity than the DAO's `min_quote_futarchic_liquidity` or `min_base_futachic_liquidity`"; + } + ]; +}; + +export const IDL: Autocrat = { + version: "0.3.0", + name: "autocrat", + instructions: [ + { + name: "initializeDao", + accounts: [ + { + name: "dao", + isMut: true, + isSigner: true, + }, + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "tokenMint", + isMut: false, + isSigner: false, + }, + { + name: "usdcMint", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "params", + type: { + defined: "InitializeDaoParams", + }, + }, + ], + }, + { + name: "initializeProposal", + accounts: [ + { + name: "proposal", + isMut: true, + isSigner: false, + }, + { + name: "dao", + isMut: true, + isSigner: false, + }, + { + name: "quoteVault", + isMut: false, + isSigner: false, + }, + { + name: "baseVault", + isMut: false, + isSigner: false, + }, + { + name: "passAmm", + isMut: false, + isSigner: false, + }, + { + name: "passLpMint", + isMut: false, + isSigner: false, + }, + { + name: "failLpMint", + isMut: false, + isSigner: false, + }, + { + name: "failAmm", + isMut: false, + isSigner: false, + }, + { + name: "passLpUserAccount", + isMut: true, + isSigner: false, + }, + { + name: "failLpUserAccount", + isMut: true, + isSigner: false, + }, + { + name: "passLpVaultAccount", + isMut: true, + isSigner: false, + }, + { + name: "failLpVaultAccount", + isMut: true, + isSigner: false, + }, + { + name: "proposer", + isMut: true, + isSigner: true, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "params", + type: { + defined: "InitializeProposalParams", + }, + }, + ], + }, + { + name: "finalizeProposal", + accounts: [ + { + name: "proposal", + isMut: true, + isSigner: false, + }, + { + name: "passAmm", + isMut: false, + isSigner: false, + }, + { + name: "failAmm", + isMut: false, + isSigner: false, + }, + { + name: "dao", + isMut: false, + isSigner: false, + }, + { + name: "baseVault", + isMut: true, + isSigner: false, + }, + { + name: "quoteVault", + isMut: true, + isSigner: false, + }, + { + name: "treasury", + isMut: false, + isSigner: false, + }, + { + name: "passLpUserAccount", + isMut: true, + isSigner: false, + }, + { + name: "failLpUserAccount", + isMut: true, + isSigner: false, + }, + { + name: "passLpVaultAccount", + isMut: true, + isSigner: false, + }, + { + name: "failLpVaultAccount", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "vaultProgram", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: "executeProposal", + accounts: [ + { + name: "proposal", + isMut: true, + isSigner: false, + }, + { + name: "dao", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: "updateDao", + accounts: [ + { + name: "dao", + isMut: true, + isSigner: false, + }, + { + name: "treasury", + isMut: false, + isSigner: true, + }, + ], + args: [ + { + name: "daoParams", + type: { + defined: "UpdateDaoParams", + }, + }, + ], + }, + ], + accounts: [ + { + name: "dao", + type: { + kind: "struct", + fields: [ + { + name: "treasuryPdaBump", + type: "u8", + }, + { + name: "treasury", + type: "publicKey", + }, + { + name: "tokenMint", + type: "publicKey", + }, + { + name: "usdcMint", + type: "publicKey", + }, + { + name: "proposalCount", + type: "u32", + }, + { + name: "passThresholdBps", + type: "u16", + }, + { + name: "slotsPerProposal", + type: "u64", + }, + { + name: "twapInitialObservation", + docs: [ + "For manipulation-resistance the TWAP is a time-weighted average observation,", + "where observation tries to approximate price but can only move by", + "`twap_max_observation_change_per_update` per update. Because it can only move", + "a little bit per update, you need to check that it has a good initial observation.", + "Otherwise, an attacker could create a very high initial observation in the pass", + "market and a very low one in the fail market to force the proposal to pass.", + "", + "We recommend setting an initial observation around the spot price of the token,", + "and max observation change per update around 2% the spot price of the token.", + "For example, if the spot price of META is $400, we'd recommend setting an initial", + "observation of 400 (converted into the AMM prices) and a max observation change per", + "update of 8 (also converted into the AMM prices). Observations can be updated once", + "a minute, so 2% allows the proposal market to reach double the spot price or 0", + "in 50 minutes.", + ], + type: "u128", + }, + { + name: "twapMaxObservationChangePerUpdate", + type: "u128", + }, + { + name: "minQuoteFutarchicLiquidity", + docs: [ + "As an anti-spam measure and to help liquidity, you need to lock up some liquidity", + "in both futarchic markets in order to create a proposal.", + "", + "For example, for META, we can use a `min_quote_futarchic_liquidity` of", + "5000 * 1_000_000 (5000 USDC) and a `min_base_futarchic_liquidity` of", + "10 * 1_000_000_000 (10 META).", + ], + type: "u64", + }, + { + name: "minBaseFutarchicLiquidity", + type: "u64", + }, + ], + }, + }, + { + name: "proposal", + type: { + kind: "struct", + fields: [ + { + name: "number", + type: "u32", + }, + { + name: "proposer", + type: "publicKey", + }, + { + name: "descriptionUrl", + type: "string", + }, + { + name: "slotEnqueued", + type: "u64", + }, + { + name: "state", + type: { + defined: "ProposalState", + }, + }, + { + name: "instruction", + type: { + defined: "ProposalInstruction", + }, + }, + { + name: "passAmm", + type: "publicKey", + }, + { + name: "failAmm", + type: "publicKey", + }, + { + name: "baseVault", + type: "publicKey", + }, + { + name: "quoteVault", + type: "publicKey", + }, + { + name: "dao", + type: "publicKey", + }, + { + name: "passLpTokensLocked", + type: "u64", + }, + { + name: "failLpTokensLocked", + type: "u64", + }, + { + name: "nonce", + docs: [ + "We need to include a per-proposer nonce to prevent some weird proposal", + "front-running edge cases. Using a `u64` means that proposers are unlikely", + "to run into collisions, even if they generate nonces randomly - I've run", + "the math :D", + ], + type: "u64", + }, + { + name: "pdaBump", + type: "u8", + }, + ], + }, + }, + ], + types: [ + { + name: "InitializeDaoParams", + type: { + kind: "struct", + fields: [ + { + name: "twapInitialObservation", + type: "u128", + }, + { + name: "twapMaxObservationChangePerUpdate", + type: "u128", + }, + { + name: "minQuoteFutarchicLiquidity", + type: "u64", + }, + { + name: "minBaseFutarchicLiquidity", + type: "u64", + }, + { + name: "passThresholdBps", + type: { + option: "u16", + }, + }, + { + name: "slotsPerProposal", + type: { + option: "u64", + }, + }, + ], + }, + }, + { + name: "InitializeProposalParams", + type: { + kind: "struct", + fields: [ + { + name: "descriptionUrl", + type: "string", + }, + { + name: "instruction", + type: { + defined: "ProposalInstruction", + }, + }, + { + name: "passLpTokensToLock", + type: "u64", + }, + { + name: "failLpTokensToLock", + type: "u64", + }, + { + name: "nonce", + type: "u64", + }, + ], + }, + }, + { + name: "UpdateDaoParams", + type: { + kind: "struct", + fields: [ + { + name: "passThresholdBps", + type: { + option: "u16", + }, + }, + { + name: "slotsPerProposal", + type: { + option: "u64", + }, + }, + { + name: "twapInitialObservation", + type: { + option: "u128", + }, + }, + { + name: "twapMaxObservationChangePerUpdate", + type: { + option: "u128", + }, + }, + { + name: "minQuoteFutarchicLiquidity", + type: { + option: "u64", + }, + }, + { + name: "minBaseFutarchicLiquidity", + type: { + option: "u64", + }, + }, + ], + }, + }, + { + name: "ProposalAccount", + type: { + kind: "struct", + fields: [ + { + name: "pubkey", + type: "publicKey", + }, + { + name: "isSigner", + type: "bool", + }, + { + name: "isWritable", + type: "bool", + }, + ], + }, + }, + { + name: "ProposalInstruction", + type: { + kind: "struct", + fields: [ + { + name: "programId", + type: "publicKey", + }, + { + name: "accounts", + type: { + vec: { + defined: "ProposalAccount", + }, + }, + }, + { + name: "data", + type: "bytes", + }, + ], + }, + }, + { + name: "ProposalState", + type: { + kind: "enum", + variants: [ + { + name: "Pending", + }, + { + name: "Passed", + }, + { + name: "Failed", + }, + { + name: "Executed", + }, + ], + }, + }, + ], + errors: [ + { + code: 6000, + name: "AmmTooOld", + msg: "Amms must have been created within 5 minutes (counted in slots) of proposal initialization", + }, + { + code: 6001, + name: "InvalidInitialObservation", + msg: "An amm has an `initial_observation` that doesn't match the `dao`'s config", + }, + { + code: 6002, + name: "InvalidMaxObservationChange", + msg: "An amm has a `max_observation_change_per_update` that doesn't match the `dao`'s config", + }, + { + code: 6003, + name: "InvalidSettlementAuthority", + msg: "One of the vaults has an invalid `settlement_authority`", + }, + { + code: 6004, + name: "ProposalTooYoung", + msg: "Proposal is too young to be executed or rejected", + }, + { + code: 6005, + name: "MarketsTooYoung", + msg: "Markets too young for proposal to be finalized. TWAP might need to be cranked", + }, + { + code: 6006, + name: "ProposalAlreadyFinalized", + msg: "This proposal has already been finalized", + }, + { + code: 6007, + name: "InvalidVaultNonce", + msg: "A conditional vault has an invalid nonce. A nonce should encode the proposal number", + }, + { + code: 6008, + name: "ProposalNotPassed", + msg: "This proposal can't be executed because it isn't in the passed state", + }, + { + code: 6009, + name: "InsufficientLpTokenBalance", + msg: "The proposer has fewer pass or fail LP tokens than they requested to lock", + }, + { + code: 6010, + name: "InsufficientLpTokenLock", + msg: "The LP tokens passed in have less liquidity than the DAO's `min_quote_futarchic_liquidity` or `min_base_futachic_liquidity`", + }, + ], +}; diff --git a/sdk/src/types/autocrat_migrator.ts b/sdk/src/v0.3/types/autocrat_migrator.ts similarity index 100% rename from sdk/src/types/autocrat_migrator.ts rename to sdk/src/v0.3/types/autocrat_migrator.ts diff --git a/sdk/src/v0.3/types/conditional_vault.ts b/sdk/src/v0.3/types/conditional_vault.ts new file mode 100644 index 00000000..0af7cb8d --- /dev/null +++ b/sdk/src/v0.3/types/conditional_vault.ts @@ -0,0 +1,895 @@ +export type ConditionalVault = { + version: "0.3.0"; + name: "conditional_vault"; + instructions: [ + { + name: "initializeConditionalVault"; + accounts: [ + { + name: "vault"; + isMut: true; + isSigner: false; + }, + { + name: "underlyingTokenMint"; + isMut: false; + isSigner: false; + }, + { + name: "conditionalOnFinalizeTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "conditionalOnRevertTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "vaultUnderlyingTokenAccount"; + isMut: false; + isSigner: false; + }, + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "associatedTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "args"; + type: { + defined: "InitializeConditionalVaultArgs"; + }; + } + ]; + }, + { + name: "addMetadataToConditionalTokens"; + accounts: [ + { + name: "payer"; + isMut: true; + isSigner: true; + }, + { + name: "vault"; + isMut: true; + isSigner: false; + }, + { + name: "underlyingTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "underlyingTokenMetadata"; + isMut: false; + isSigner: false; + }, + { + name: "conditionalOnFinalizeTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "conditionalOnRevertTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "conditionalOnFinalizeTokenMetadata"; + isMut: true; + isSigner: false; + }, + { + name: "conditionalOnRevertTokenMetadata"; + isMut: true; + isSigner: false; + }, + { + name: "tokenMetadataProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "rent"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "args"; + type: { + defined: "AddMetadataToConditionalTokensArgs"; + }; + } + ]; + }, + { + name: "settleConditionalVault"; + accounts: [ + { + name: "settlementAuthority"; + isMut: false; + isSigner: true; + }, + { + name: "vault"; + isMut: true; + isSigner: false; + } + ]; + args: [ + { + name: "newStatus"; + type: { + defined: "VaultStatus"; + }; + } + ]; + }, + { + name: "mergeConditionalTokensForUnderlyingTokens"; + accounts: [ + { + name: "vault"; + isMut: false; + isSigner: false; + }, + { + name: "conditionalOnFinalizeTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "conditionalOnRevertTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "vaultUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "authority"; + isMut: false; + isSigner: true; + }, + { + name: "userConditionalOnFinalizeTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userConditionalOnRevertTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "amount"; + type: "u64"; + } + ]; + }, + { + name: "mintConditionalTokens"; + accounts: [ + { + name: "vault"; + isMut: false; + isSigner: false; + }, + { + name: "conditionalOnFinalizeTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "conditionalOnRevertTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "vaultUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "authority"; + isMut: false; + isSigner: true; + }, + { + name: "userConditionalOnFinalizeTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userConditionalOnRevertTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "amount"; + type: "u64"; + } + ]; + }, + { + name: "redeemConditionalTokensForUnderlyingTokens"; + accounts: [ + { + name: "vault"; + isMut: false; + isSigner: false; + }, + { + name: "conditionalOnFinalizeTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "conditionalOnRevertTokenMint"; + isMut: true; + isSigner: false; + }, + { + name: "vaultUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "authority"; + isMut: false; + isSigner: true; + }, + { + name: "userConditionalOnFinalizeTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userConditionalOnRevertTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userUnderlyingTokenAccount"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + } + ]; + args: []; + } + ]; + accounts: [ + { + name: "conditionalVault"; + type: { + kind: "struct"; + fields: [ + { + name: "status"; + type: { + defined: "VaultStatus"; + }; + }, + { + name: "settlementAuthority"; + docs: [ + "The account that can either finalize the vault to make conditional tokens", + "redeemable for underlying tokens or revert the vault to make deposit", + "slips redeemable for underlying tokens." + ]; + type: "publicKey"; + }, + { + name: "underlyingTokenMint"; + docs: ["The mint of the tokens that are deposited into the vault."]; + type: "publicKey"; + }, + { + name: "underlyingTokenAccount"; + docs: ["The vault's storage account for deposited funds."]; + type: "publicKey"; + }, + { + name: "conditionalOnFinalizeTokenMint"; + type: "publicKey"; + }, + { + name: "conditionalOnRevertTokenMint"; + type: "publicKey"; + }, + { + name: "pdaBump"; + type: "u8"; + }, + { + name: "decimals"; + type: "u8"; + } + ]; + }; + } + ]; + types: [ + { + name: "AddMetadataToConditionalTokensArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "proposalNumber"; + type: "u64"; + }, + { + name: "onFinalizeUri"; + type: "string"; + }, + { + name: "onRevertUri"; + type: "string"; + } + ]; + }; + }, + { + name: "InitializeConditionalVaultArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "settlementAuthority"; + type: "publicKey"; + } + ]; + }; + }, + { + name: "VaultStatus"; + type: { + kind: "enum"; + variants: [ + { + name: "Active"; + }, + { + name: "Finalized"; + }, + { + name: "Reverted"; + } + ]; + }; + } + ]; + errors: [ + { + code: 6000; + name: "InsufficientUnderlyingTokens"; + msg: "Insufficient underlying token balance to mint this amount of conditional tokens"; + }, + { + code: 6001; + name: "InvalidVaultUnderlyingTokenAccount"; + msg: "This `vault_underlying_token_account` is not this vault's `underlying_token_account`"; + }, + { + code: 6002; + name: "InvalidConditionalTokenMint"; + msg: "This conditional token mint is not this vault's conditional token mint"; + }, + { + code: 6003; + name: "CantRedeemConditionalTokens"; + msg: "Vault needs to be settled as finalized before users can redeem conditional tokens for underlying tokens"; + }, + { + code: 6004; + name: "VaultAlreadySettled"; + msg: "Once a vault has been settled, its status as either finalized or reverted cannot be changed"; + } + ]; +}; + +export const IDL: ConditionalVault = { + version: "0.3.0", + name: "conditional_vault", + instructions: [ + { + name: "initializeConditionalVault", + accounts: [ + { + name: "vault", + isMut: true, + isSigner: false, + }, + { + name: "underlyingTokenMint", + isMut: false, + isSigner: false, + }, + { + name: "conditionalOnFinalizeTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "conditionalOnRevertTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "vaultUnderlyingTokenAccount", + isMut: false, + isSigner: false, + }, + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "associatedTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "InitializeConditionalVaultArgs", + }, + }, + ], + }, + { + name: "addMetadataToConditionalTokens", + accounts: [ + { + name: "payer", + isMut: true, + isSigner: true, + }, + { + name: "vault", + isMut: true, + isSigner: false, + }, + { + name: "underlyingTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "underlyingTokenMetadata", + isMut: false, + isSigner: false, + }, + { + name: "conditionalOnFinalizeTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "conditionalOnRevertTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "conditionalOnFinalizeTokenMetadata", + isMut: true, + isSigner: false, + }, + { + name: "conditionalOnRevertTokenMetadata", + isMut: true, + isSigner: false, + }, + { + name: "tokenMetadataProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "rent", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "AddMetadataToConditionalTokensArgs", + }, + }, + ], + }, + { + name: "settleConditionalVault", + accounts: [ + { + name: "settlementAuthority", + isMut: false, + isSigner: true, + }, + { + name: "vault", + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: "newStatus", + type: { + defined: "VaultStatus", + }, + }, + ], + }, + { + name: "mergeConditionalTokensForUnderlyingTokens", + accounts: [ + { + name: "vault", + isMut: false, + isSigner: false, + }, + { + name: "conditionalOnFinalizeTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "conditionalOnRevertTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "vaultUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "authority", + isMut: false, + isSigner: true, + }, + { + name: "userConditionalOnFinalizeTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "userConditionalOnRevertTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "userUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "amount", + type: "u64", + }, + ], + }, + { + name: "mintConditionalTokens", + accounts: [ + { + name: "vault", + isMut: false, + isSigner: false, + }, + { + name: "conditionalOnFinalizeTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "conditionalOnRevertTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "vaultUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "authority", + isMut: false, + isSigner: true, + }, + { + name: "userConditionalOnFinalizeTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "userConditionalOnRevertTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "userUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "amount", + type: "u64", + }, + ], + }, + { + name: "redeemConditionalTokensForUnderlyingTokens", + accounts: [ + { + name: "vault", + isMut: false, + isSigner: false, + }, + { + name: "conditionalOnFinalizeTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "conditionalOnRevertTokenMint", + isMut: true, + isSigner: false, + }, + { + name: "vaultUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "authority", + isMut: false, + isSigner: true, + }, + { + name: "userConditionalOnFinalizeTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "userConditionalOnRevertTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "userUnderlyingTokenAccount", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + ], + accounts: [ + { + name: "conditionalVault", + type: { + kind: "struct", + fields: [ + { + name: "status", + type: { + defined: "VaultStatus", + }, + }, + { + name: "settlementAuthority", + docs: [ + "The account that can either finalize the vault to make conditional tokens", + "redeemable for underlying tokens or revert the vault to make deposit", + "slips redeemable for underlying tokens.", + ], + type: "publicKey", + }, + { + name: "underlyingTokenMint", + docs: ["The mint of the tokens that are deposited into the vault."], + type: "publicKey", + }, + { + name: "underlyingTokenAccount", + docs: ["The vault's storage account for deposited funds."], + type: "publicKey", + }, + { + name: "conditionalOnFinalizeTokenMint", + type: "publicKey", + }, + { + name: "conditionalOnRevertTokenMint", + type: "publicKey", + }, + { + name: "pdaBump", + type: "u8", + }, + { + name: "decimals", + type: "u8", + }, + ], + }, + }, + ], + types: [ + { + name: "AddMetadataToConditionalTokensArgs", + type: { + kind: "struct", + fields: [ + { + name: "proposalNumber", + type: "u64", + }, + { + name: "onFinalizeUri", + type: "string", + }, + { + name: "onRevertUri", + type: "string", + }, + ], + }, + }, + { + name: "InitializeConditionalVaultArgs", + type: { + kind: "struct", + fields: [ + { + name: "settlementAuthority", + type: "publicKey", + }, + ], + }, + }, + { + name: "VaultStatus", + type: { + kind: "enum", + variants: [ + { + name: "Active", + }, + { + name: "Finalized", + }, + { + name: "Reverted", + }, + ], + }, + }, + ], + errors: [ + { + code: 6000, + name: "InsufficientUnderlyingTokens", + msg: "Insufficient underlying token balance to mint this amount of conditional tokens", + }, + { + code: 6001, + name: "InvalidVaultUnderlyingTokenAccount", + msg: "This `vault_underlying_token_account` is not this vault's `underlying_token_account`", + }, + { + code: 6002, + name: "InvalidConditionalTokenMint", + msg: "This conditional token mint is not this vault's conditional token mint", + }, + { + code: 6003, + name: "CantRedeemConditionalTokens", + msg: "Vault needs to be settled as finalized before users can redeem conditional tokens for underlying tokens", + }, + { + code: 6004, + name: "VaultAlreadySettled", + msg: "Once a vault has been settled, its status as either finalized or reverted cannot be changed", + }, + ], +}; diff --git a/sdk/src/types/index.ts b/sdk/src/v0.3/types/index.ts similarity index 100% rename from sdk/src/types/index.ts rename to sdk/src/v0.3/types/index.ts diff --git a/sdk/src/types/optimistic_timelock.ts b/sdk/src/v0.3/types/optimistic_timelock.ts similarity index 100% rename from sdk/src/types/optimistic_timelock.ts rename to sdk/src/v0.3/types/optimistic_timelock.ts diff --git a/sdk/src/types/utils.ts b/sdk/src/v0.3/types/utils.ts similarity index 100% rename from sdk/src/types/utils.ts rename to sdk/src/v0.3/types/utils.ts diff --git a/sdk/src/v0.3/utils/cu.ts b/sdk/src/v0.3/utils/cu.ts new file mode 100644 index 00000000..086df7c2 --- /dev/null +++ b/sdk/src/v0.3/utils/cu.ts @@ -0,0 +1,11 @@ +export const MaxCUs = { + initializeDao: 20_000, + createIdempotent: 25_000, + initializeConditionalVault: 45_000, + mintConditionalTokens: 35_000, + initializeAmm: 120_000, + addLiquidity: 120_000, + initializeProposal: 60_000, +}; + +export const DEFAULT_CU_PRICE = 1; diff --git a/sdk/src/utils/filters.ts b/sdk/src/v0.3/utils/filters.ts similarity index 100% rename from sdk/src/utils/filters.ts rename to sdk/src/v0.3/utils/filters.ts diff --git a/sdk/src/utils/index.ts b/sdk/src/v0.3/utils/index.ts similarity index 100% rename from sdk/src/utils/index.ts rename to sdk/src/v0.3/utils/index.ts diff --git a/sdk/src/utils/instruction.ts b/sdk/src/v0.3/utils/instruction.ts similarity index 100% rename from sdk/src/utils/instruction.ts rename to sdk/src/v0.3/utils/instruction.ts diff --git a/sdk/src/utils/metadata.ts b/sdk/src/v0.3/utils/metadata.ts similarity index 100% rename from sdk/src/utils/metadata.ts rename to sdk/src/v0.3/utils/metadata.ts diff --git a/sdk/src/v0.3/utils/pda.ts b/sdk/src/v0.3/utils/pda.ts new file mode 100644 index 00000000..115043a8 --- /dev/null +++ b/sdk/src/v0.3/utils/pda.ts @@ -0,0 +1,110 @@ +import { AccountMeta, PublicKey } from "@solana/web3.js"; +import { utils } from "@coral-xyz/anchor"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import BN from "bn.js"; +import { + fromWeb3JsPublicKey, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { MPL_TOKEN_METADATA_PROGRAM_ID } from "../constants"; + +export const getVaultAddr = ( + programId: PublicKey, + settlementAuthority: PublicKey, + underlyingTokenMint: PublicKey +) => { + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("conditional_vault"), + settlementAuthority.toBuffer(), + underlyingTokenMint.toBuffer(), + ], + programId + ); +}; + +export const getVaultFinalizeMintAddr = ( + programId: PublicKey, + vault: PublicKey +) => { + return getVaultMintAddr(programId, vault, "conditional_on_finalize_mint"); +}; + +export const getMetadataAddr = (mint: PublicKey) => { + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("metadata"), + MPL_TOKEN_METADATA_PROGRAM_ID.toBuffer(), + mint.toBuffer(), + ], + MPL_TOKEN_METADATA_PROGRAM_ID + ); +}; + +export const getVaultRevertMintAddr = ( + programId: PublicKey, + vault: PublicKey +) => { + return getVaultMintAddr(programId, vault, "conditional_on_revert_mint"); +}; + +const getVaultMintAddr = ( + programId: PublicKey, + vault: PublicKey, + seed: string +) => { + return PublicKey.findProgramAddressSync( + [utils.bytes.utf8.encode(seed), vault.toBuffer()], + programId + ); +}; + +export const getDaoTreasuryAddr = ( + programId: PublicKey, + dao: PublicKey +): [PublicKey, number] => { + return PublicKey.findProgramAddressSync([dao.toBuffer()], programId); +}; + +export const getProposalAddr = ( + programId: PublicKey, + proposer: PublicKey, + nonce: BN +): [PublicKey, number] => { + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("proposal"), + proposer.toBuffer(), + nonce.toArrayLike(Buffer, "le", 8), + ], + programId + ); +}; + +export const getAmmAddr = ( + programId: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey +): [PublicKey, number] => { + return PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("amm__"), + baseMint.toBuffer(), + quoteMint.toBuffer(), + ], + programId + ); +}; + +export const getAmmLpMintAddr = ( + programId: PublicKey, + amm: PublicKey +): [PublicKey, number] => { + return PublicKey.findProgramAddressSync( + [utils.bytes.utf8.encode("amm_lp_mint"), amm.toBuffer()], + programId + ); +}; diff --git a/sdk/src/utils/priceMath.ts b/sdk/src/v0.3/utils/priceMath.ts similarity index 100% rename from sdk/src/utils/priceMath.ts rename to sdk/src/v0.3/utils/priceMath.ts diff --git a/sdk/src/v0.4/AmmClient.ts b/sdk/src/v0.4/AmmClient.ts new file mode 100644 index 00000000..c6db19ec --- /dev/null +++ b/sdk/src/v0.4/AmmClient.ts @@ -0,0 +1,526 @@ +import { AnchorProvider, IdlTypes, Program } from "@coral-xyz/anchor"; +import { AddressLookupTableAccount, Keypair, PublicKey } from "@solana/web3.js"; + +import { Amm as AmmIDLType, IDL as AmmIDL } from "./types/amm.js"; + +import BN from "bn.js"; +import { AMM_PROGRAM_ID } from "./constants.js"; +import { AmmAccount, LowercaseKeys } from "./types/index.js"; +import { getAmmLpMintAddr, getAmmAddr } from "./utils/pda.js"; +// import { MethodsBuilder } from "@coral-xyz/anchor/dist/cjs/program/namespace/methods"; +import { + MintLayout, + unpackMint, + getAssociatedTokenAddressSync, + createAssociatedTokenAccountIdempotentInstruction, +} from "@solana/spl-token"; +import { PriceMath } from "./utils/priceMath.js"; + +export type SwapType = LowercaseKeys["SwapType"]>; + +export type CreateAmmClientParams = { + provider: AnchorProvider; + ammProgramId?: PublicKey; +}; + +export type AddLiquiditySimulation = { + baseAmount: BN; + quoteAmount: BN; + expectedLpTokens: BN; + minLpTokens?: BN; + maxBaseAmount?: BN; +}; + +export type SwapSimulation = { + expectedOut: BN; + newBaseReserves: BN; + newQuoteReserves: BN; + minExpectedOut?: BN; +}; + +export type RemoveLiquiditySimulation = { + expectedBaseOut: BN; + expectedQuoteOut: BN; + minBaseOut?: BN; + minQuoteOut?: BN; +}; + +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 createClient( + createAutocratClientParams: CreateAmmClientParams + ): AmmClient { + let { provider, ammProgramId: programId } = createAutocratClientParams; + + const luts: AddressLookupTableAccount[] = []; + + return new AmmClient(provider, programId || AMM_PROGRAM_ID, luts); + } + + getProgramId(): PublicKey { + return this.program.programId; + } + + async createAmm( + proposal: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + twapInitialObservation: number, + twapMaxObservationChangePerUpdate?: number + ): Promise { + if (!twapMaxObservationChangePerUpdate) { + twapMaxObservationChangePerUpdate = twapInitialObservation * 0.02; + } + let [amm] = getAmmAddr(this.getProgramId(), baseMint, quoteMint); + + let baseDecimals = unpackMint( + baseMint, + await this.provider.connection.getAccountInfo(baseMint) + ).decimals; + let quoteDecimals = unpackMint( + quoteMint, + await this.provider.connection.getAccountInfo(quoteMint) + ).decimals; + + let [twapFirstObservationScaled, twapMaxObservationChangePerUpdateScaled] = + PriceMath.getAmmPrices( + baseDecimals, + quoteDecimals, + twapInitialObservation, + twapMaxObservationChangePerUpdate + ); + + await this.createAmmIx( + baseMint, + quoteMint, + twapFirstObservationScaled, + twapMaxObservationChangePerUpdateScaled + ).rpc(); + + return amm; + } + + // both twap values need to be scaled beforehand + createAmmIx( + baseMint: PublicKey, + quoteMint: PublicKey, + twapInitialObservation: BN, + twapMaxObservationChangePerUpdate: BN + ) { + let [amm] = getAmmAddr(this.getProgramId(), baseMint, quoteMint); + let [lpMint] = getAmmLpMintAddr(this.getProgramId(), amm); + + let vaultAtaBase = getAssociatedTokenAddressSync(baseMint, amm, true); + let vaultAtaQuote = getAssociatedTokenAddressSync(quoteMint, amm, true); + + return this.program.methods + .createAmm({ + twapInitialObservation, + twapMaxObservationChangePerUpdate, + }) + .accounts({ + user: this.provider.publicKey, + amm, + lpMint, + baseMint, + quoteMint, + vaultAtaBase, + vaultAtaQuote, + }); + } + + async addLiquidity( + amm: PublicKey, + quoteAmount?: number, + baseAmount?: number + ) { + let storedAmm = await this.getAmm(amm); + + let lpMintSupply = unpackMint( + storedAmm.lpMint, + await this.provider.connection.getAccountInfo(storedAmm.lpMint) + ).supply; + + let quoteAmountCasted: BN | undefined; + let baseAmountCasted: BN | undefined; + + if (quoteAmount != undefined) { + let quoteDecimals = unpackMint( + storedAmm.quoteMint, + await this.provider.connection.getAccountInfo(storedAmm.quoteMint) + ).decimals; + quoteAmountCasted = new BN(quoteAmount).mul( + new BN(10).pow(new BN(quoteDecimals)) + ); + } + + if (baseAmount != undefined) { + let baseDecimals = unpackMint( + storedAmm.baseMint, + await this.provider.connection.getAccountInfo(storedAmm.baseMint) + ).decimals; + baseAmountCasted = new BN(baseAmount).mul( + new BN(10).pow(new BN(baseDecimals)) + ); + } + + if (lpMintSupply == 0n) { + if (quoteAmount == undefined || baseAmount == undefined) { + throw new Error( + "No pool created yet, you need to specify both base and quote" + ); + } + + // console.log(quoteAmountCasted?.toString()); + // console.log(baseAmountCasted?.toString()) + + return await this.addLiquidityIx( + amm, + storedAmm.baseMint, + storedAmm.quoteMint, + quoteAmountCasted as BN, + baseAmountCasted as BN, + new BN(0) + ).rpc(); + } + + // quoteAmount == undefined ? undefined : new BN(quoteAmount); + // let baseAmountCasted: BN | undefined = + // baseAmount == undefined ? undefined : new BN(baseAmount); + + let sim = this.simulateAddLiquidity( + storedAmm.baseAmount, + storedAmm.quoteAmount, + Number(lpMintSupply), + baseAmountCasted, + quoteAmountCasted + ); + + await this.addLiquidityIx( + amm, + storedAmm.baseMint, + storedAmm.quoteMint, + sim.quoteAmount, + sim.baseAmount, + sim.expectedLpTokens + ).rpc(); + } + + addLiquidityIx( + amm: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + quoteAmount: BN, + maxBaseAmount: BN, + minLpTokens: BN, + user: PublicKey = this.provider.publicKey + ) { + const [lpMint] = getAmmLpMintAddr(this.program.programId, amm); + + const userLpAccount = getAssociatedTokenAddressSync(lpMint, user); + + return this.program.methods + .addLiquidity({ + quoteAmount, + maxBaseAmount, + minLpTokens, + }) + .accounts({ + user, + amm, + lpMint, + userLpAccount, + userBaseAccount: getAssociatedTokenAddressSync(baseMint, user), + userQuoteAccount: getAssociatedTokenAddressSync(quoteMint, user), + vaultAtaBase: getAssociatedTokenAddressSync(baseMint, amm, true), + vaultAtaQuote: getAssociatedTokenAddressSync(quoteMint, amm, true), + }) + .preInstructions([ + createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + userLpAccount, + this.provider.publicKey, + lpMint + ), + ]); + } + + removeLiquidityIx( + ammAddr: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + lpTokensToBurn: BN, + minBaseAmount: BN, + minQuoteAmount: BN + ) { + const [lpMint] = getAmmLpMintAddr(this.program.programId, ammAddr); + + return this.program.methods + .removeLiquidity({ + lpTokensToBurn, + minBaseAmount, + minQuoteAmount, + }) + .accounts({ + user: this.provider.publicKey, + amm: ammAddr, + lpMint, + userLpAccount: getAssociatedTokenAddressSync( + lpMint, + this.provider.publicKey + ), + userBaseAccount: getAssociatedTokenAddressSync( + baseMint, + this.provider.publicKey + ), + userQuoteAccount: getAssociatedTokenAddressSync( + quoteMint, + this.provider.publicKey + ), + vaultAtaBase: getAssociatedTokenAddressSync(baseMint, ammAddr, true), + vaultAtaQuote: getAssociatedTokenAddressSync(quoteMint, ammAddr, true), + }); + } + + async swap( + amm: PublicKey, + swapType: SwapType, + inputAmount: number, + outputAmountMin: number + ) { + const storedAmm = await this.getAmm(amm); + + let quoteDecimals = await this.getDecimals(storedAmm.quoteMint); + let baseDecimals = await this.getDecimals(storedAmm.baseMint); + + let inputAmountScaled: BN; + let outputAmountMinScaled: BN; + if (swapType.buy) { + inputAmountScaled = PriceMath.scale(inputAmount, quoteDecimals); + outputAmountMinScaled = PriceMath.scale(outputAmountMin, baseDecimals); + } else { + inputAmountScaled = PriceMath.scale(inputAmount, baseDecimals); + outputAmountMinScaled = PriceMath.scale(outputAmountMin, quoteDecimals); + } + + return await this.swapIx( + amm, + storedAmm.baseMint, + storedAmm.quoteMint, + swapType, + inputAmountScaled, + outputAmountMinScaled + ).rpc(); + } + + swapIx( + amm: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + swapType: SwapType, + inputAmount: BN, + outputAmountMin: BN + ) { + const receivingToken = swapType.buy ? baseMint : quoteMint; + + return this.program.methods + .swap({ + swapType, + inputAmount, + outputAmountMin, + }) + .accounts({ + user: this.provider.publicKey, + amm, + userBaseAccount: getAssociatedTokenAddressSync( + baseMint, + this.provider.publicKey, + true + ), + userQuoteAccount: getAssociatedTokenAddressSync( + quoteMint, + this.provider.publicKey, + true + ), + vaultAtaBase: getAssociatedTokenAddressSync(baseMint, amm, true), + vaultAtaQuote: getAssociatedTokenAddressSync(quoteMint, amm, true), + }) + .preInstructions([ + // create the receiving token account if it doesn't exist + createAssociatedTokenAccountIdempotentInstruction( + this.provider.publicKey, + getAssociatedTokenAddressSync( + receivingToken, + this.provider.publicKey + ), + this.provider.publicKey, + receivingToken + ), + ]); + } + + async crankThatTwap(amm: PublicKey) { + return this.crankThatTwapIx(amm).rpc(); + } + + crankThatTwapIx(amm: PublicKey) { + return this.program.methods.crankThatTwap().accounts({ + amm, + }); + } + + // getter functions + + // async getLTWAP(ammAddr: PublicKey): Promise { + // const amm = await this.program.account.amm.fetch(ammAddr); + // return amm.twapLastObservationUq64X32 + // .div(new BN(2).pow(new BN(32))) + // .toNumber(); + // } + + async getAmm(amm: PublicKey): Promise { + return await this.program.account.amm.fetch(amm); + } + + getTwap(amm: AmmAccount): BN { + return amm.oracle.aggregator.div( + amm.oracle.lastUpdatedSlot.sub(amm.createdAtSlot) + ); + } + + simulateAddLiquidity( + baseReserves: BN, + quoteReserves: BN, + lpMintSupply: number, + baseAmount?: BN, + quoteAmount?: BN, + slippageBps?: BN + ): AddLiquiditySimulation { + if (lpMintSupply == 0) { + throw new Error( + "This AMM doesn't have existing liquidity so we can't fill in the blanks" + ); + } + + if (baseAmount == undefined && quoteAmount == undefined) { + throw new Error("Must specify either a base amount or a quote amount"); + } + + let expectedLpTokens: BN; + + if (quoteAmount == undefined) { + quoteAmount = baseAmount?.mul(quoteReserves).div(baseReserves); + } + baseAmount = quoteAmount?.mul(baseReserves).div(quoteReserves).addn(1); + + expectedLpTokens = quoteAmount + ?.mul(new BN(lpMintSupply)) + .div(quoteReserves) as BN; + + let minLpTokens, maxBaseAmount; + if (slippageBps) { + minLpTokens = PriceMath.subtractSlippage(expectedLpTokens, slippageBps); + maxBaseAmount = PriceMath.addSlippage(baseAmount as BN, slippageBps); + } + + return { + quoteAmount: quoteAmount as BN, + baseAmount: baseAmount as BN, + expectedLpTokens, + minLpTokens, + maxBaseAmount, + }; + } + + simulateSwap( + inputAmount: BN, + swapType: SwapType, + baseReserves: BN, + quoteReserves: BN, + slippageBps?: BN + ): SwapSimulation { + if (baseReserves.eqn(0) || quoteReserves.eqn(0)) { + throw new Error("reserves must be non-zero"); + } + + let inputReserves, outputReserves: BN; + if (swapType.buy) { + inputReserves = quoteReserves; + outputReserves = baseReserves; + } else { + inputReserves = baseReserves; + outputReserves = quoteReserves; + } + + let inputAmountWithFee: BN = inputAmount.muln(990); + + let numerator: BN = inputAmountWithFee.mul(outputReserves); + let denominator: BN = inputReserves.muln(1000).add(inputAmountWithFee); + + let expectedOut = numerator.div(denominator); + let minExpectedOut; + if (slippageBps) { + minExpectedOut = PriceMath.subtractSlippage(expectedOut, slippageBps); + } + + let newBaseReserves, newQuoteReserves: BN; + if (swapType.buy) { + newBaseReserves = baseReserves.sub(expectedOut); + newQuoteReserves = quoteReserves.add(inputAmount); + } else { + newBaseReserves = baseReserves.add(inputAmount); + newQuoteReserves = quoteReserves.sub(expectedOut); + } + + return { + expectedOut, + newBaseReserves, + newQuoteReserves, + minExpectedOut, + }; + } + + simulateRemoveLiquidity( + lpTokensToBurn: BN, + baseReserves: BN, + quoteReserves: BN, + lpTotalSupply: BN, + slippageBps?: BN + ): RemoveLiquiditySimulation { + const expectedBaseOut = lpTokensToBurn.mul(baseReserves).div(lpTotalSupply); + const expectedQuoteOut = lpTokensToBurn + .mul(quoteReserves) + .div(lpTotalSupply); + + let minBaseOut, minQuoteOut; + if (slippageBps) { + minBaseOut = PriceMath.subtractSlippage(expectedBaseOut, slippageBps); + minQuoteOut = PriceMath.subtractSlippage(expectedQuoteOut, slippageBps); + } + + return { + expectedBaseOut, + expectedQuoteOut, + minBaseOut, + minQuoteOut, + }; + } + + async getDecimals(mint: PublicKey): Promise { + return unpackMint(mint, await this.provider.connection.getAccountInfo(mint)) + .decimals; + } +} diff --git a/sdk/src/AutocratClient.ts b/sdk/src/v0.4/AutocratClient.ts similarity index 97% rename from sdk/src/AutocratClient.ts rename to sdk/src/v0.4/AutocratClient.ts index e71db721..eeef249e 100644 --- a/sdk/src/AutocratClient.ts +++ b/sdk/src/v0.4/AutocratClient.ts @@ -9,14 +9,14 @@ import { Transaction, TransactionInstruction, } from "@solana/web3.js"; -import { PriceMath } from "./utils/priceMath"; -import { ProposalInstruction, InitializeDaoParams } from "./types"; +import { PriceMath } from "./utils/priceMath.js"; +import { ProposalInstruction, InitializeDaoParams } from "./types/index.js"; -import { Autocrat, IDL as AutocratIDL } from "./types/autocrat"; +import { Autocrat, IDL as AutocratIDL } from "./types/autocrat.js"; import { ConditionalVault, IDL as ConditionalVaultIDL, -} from "./types/conditional_vault"; +} from "./types/conditional_vault.js"; import BN from "bn.js"; import { @@ -25,7 +25,7 @@ import { CONDITIONAL_VAULT_PROGRAM_ID, MAINNET_USDC, USDC_DECIMALS, -} from "./constants"; +} from "./constants.js"; import { DEFAULT_CU_PRICE, InstructionUtils, @@ -39,15 +39,16 @@ import { getVaultAddr, getVaultFinalizeMintAddr, getVaultRevertMintAddr, -} from "./utils"; -import { ConditionalVaultClient } from "./ConditionalVaultClient"; -import { AmmClient } from "./AmmClient"; +} from "./utils/index.js"; +import { ConditionalVaultClient } from "./ConditionalVaultClient.js"; +import { AmmClient } from "./AmmClient.js"; import { createAssociatedTokenAccountIdempotentInstruction, getAssociatedTokenAddressSync, unpackMint, } from "@solana/spl-token"; import { sha256 } from "@noble/hashes/sha256"; +import { Dao, Proposal } from "./types/index.js"; export type CreateClientParams = { provider: AnchorProvider; @@ -110,11 +111,11 @@ export class AutocratClient { ); } - async getProposal(proposal: PublicKey) { + async getProposal(proposal: PublicKey): Promise { return this.autocrat.account.proposal.fetch(proposal); } - async getDao(dao: PublicKey) { + async getDao(dao: PublicKey): Promise { return this.autocrat.account.dao.fetch(dao); } diff --git a/sdk/src/ConditionalVaultClient.ts b/sdk/src/v0.4/ConditionalVaultClient.ts similarity index 98% rename from sdk/src/ConditionalVaultClient.ts rename to sdk/src/v0.4/ConditionalVaultClient.ts index d1464ea6..f798eb93 100644 --- a/sdk/src/ConditionalVaultClient.ts +++ b/sdk/src/v0.4/ConditionalVaultClient.ts @@ -9,13 +9,13 @@ import { import { ConditionalVault, IDL as ConditionalVaultIDL, -} from "./types/conditional_vault"; +} from "./types/conditional_vault.js"; import BN from "bn.js"; import { CONDITIONAL_VAULT_PROGRAM_ID, MPL_TOKEN_METADATA_PROGRAM_ID, -} from "./constants"; +} from "./constants.js"; import { getQuestionAddr, getMetadataAddr, @@ -23,8 +23,7 @@ import { getVaultFinalizeMintAddr, getVaultRevertMintAddr, getConditionalTokenMintAddr, -} from "./utils"; -import { MethodsBuilder } from "@coral-xyz/anchor/dist/cjs/program/namespace/methods"; +} from "./utils/index.js"; import { createAssociatedTokenAccountIdempotentInstruction, createAssociatedTokenAccountInstruction, @@ -117,7 +116,7 @@ export class ConditionalVaultClient { question: PublicKey, underlyingTokenMint: PublicKey, numOutcomes: number - ): MethodsBuilder { + ) { const [vault] = getVaultAddr( this.vaultProgram.programId, question, diff --git a/sdk/src/v0.4/constants.ts b/sdk/src/v0.4/constants.ts new file mode 100644 index 00000000..66ef1523 --- /dev/null +++ b/sdk/src/v0.4/constants.ts @@ -0,0 +1,26 @@ +import { PublicKey } from "@solana/web3.js"; + +export const AUTOCRAT_PROGRAM_ID = new PublicKey( + "autoQP9RmUNkzzKRXsMkWicDVZ3h29vvyMDcAYjCxxg" +); +export const AMM_PROGRAM_ID = new PublicKey( + "AMM5G2nxuKUwCLRYTW7qqEwuoqCtNSjtbipwEmm2g8bH" +); +export const CONDITIONAL_VAULT_PROGRAM_ID = new PublicKey( + "VAU1T7S5UuEHmMvXtXMVmpEoQtZ2ya7eRb7gcN47wDp" +); + +export const MPL_TOKEN_METADATA_PROGRAM_ID = new PublicKey( + "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" +); + +export const META_MINT = new PublicKey( + "3gN1WVEJwSHNWjo7hr87DgZp6zkf8kWgAJD29DmfE2Gr" +); +export const MAINNET_USDC = new PublicKey( + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" +); + +export const USDC_DECIMALS = 6; + +export const AUTOCRAT_LUTS: PublicKey[] = []; diff --git a/sdk/src/v0.4/index.ts b/sdk/src/v0.4/index.ts new file mode 100644 index 00000000..1aa39fe5 --- /dev/null +++ b/sdk/src/v0.4/index.ts @@ -0,0 +1,6 @@ +export * from "./types/index.js"; +export * from "./utils/index.js"; +export * from "./constants.js"; +export * from "./AmmClient.js"; +export * from "./AutocratClient.js"; +export * from "./ConditionalVaultClient.js"; diff --git a/sdk/src/v0.4/types/amm.ts b/sdk/src/v0.4/types/amm.ts new file mode 100644 index 00000000..0a7d6669 --- /dev/null +++ b/sdk/src/v0.4/types/amm.ts @@ -0,0 +1,1083 @@ +export type Amm = { + version: "0.3.0"; + name: "amm"; + instructions: [ + { + name: "createAmm"; + accounts: [ + { + name: "user"; + isMut: true; + isSigner: true; + }, + { + name: "amm"; + isMut: true; + isSigner: false; + }, + { + name: "lpMint"; + isMut: true; + isSigner: false; + }, + { + name: "baseMint"; + isMut: false; + isSigner: false; + }, + { + name: "quoteMint"; + isMut: false; + isSigner: false; + }, + { + name: "vaultAtaBase"; + isMut: true; + isSigner: false; + }, + { + name: "vaultAtaQuote"; + isMut: true; + isSigner: false; + }, + { + name: "associatedTokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "args"; + type: { + defined: "CreateAmmArgs"; + }; + } + ]; + }, + { + name: "addLiquidity"; + accounts: [ + { + name: "user"; + isMut: true; + isSigner: true; + }, + { + name: "amm"; + isMut: true; + isSigner: false; + }, + { + name: "lpMint"; + isMut: true; + isSigner: false; + }, + { + name: "userLpAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userBaseAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userQuoteAccount"; + isMut: true; + isSigner: false; + }, + { + name: "vaultAtaBase"; + isMut: true; + isSigner: false; + }, + { + name: "vaultAtaQuote"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "args"; + type: { + defined: "AddLiquidityArgs"; + }; + } + ]; + }, + { + name: "removeLiquidity"; + accounts: [ + { + name: "user"; + isMut: true; + isSigner: true; + }, + { + name: "amm"; + isMut: true; + isSigner: false; + }, + { + name: "lpMint"; + isMut: true; + isSigner: false; + }, + { + name: "userLpAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userBaseAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userQuoteAccount"; + isMut: true; + isSigner: false; + }, + { + name: "vaultAtaBase"; + isMut: true; + isSigner: false; + }, + { + name: "vaultAtaQuote"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "args"; + type: { + defined: "RemoveLiquidityArgs"; + }; + } + ]; + }, + { + name: "swap"; + accounts: [ + { + name: "user"; + isMut: true; + isSigner: true; + }, + { + name: "amm"; + isMut: true; + isSigner: false; + }, + { + name: "userBaseAccount"; + isMut: true; + isSigner: false; + }, + { + name: "userQuoteAccount"; + isMut: true; + isSigner: false; + }, + { + name: "vaultAtaBase"; + isMut: true; + isSigner: false; + }, + { + name: "vaultAtaQuote"; + isMut: true; + isSigner: false; + }, + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: "args"; + type: { + defined: "SwapArgs"; + }; + } + ]; + }, + { + name: "crankThatTwap"; + accounts: [ + { + name: "amm"; + isMut: true; + isSigner: false; + } + ]; + args: []; + } + ]; + accounts: [ + { + name: "amm"; + type: { + kind: "struct"; + fields: [ + { + name: "bump"; + type: "u8"; + }, + { + name: "createdAtSlot"; + type: "u64"; + }, + { + name: "lpMint"; + type: "publicKey"; + }, + { + name: "baseMint"; + type: "publicKey"; + }, + { + name: "quoteMint"; + type: "publicKey"; + }, + { + name: "baseMintDecimals"; + type: "u8"; + }, + { + name: "quoteMintDecimals"; + type: "u8"; + }, + { + name: "baseAmount"; + type: "u64"; + }, + { + name: "quoteAmount"; + type: "u64"; + }, + { + name: "oracle"; + type: { + defined: "TwapOracle"; + }; + } + ]; + }; + } + ]; + types: [ + { + name: "AddLiquidityArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "quoteAmount"; + docs: ["How much quote token you will deposit to the pool"]; + type: "u64"; + }, + { + name: "maxBaseAmount"; + docs: ["The maximum base token you will deposit to the pool"]; + type: "u64"; + }, + { + name: "minLpTokens"; + docs: ["The minimum LP token you will get back"]; + type: "u64"; + } + ]; + }; + }, + { + name: "CreateAmmArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "twapInitialObservation"; + type: "u128"; + }, + { + name: "twapMaxObservationChangePerUpdate"; + type: "u128"; + } + ]; + }; + }, + { + name: "RemoveLiquidityArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "lpTokensToBurn"; + type: "u64"; + }, + { + name: "minQuoteAmount"; + type: "u64"; + }, + { + name: "minBaseAmount"; + type: "u64"; + } + ]; + }; + }, + { + name: "SwapArgs"; + type: { + kind: "struct"; + fields: [ + { + name: "swapType"; + type: { + defined: "SwapType"; + }; + }, + { + name: "inputAmount"; + type: "u64"; + }, + { + name: "outputAmountMin"; + type: "u64"; + } + ]; + }; + }, + { + name: "TwapOracle"; + type: { + kind: "struct"; + fields: [ + { + name: "lastUpdatedSlot"; + type: "u64"; + }, + { + name: "lastPrice"; + docs: [ + "A price is the number of quote units per base unit multiplied by 1e12.", + "You cannot simply divide by 1e12 to get a price you can display in the UI", + "because the base and quote decimals may be different. Instead, do:", + "ui_price = (price * (10**(base_decimals - quote_decimals))) / 1e12" + ]; + type: "u128"; + }, + { + name: "lastObservation"; + docs: [ + "If we did a raw TWAP over prices, someone could push the TWAP heavily with", + "a few extremely large outliers. So we use observations, which can only move", + "by `max_observation_change_per_update` per update." + ]; + type: "u128"; + }, + { + name: "aggregator"; + docs: [ + "Running sum of slots_per_last_update * last_observation.", + "", + "Assuming latest observations are as big as possible (u64::MAX * 1e12),", + "we can store 18 million slots worth of observations, which turns out to", + "be ~85 days worth of slots.", + "", + "Assuming that latest observations are 100x smaller than they could theoretically", + "be, we can store 8500 days (23 years) worth of them. Even this is a very", + "very conservative assumption - META/USDC prices should be between 1e9 and", + "1e15, which would overflow after 1e15 years worth of slots.", + "", + "So in the case of an overflow, the aggregator rolls back to 0. It's the", + "client's responsibility to sanity check the assets or to handle an", + "aggregator at t2 being smaller than an aggregator at t1." + ]; + type: "u128"; + }, + { + name: "maxObservationChangePerUpdate"; + docs: ["The most that an observation can change per update."]; + type: "u128"; + }, + { + name: "initialObservation"; + docs: ["What the initial `latest_observation` is set to."]; + type: "u128"; + } + ]; + }; + }, + { + name: "SwapType"; + type: { + kind: "enum"; + variants: [ + { + name: "Buy"; + }, + { + name: "Sell"; + } + ]; + }; + } + ]; + errors: [ + { + code: 6000; + name: "NoSlotsPassed"; + msg: "Can't get a TWAP before some observations have been stored"; + }, + { + code: 6001; + name: "NoReserves"; + msg: "Can't swap through a pool without token reserves on either side"; + }, + { + code: 6002; + name: "InputAmountOverflow"; + msg: "Input token amount is too large for a swap, causes overflow"; + }, + { + code: 6003; + name: "AddLiquidityCalculationError"; + msg: "Add liquidity calculation error"; + }, + { + code: 6004; + name: "DecimalScaleError"; + msg: "Error in decimal scale conversion"; + }, + { + code: 6005; + name: "SameTokenMints"; + msg: "You can't create an AMM pool where the token mints are the same"; + }, + { + code: 6006; + name: "SwapSlippageExceeded"; + msg: "A user wouldn't have gotten back their `output_amount_min`, reverting"; + }, + { + code: 6007; + name: "InsufficientBalance"; + msg: "The user had insufficient balance to do this"; + }, + { + code: 6008; + name: "ZeroLiquidityRemove"; + msg: "Must remove a non-zero amount of liquidity"; + }, + { + code: 6009; + name: "ZeroLiquidityToAdd"; + msg: "Cannot add liquidity with 0 tokens on either side"; + }, + { + code: 6010; + name: "ZeroMinLpTokens"; + msg: "Must specify a non-zero `min_lp_tokens` when adding to an existing pool"; + }, + { + code: 6011; + name: "AddLiquiditySlippageExceeded"; + msg: "LP wouldn't have gotten back `lp_token_min`"; + }, + { + code: 6012; + name: "AddLiquidityMaxBaseExceeded"; + msg: "LP would have spent more than `max_base_amount`"; + }, + { + code: 6013; + name: "InsufficientQuoteAmount"; + msg: "`quote_amount` must be greater than 100000000 when initializing a pool"; + }, + { + code: 6014; + name: "ZeroSwapAmount"; + msg: "Users must swap a non-zero amount"; + }, + { + code: 6015; + name: "ConstantProductInvariantFailed"; + msg: "K should always be increasing"; + }, + { + code: 6016; + name: "CastingOverflow"; + msg: "Casting has caused an overflow"; + } + ]; +}; + +export const IDL: Amm = { + version: "0.3.0", + name: "amm", + instructions: [ + { + name: "createAmm", + accounts: [ + { + name: "user", + isMut: true, + isSigner: true, + }, + { + name: "amm", + isMut: true, + isSigner: false, + }, + { + name: "lpMint", + isMut: true, + isSigner: false, + }, + { + name: "baseMint", + isMut: false, + isSigner: false, + }, + { + name: "quoteMint", + isMut: false, + isSigner: false, + }, + { + name: "vaultAtaBase", + isMut: true, + isSigner: false, + }, + { + name: "vaultAtaQuote", + isMut: true, + isSigner: false, + }, + { + name: "associatedTokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "CreateAmmArgs", + }, + }, + ], + }, + { + name: "addLiquidity", + accounts: [ + { + name: "user", + isMut: true, + isSigner: true, + }, + { + name: "amm", + isMut: true, + isSigner: false, + }, + { + name: "lpMint", + isMut: true, + isSigner: false, + }, + { + name: "userLpAccount", + isMut: true, + isSigner: false, + }, + { + name: "userBaseAccount", + isMut: true, + isSigner: false, + }, + { + name: "userQuoteAccount", + isMut: true, + isSigner: false, + }, + { + name: "vaultAtaBase", + isMut: true, + isSigner: false, + }, + { + name: "vaultAtaQuote", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "AddLiquidityArgs", + }, + }, + ], + }, + { + name: "removeLiquidity", + accounts: [ + { + name: "user", + isMut: true, + isSigner: true, + }, + { + name: "amm", + isMut: true, + isSigner: false, + }, + { + name: "lpMint", + isMut: true, + isSigner: false, + }, + { + name: "userLpAccount", + isMut: true, + isSigner: false, + }, + { + name: "userBaseAccount", + isMut: true, + isSigner: false, + }, + { + name: "userQuoteAccount", + isMut: true, + isSigner: false, + }, + { + name: "vaultAtaBase", + isMut: true, + isSigner: false, + }, + { + name: "vaultAtaQuote", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "RemoveLiquidityArgs", + }, + }, + ], + }, + { + name: "swap", + accounts: [ + { + name: "user", + isMut: true, + isSigner: true, + }, + { + name: "amm", + isMut: true, + isSigner: false, + }, + { + name: "userBaseAccount", + isMut: true, + isSigner: false, + }, + { + name: "userQuoteAccount", + isMut: true, + isSigner: false, + }, + { + name: "vaultAtaBase", + isMut: true, + isSigner: false, + }, + { + name: "vaultAtaQuote", + isMut: true, + isSigner: false, + }, + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "args", + type: { + defined: "SwapArgs", + }, + }, + ], + }, + { + name: "crankThatTwap", + accounts: [ + { + name: "amm", + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + ], + accounts: [ + { + name: "amm", + type: { + kind: "struct", + fields: [ + { + name: "bump", + type: "u8", + }, + { + name: "createdAtSlot", + type: "u64", + }, + { + name: "lpMint", + type: "publicKey", + }, + { + name: "baseMint", + type: "publicKey", + }, + { + name: "quoteMint", + type: "publicKey", + }, + { + name: "baseMintDecimals", + type: "u8", + }, + { + name: "quoteMintDecimals", + type: "u8", + }, + { + name: "baseAmount", + type: "u64", + }, + { + name: "quoteAmount", + type: "u64", + }, + { + name: "oracle", + type: { + defined: "TwapOracle", + }, + }, + ], + }, + }, + ], + types: [ + { + name: "AddLiquidityArgs", + type: { + kind: "struct", + fields: [ + { + name: "quoteAmount", + docs: ["How much quote token you will deposit to the pool"], + type: "u64", + }, + { + name: "maxBaseAmount", + docs: ["The maximum base token you will deposit to the pool"], + type: "u64", + }, + { + name: "minLpTokens", + docs: ["The minimum LP token you will get back"], + type: "u64", + }, + ], + }, + }, + { + name: "CreateAmmArgs", + type: { + kind: "struct", + fields: [ + { + name: "twapInitialObservation", + type: "u128", + }, + { + name: "twapMaxObservationChangePerUpdate", + type: "u128", + }, + ], + }, + }, + { + name: "RemoveLiquidityArgs", + type: { + kind: "struct", + fields: [ + { + name: "lpTokensToBurn", + type: "u64", + }, + { + name: "minQuoteAmount", + type: "u64", + }, + { + name: "minBaseAmount", + type: "u64", + }, + ], + }, + }, + { + name: "SwapArgs", + type: { + kind: "struct", + fields: [ + { + name: "swapType", + type: { + defined: "SwapType", + }, + }, + { + name: "inputAmount", + type: "u64", + }, + { + name: "outputAmountMin", + type: "u64", + }, + ], + }, + }, + { + name: "TwapOracle", + type: { + kind: "struct", + fields: [ + { + name: "lastUpdatedSlot", + type: "u64", + }, + { + name: "lastPrice", + docs: [ + "A price is the number of quote units per base unit multiplied by 1e12.", + "You cannot simply divide by 1e12 to get a price you can display in the UI", + "because the base and quote decimals may be different. Instead, do:", + "ui_price = (price * (10**(base_decimals - quote_decimals))) / 1e12", + ], + type: "u128", + }, + { + name: "lastObservation", + docs: [ + "If we did a raw TWAP over prices, someone could push the TWAP heavily with", + "a few extremely large outliers. So we use observations, which can only move", + "by `max_observation_change_per_update` per update.", + ], + type: "u128", + }, + { + name: "aggregator", + docs: [ + "Running sum of slots_per_last_update * last_observation.", + "", + "Assuming latest observations are as big as possible (u64::MAX * 1e12),", + "we can store 18 million slots worth of observations, which turns out to", + "be ~85 days worth of slots.", + "", + "Assuming that latest observations are 100x smaller than they could theoretically", + "be, we can store 8500 days (23 years) worth of them. Even this is a very", + "very conservative assumption - META/USDC prices should be between 1e9 and", + "1e15, which would overflow after 1e15 years worth of slots.", + "", + "So in the case of an overflow, the aggregator rolls back to 0. It's the", + "client's responsibility to sanity check the assets or to handle an", + "aggregator at t2 being smaller than an aggregator at t1.", + ], + type: "u128", + }, + { + name: "maxObservationChangePerUpdate", + docs: ["The most that an observation can change per update."], + type: "u128", + }, + { + name: "initialObservation", + docs: ["What the initial `latest_observation` is set to."], + type: "u128", + }, + ], + }, + }, + { + name: "SwapType", + type: { + kind: "enum", + variants: [ + { + name: "Buy", + }, + { + name: "Sell", + }, + ], + }, + }, + ], + errors: [ + { + code: 6000, + name: "NoSlotsPassed", + msg: "Can't get a TWAP before some observations have been stored", + }, + { + code: 6001, + name: "NoReserves", + msg: "Can't swap through a pool without token reserves on either side", + }, + { + code: 6002, + name: "InputAmountOverflow", + msg: "Input token amount is too large for a swap, causes overflow", + }, + { + code: 6003, + name: "AddLiquidityCalculationError", + msg: "Add liquidity calculation error", + }, + { + code: 6004, + name: "DecimalScaleError", + msg: "Error in decimal scale conversion", + }, + { + code: 6005, + name: "SameTokenMints", + msg: "You can't create an AMM pool where the token mints are the same", + }, + { + code: 6006, + name: "SwapSlippageExceeded", + msg: "A user wouldn't have gotten back their `output_amount_min`, reverting", + }, + { + code: 6007, + name: "InsufficientBalance", + msg: "The user had insufficient balance to do this", + }, + { + code: 6008, + name: "ZeroLiquidityRemove", + msg: "Must remove a non-zero amount of liquidity", + }, + { + code: 6009, + name: "ZeroLiquidityToAdd", + msg: "Cannot add liquidity with 0 tokens on either side", + }, + { + code: 6010, + name: "ZeroMinLpTokens", + msg: "Must specify a non-zero `min_lp_tokens` when adding to an existing pool", + }, + { + code: 6011, + name: "AddLiquiditySlippageExceeded", + msg: "LP wouldn't have gotten back `lp_token_min`", + }, + { + code: 6012, + name: "AddLiquidityMaxBaseExceeded", + msg: "LP would have spent more than `max_base_amount`", + }, + { + code: 6013, + name: "InsufficientQuoteAmount", + msg: "`quote_amount` must be greater than 100000000 when initializing a pool", + }, + { + code: 6014, + name: "ZeroSwapAmount", + msg: "Users must swap a non-zero amount", + }, + { + code: 6015, + name: "ConstantProductInvariantFailed", + msg: "K should always be increasing", + }, + { + code: 6016, + name: "CastingOverflow", + msg: "Casting has caused an overflow", + }, + ], +}; diff --git a/sdk/src/types/autocrat.ts b/sdk/src/v0.4/types/autocrat.ts similarity index 100% rename from sdk/src/types/autocrat.ts rename to sdk/src/v0.4/types/autocrat.ts diff --git a/sdk/src/v0.4/types/autocrat_migrator.ts b/sdk/src/v0.4/types/autocrat_migrator.ts new file mode 100644 index 00000000..05f5d3fc --- /dev/null +++ b/sdk/src/v0.4/types/autocrat_migrator.ts @@ -0,0 +1,237 @@ +export type AutocratMigrator = { + version: "0.1.0"; + name: "autocrat_migrator"; + instructions: [ + { + name: "multiTransfer2"; + accounts: [ + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "authority"; + isMut: true; + isSigner: true; + }, + { + name: "from0"; + isMut: true; + isSigner: false; + }, + { + name: "to0"; + isMut: true; + isSigner: false; + }, + { + name: "from1"; + isMut: true; + isSigner: false; + }, + { + name: "to1"; + isMut: true; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "lamportReceiver"; + isMut: true; + isSigner: false; + } + ]; + args: []; + }, + { + name: "multiTransfer4"; + accounts: [ + { + name: "tokenProgram"; + isMut: false; + isSigner: false; + }, + { + name: "authority"; + isMut: true; + isSigner: true; + }, + { + name: "from0"; + isMut: true; + isSigner: false; + }, + { + name: "to0"; + isMut: true; + isSigner: false; + }, + { + name: "from1"; + isMut: true; + isSigner: false; + }, + { + name: "to1"; + isMut: true; + isSigner: false; + }, + { + name: "from2"; + isMut: true; + isSigner: false; + }, + { + name: "to2"; + isMut: true; + isSigner: false; + }, + { + name: "from3"; + isMut: true; + isSigner: false; + }, + { + name: "to3"; + isMut: true; + isSigner: false; + }, + { + name: "systemProgram"; + isMut: false; + isSigner: false; + }, + { + name: "lamportReceiver"; + isMut: true; + isSigner: false; + } + ]; + args: []; + } + ]; +}; + +export const IDL: AutocratMigrator = { + version: "0.1.0", + name: "autocrat_migrator", + instructions: [ + { + name: "multiTransfer2", + accounts: [ + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "authority", + isMut: true, + isSigner: true, + }, + { + name: "from0", + isMut: true, + isSigner: false, + }, + { + name: "to0", + isMut: true, + isSigner: false, + }, + { + name: "from1", + isMut: true, + isSigner: false, + }, + { + name: "to1", + isMut: true, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "lamportReceiver", + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + { + name: "multiTransfer4", + accounts: [ + { + name: "tokenProgram", + isMut: false, + isSigner: false, + }, + { + name: "authority", + isMut: true, + isSigner: true, + }, + { + name: "from0", + isMut: true, + isSigner: false, + }, + { + name: "to0", + isMut: true, + isSigner: false, + }, + { + name: "from1", + isMut: true, + isSigner: false, + }, + { + name: "to1", + isMut: true, + isSigner: false, + }, + { + name: "from2", + isMut: true, + isSigner: false, + }, + { + name: "to2", + isMut: true, + isSigner: false, + }, + { + name: "from3", + isMut: true, + isSigner: false, + }, + { + name: "to3", + isMut: true, + isSigner: false, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + { + name: "lamportReceiver", + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + ], +}; diff --git a/sdk/src/types/conditional_vault.ts b/sdk/src/v0.4/types/conditional_vault.ts similarity index 100% rename from sdk/src/types/conditional_vault.ts rename to sdk/src/v0.4/types/conditional_vault.ts diff --git a/sdk/src/v0.4/types/index.ts b/sdk/src/v0.4/types/index.ts new file mode 100644 index 00000000..472e02bf --- /dev/null +++ b/sdk/src/v0.4/types/index.ts @@ -0,0 +1,34 @@ +import { Autocrat } from "./autocrat.js"; +export { Autocrat, IDL as AutocratIDL } from "./autocrat.js"; + +import { Amm } from "./amm.js"; +export { Amm, IDL as AmmIDL } from "./amm.js"; + +import { ConditionalVault } from "./conditional_vault.js"; +export { + ConditionalVault, + IDL as ConditionalVaultIDL, +} from "./conditional_vault.js"; + +export { LowercaseKeys } from "./utils.js"; + +import type { IdlAccounts, IdlTypes } from "@coral-xyz/anchor"; +import { PublicKey } from "@solana/web3.js"; + +export type InitializeDaoParams = IdlTypes["InitializeDaoParams"]; +export type UpdateDaoParams = IdlTypes["UpdateDaoParams"]; +export type ProposalInstruction = IdlTypes["ProposalInstruction"]; + +export type Proposal = IdlAccounts["proposal"]; +export type ProposalWrapper = { + account: Proposal; + publicKey: PublicKey; +}; + +export type Dao = IdlAccounts["dao"]; +export type ProposalAccount = IdlAccounts["proposal"]; + +export type AmmAccount = IdlAccounts["amm"]; + +export type ConditionalVaultAccount = + IdlAccounts["conditionalVault"]; diff --git a/sdk/src/v0.4/types/optimistic_timelock.ts b/sdk/src/v0.4/types/optimistic_timelock.ts new file mode 100644 index 00000000..dda1fa46 --- /dev/null +++ b/sdk/src/v0.4/types/optimistic_timelock.ts @@ -0,0 +1,1023 @@ +export type OptimisticTimelock = { + version: "0.3.0"; + name: "optimistic_timelock"; + instructions: [ + { + name: "createTimelock"; + accounts: [ + { + name: "timelockSigner"; + isMut: false; + isSigner: false; + }, + { + name: "timelock"; + isMut: true; + isSigner: true; + } + ]; + args: [ + { + name: "authority"; + type: "publicKey"; + }, + { + name: "delayInSlots"; + type: "u64"; + }, + { + name: "enqueuers"; + type: { + vec: "publicKey"; + }; + }, + { + name: "enqueuerCooldownSlots"; + type: "u64"; + } + ]; + }, + { + name: "setDelayInSlots"; + accounts: [ + { + name: "timelockSigner"; + isMut: false; + isSigner: true; + }, + { + name: "timelock"; + isMut: true; + isSigner: false; + } + ]; + args: [ + { + name: "delayInSlots"; + type: "u64"; + } + ]; + }, + { + name: "setAuthority"; + accounts: [ + { + name: "timelockSigner"; + isMut: false; + isSigner: true; + }, + { + name: "timelock"; + isMut: true; + isSigner: false; + } + ]; + args: [ + { + name: "authority"; + type: "publicKey"; + } + ]; + }, + { + name: "setOptimisticProposerCooldownSlots"; + accounts: [ + { + name: "timelockSigner"; + isMut: false; + isSigner: true; + }, + { + name: "timelock"; + isMut: true; + isSigner: false; + } + ]; + args: [ + { + name: "cooldownSlots"; + type: "u64"; + } + ]; + }, + { + name: "addOptimisticProposer"; + accounts: [ + { + name: "timelockSigner"; + isMut: false; + isSigner: true; + }, + { + name: "timelock"; + isMut: true; + isSigner: false; + } + ]; + args: [ + { + name: "enqueuer"; + type: "publicKey"; + } + ]; + }, + { + name: "removeOptimisticProposer"; + accounts: [ + { + name: "timelockSigner"; + isMut: false; + isSigner: true; + }, + { + name: "timelock"; + isMut: true; + isSigner: false; + } + ]; + args: [ + { + name: "optimisticProposer"; + type: "publicKey"; + } + ]; + }, + { + name: "createTransactionBatch"; + accounts: [ + { + name: "transactionBatchAuthority"; + isMut: false; + isSigner: true; + }, + { + name: "timelock"; + isMut: false; + isSigner: false; + }, + { + name: "transactionBatch"; + isMut: true; + isSigner: true; + } + ]; + args: []; + }, + { + name: "addTransaction"; + accounts: [ + { + name: "transactionBatchAuthority"; + isMut: false; + isSigner: true; + }, + { + name: "transactionBatch"; + isMut: true; + isSigner: false; + } + ]; + args: [ + { + name: "programId"; + type: "publicKey"; + }, + { + name: "accounts"; + type: { + vec: { + defined: "TransactionAccount"; + }; + }; + }, + { + name: "data"; + type: "bytes"; + } + ]; + }, + { + name: "sealTransactionBatch"; + accounts: [ + { + name: "transactionBatchAuthority"; + isMut: false; + isSigner: true; + }, + { + name: "transactionBatch"; + isMut: true; + isSigner: false; + } + ]; + args: []; + }, + { + name: "enqueueTransactionBatch"; + accounts: [ + { + name: "authority"; + isMut: false; + isSigner: true; + }, + { + name: "timelock"; + isMut: true; + isSigner: false; + }, + { + name: "transactionBatch"; + isMut: true; + isSigner: false; + } + ]; + args: []; + }, + { + name: "cancelTransactionBatch"; + accounts: [ + { + name: "authority"; + isMut: false; + isSigner: true; + }, + { + name: "timelock"; + isMut: true; + isSigner: false; + }, + { + name: "transactionBatch"; + isMut: true; + isSigner: false; + } + ]; + args: []; + }, + { + name: "executeTransactionBatch"; + accounts: [ + { + name: "timelockSigner"; + isMut: false; + isSigner: false; + }, + { + name: "timelock"; + isMut: false; + isSigner: false; + }, + { + name: "transactionBatch"; + isMut: true; + isSigner: false; + } + ]; + args: []; + } + ]; + accounts: [ + { + name: "timelock"; + type: { + kind: "struct"; + fields: [ + { + name: "authority"; + type: "publicKey"; + }, + { + name: "signerBump"; + type: "u8"; + }, + { + name: "delayInSlots"; + type: "u64"; + }, + { + name: "optimisticProposers"; + type: { + vec: { + defined: "OptimisticProposer"; + }; + }; + }, + { + name: "optimisticProposerCooldownSlots"; + docs: [ + "The cooldown period for enqueuers to prevent spamming the timelock." + ]; + type: "u64"; + } + ]; + }; + }, + { + name: "transactionBatch"; + type: { + kind: "struct"; + fields: [ + { + name: "status"; + type: { + defined: "TransactionBatchStatus"; + }; + }, + { + name: "transactions"; + type: { + vec: { + defined: "Transaction"; + }; + }; + }, + { + name: "timelock"; + type: "publicKey"; + }, + { + name: "enqueuedSlot"; + type: "u64"; + }, + { + name: "transactionBatchAuthority"; + type: "publicKey"; + }, + { + name: "enqueuerType"; + type: { + defined: "AuthorityType"; + }; + } + ]; + }; + } + ]; + types: [ + { + name: "OptimisticProposer"; + type: { + kind: "struct"; + fields: [ + { + name: "pubkey"; + type: "publicKey"; + }, + { + name: "lastSlotEnqueued"; + type: "u64"; + } + ]; + }; + }, + { + name: "Transaction"; + type: { + kind: "struct"; + fields: [ + { + name: "programId"; + type: "publicKey"; + }, + { + name: "accounts"; + type: { + vec: { + defined: "TransactionAccount"; + }; + }; + }, + { + name: "data"; + type: "bytes"; + }, + { + name: "didExecute"; + type: "bool"; + } + ]; + }; + }, + { + name: "TransactionAccount"; + type: { + kind: "struct"; + fields: [ + { + name: "pubkey"; + type: "publicKey"; + }, + { + name: "isSigner"; + type: "bool"; + }, + { + name: "isWritable"; + type: "bool"; + } + ]; + }; + }, + { + name: "AuthorityType"; + type: { + kind: "enum"; + variants: [ + { + name: "OptimisticProposer"; + }, + { + name: "TimelockAuthority"; + } + ]; + }; + }, + { + name: "TransactionBatchStatus"; + type: { + kind: "enum"; + variants: [ + { + name: "Created"; + }, + { + name: "Sealed"; + }, + { + name: "Enqueued"; + }, + { + name: "Cancelled"; + }, + { + name: "Executed"; + } + ]; + }; + } + ]; + errors: [ + { + code: 6000; + name: "NotReady"; + msg: "This transaction is not yet ready to be executed"; + }, + { + code: 6001; + name: "CannotAddTransactions"; + msg: "Can only add instructions when transaction batch status is `Created`"; + }, + { + code: 6002; + name: "CannotSealTransactionBatch"; + msg: "Can only seal the transaction batch when status is `Created`"; + }, + { + code: 6003; + name: "CannotEnqueueTransactionBatch"; + msg: "Can only enqueue the timelock running once the status is `Sealed`"; + }, + { + code: 6004; + name: "CannotCancelTimelock"; + msg: "Can only cancel the transactions if the status `Enqueued`"; + }, + { + code: 6005; + name: "CanOnlyCancelDuringTimelockPeriod"; + msg: "Can only cancel the transactions during the timelock period"; + }, + { + code: 6006; + name: "CannotExecuteTransactions"; + msg: "Can only execute the transactions if the status is `Enqueued`"; + }, + { + code: 6007; + name: "NoAuthority"; + msg: "The signer is neither the timelock authority nor an optimistic proposer"; + }, + { + code: 6008; + name: "InsufficientPermissions"; + msg: "Optimistic proposers can't cancel transaction batches enqueued by the timelock authority"; + }, + { + code: 6009; + name: "OptimisticProposerCooldown"; + msg: "This optimistic proposer is still in its cooldown period"; + } + ]; +}; + +export const IDL: OptimisticTimelock = { + version: "0.3.0", + name: "optimistic_timelock", + instructions: [ + { + name: "createTimelock", + accounts: [ + { + name: "timelockSigner", + isMut: false, + isSigner: false, + }, + { + name: "timelock", + isMut: true, + isSigner: true, + }, + ], + args: [ + { + name: "authority", + type: "publicKey", + }, + { + name: "delayInSlots", + type: "u64", + }, + { + name: "enqueuers", + type: { + vec: "publicKey", + }, + }, + { + name: "enqueuerCooldownSlots", + type: "u64", + }, + ], + }, + { + name: "setDelayInSlots", + accounts: [ + { + name: "timelockSigner", + isMut: false, + isSigner: true, + }, + { + name: "timelock", + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: "delayInSlots", + type: "u64", + }, + ], + }, + { + name: "setAuthority", + accounts: [ + { + name: "timelockSigner", + isMut: false, + isSigner: true, + }, + { + name: "timelock", + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: "authority", + type: "publicKey", + }, + ], + }, + { + name: "setOptimisticProposerCooldownSlots", + accounts: [ + { + name: "timelockSigner", + isMut: false, + isSigner: true, + }, + { + name: "timelock", + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: "cooldownSlots", + type: "u64", + }, + ], + }, + { + name: "addOptimisticProposer", + accounts: [ + { + name: "timelockSigner", + isMut: false, + isSigner: true, + }, + { + name: "timelock", + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: "enqueuer", + type: "publicKey", + }, + ], + }, + { + name: "removeOptimisticProposer", + accounts: [ + { + name: "timelockSigner", + isMut: false, + isSigner: true, + }, + { + name: "timelock", + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: "optimisticProposer", + type: "publicKey", + }, + ], + }, + { + name: "createTransactionBatch", + accounts: [ + { + name: "transactionBatchAuthority", + isMut: false, + isSigner: true, + }, + { + name: "timelock", + isMut: false, + isSigner: false, + }, + { + name: "transactionBatch", + isMut: true, + isSigner: true, + }, + ], + args: [], + }, + { + name: "addTransaction", + accounts: [ + { + name: "transactionBatchAuthority", + isMut: false, + isSigner: true, + }, + { + name: "transactionBatch", + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: "programId", + type: "publicKey", + }, + { + name: "accounts", + type: { + vec: { + defined: "TransactionAccount", + }, + }, + }, + { + name: "data", + type: "bytes", + }, + ], + }, + { + name: "sealTransactionBatch", + accounts: [ + { + name: "transactionBatchAuthority", + isMut: false, + isSigner: true, + }, + { + name: "transactionBatch", + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + { + name: "enqueueTransactionBatch", + accounts: [ + { + name: "authority", + isMut: false, + isSigner: true, + }, + { + name: "timelock", + isMut: true, + isSigner: false, + }, + { + name: "transactionBatch", + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + { + name: "cancelTransactionBatch", + accounts: [ + { + name: "authority", + isMut: false, + isSigner: true, + }, + { + name: "timelock", + isMut: true, + isSigner: false, + }, + { + name: "transactionBatch", + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + { + name: "executeTransactionBatch", + accounts: [ + { + name: "timelockSigner", + isMut: false, + isSigner: false, + }, + { + name: "timelock", + isMut: false, + isSigner: false, + }, + { + name: "transactionBatch", + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + ], + accounts: [ + { + name: "timelock", + type: { + kind: "struct", + fields: [ + { + name: "authority", + type: "publicKey", + }, + { + name: "signerBump", + type: "u8", + }, + { + name: "delayInSlots", + type: "u64", + }, + { + name: "optimisticProposers", + type: { + vec: { + defined: "OptimisticProposer", + }, + }, + }, + { + name: "optimisticProposerCooldownSlots", + docs: [ + "The cooldown period for enqueuers to prevent spamming the timelock.", + ], + type: "u64", + }, + ], + }, + }, + { + name: "transactionBatch", + type: { + kind: "struct", + fields: [ + { + name: "status", + type: { + defined: "TransactionBatchStatus", + }, + }, + { + name: "transactions", + type: { + vec: { + defined: "Transaction", + }, + }, + }, + { + name: "timelock", + type: "publicKey", + }, + { + name: "enqueuedSlot", + type: "u64", + }, + { + name: "transactionBatchAuthority", + type: "publicKey", + }, + { + name: "enqueuerType", + type: { + defined: "AuthorityType", + }, + }, + ], + }, + }, + ], + types: [ + { + name: "OptimisticProposer", + type: { + kind: "struct", + fields: [ + { + name: "pubkey", + type: "publicKey", + }, + { + name: "lastSlotEnqueued", + type: "u64", + }, + ], + }, + }, + { + name: "Transaction", + type: { + kind: "struct", + fields: [ + { + name: "programId", + type: "publicKey", + }, + { + name: "accounts", + type: { + vec: { + defined: "TransactionAccount", + }, + }, + }, + { + name: "data", + type: "bytes", + }, + { + name: "didExecute", + type: "bool", + }, + ], + }, + }, + { + name: "TransactionAccount", + type: { + kind: "struct", + fields: [ + { + name: "pubkey", + type: "publicKey", + }, + { + name: "isSigner", + type: "bool", + }, + { + name: "isWritable", + type: "bool", + }, + ], + }, + }, + { + name: "AuthorityType", + type: { + kind: "enum", + variants: [ + { + name: "OptimisticProposer", + }, + { + name: "TimelockAuthority", + }, + ], + }, + }, + { + name: "TransactionBatchStatus", + type: { + kind: "enum", + variants: [ + { + name: "Created", + }, + { + name: "Sealed", + }, + { + name: "Enqueued", + }, + { + name: "Cancelled", + }, + { + name: "Executed", + }, + ], + }, + }, + ], + errors: [ + { + code: 6000, + name: "NotReady", + msg: "This transaction is not yet ready to be executed", + }, + { + code: 6001, + name: "CannotAddTransactions", + msg: "Can only add instructions when transaction batch status is `Created`", + }, + { + code: 6002, + name: "CannotSealTransactionBatch", + msg: "Can only seal the transaction batch when status is `Created`", + }, + { + code: 6003, + name: "CannotEnqueueTransactionBatch", + msg: "Can only enqueue the timelock running once the status is `Sealed`", + }, + { + code: 6004, + name: "CannotCancelTimelock", + msg: "Can only cancel the transactions if the status `Enqueued`", + }, + { + code: 6005, + name: "CanOnlyCancelDuringTimelockPeriod", + msg: "Can only cancel the transactions during the timelock period", + }, + { + code: 6006, + name: "CannotExecuteTransactions", + msg: "Can only execute the transactions if the status is `Enqueued`", + }, + { + code: 6007, + name: "NoAuthority", + msg: "The signer is neither the timelock authority nor an optimistic proposer", + }, + { + code: 6008, + name: "InsufficientPermissions", + msg: "Optimistic proposers can't cancel transaction batches enqueued by the timelock authority", + }, + { + code: 6009, + name: "OptimisticProposerCooldown", + msg: "This optimistic proposer is still in its cooldown period", + }, + ], +}; diff --git a/sdk/src/v0.4/types/utils.ts b/sdk/src/v0.4/types/utils.ts new file mode 100644 index 00000000..c878debe --- /dev/null +++ b/sdk/src/v0.4/types/utils.ts @@ -0,0 +1,3 @@ +export type LowercaseKeys = { + [K in keyof T as Lowercase]: T[K]; +}; diff --git a/sdk/src/utils/cu.ts b/sdk/src/v0.4/utils/cu.ts similarity index 100% rename from sdk/src/utils/cu.ts rename to sdk/src/v0.4/utils/cu.ts diff --git a/sdk/src/v0.4/utils/filters.ts b/sdk/src/v0.4/utils/filters.ts new file mode 100644 index 00000000..945f7731 --- /dev/null +++ b/sdk/src/v0.4/utils/filters.ts @@ -0,0 +1,21 @@ +import { GetProgramAccountsFilter, PublicKey } from "@solana/web3.js"; + +export const filterPositionsByUser = ( + userAddr: PublicKey +): GetProgramAccountsFilter => ({ + memcmp: { + offset: 8, // discriminator + bytes: userAddr.toBase58(), + }, +}); + +export const filterPositionsByAmm = ( + ammAddr: PublicKey +): GetProgramAccountsFilter => ({ + memcmp: { + offset: + 8 + // discriminator + 32, // user address + bytes: ammAddr.toBase58(), + }, +}); diff --git a/sdk/src/v0.4/utils/index.ts b/sdk/src/v0.4/utils/index.ts new file mode 100644 index 00000000..cb5d012e --- /dev/null +++ b/sdk/src/v0.4/utils/index.ts @@ -0,0 +1,40 @@ +export * from "./filters.js"; +export * from "./pda.js"; +export * from "./priceMath.js"; +export * from "./metadata.js"; +export * from "./cu.js"; +export * from "./instruction.js"; + +import { AccountMeta, ComputeBudgetProgram, PublicKey } from "@solana/web3.js"; + +export enum PriorityFeeTier { + NORMAL = 35, + HIGH = 3571, + TURBO = 357142, +} + +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 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)); +} diff --git a/sdk/src/v0.4/utils/instruction.ts b/sdk/src/v0.4/utils/instruction.ts new file mode 100644 index 00000000..f18e4883 --- /dev/null +++ b/sdk/src/v0.4/utils/instruction.ts @@ -0,0 +1,16 @@ +import * as anchor from "@coral-xyz/anchor"; +import { TransactionInstruction } from "@solana/web3.js"; + +export class InstructionUtils { + public static async getInstructions( + ...methodBuilders: any[] + ): Promise { + let instructions: TransactionInstruction[] = []; + + for (const methodBuilder of methodBuilders) { + instructions.push(...(await methodBuilder.transaction()).instructions); + } + + return instructions; + } +} diff --git a/sdk/src/v0.4/utils/metadata.ts b/sdk/src/v0.4/utils/metadata.ts new file mode 100644 index 00000000..922a89f3 --- /dev/null +++ b/sdk/src/v0.4/utils/metadata.ts @@ -0,0 +1,35 @@ +import BN from "bn.js"; +import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; +import { Connection } from "@solana/web3.js"; +import { bundlrUploader } from "@metaplex-foundation/umi-uploader-bundlr"; +import { UmiPlugin } from "@metaplex-foundation/umi"; + +export const assetImageMap: Record = { + fMETA: "https://arweave.net/tGxvOjMZw7B0qHsdCcIMO57oH5g5OaItOZdXo3BXKz8", + fUSDC: "https://arweave.net/DpvxeAyVbaoivhIVCLjdf566k2SwVn0YVBL0sTOezWk", + pMETA: "https://arweave.net/iuqi7PRRESdDxj1oRyk2WzR90_zdFcmZsuWicv3XGfs", + pUSDC: "https://arweave.net/e4IO7F59F_RKCiuB--_ABPot7Qh1yFsGkWzVhcXuKDU", +}; + +// Upload some JSON, returning its URL +export const uploadConditionalTokenMetadataJson = async ( + connection: Connection, + identityPlugin: UmiPlugin, + proposalNumber: number, + symbol: string + // proposal: BN, + // conditionalToken: string, + // image: string +): Promise => { + // use bundlr, targeting arweave + const umi = createUmi(connection); + umi.use(bundlrUploader()); + umi.use(identityPlugin); + + return umi.uploader.uploadJson({ + name: `Proposal ${proposalNumber}: ${symbol}`, + image: assetImageMap[symbol], + symbol, + description: "A conditional token for use in futarchy.", + }); +}; diff --git a/sdk/src/utils/pda.ts b/sdk/src/v0.4/utils/pda.ts similarity index 98% rename from sdk/src/utils/pda.ts rename to sdk/src/v0.4/utils/pda.ts index c7dc1b89..13d5a4d5 100644 --- a/sdk/src/utils/pda.ts +++ b/sdk/src/v0.4/utils/pda.ts @@ -9,7 +9,7 @@ import { fromWeb3JsPublicKey, toWeb3JsPublicKey, } from "@metaplex-foundation/umi-web3js-adapters"; -import { MPL_TOKEN_METADATA_PROGRAM_ID } from "../constants"; +import { MPL_TOKEN_METADATA_PROGRAM_ID } from "../constants.js"; export const getQuestionAddr = ( programId: PublicKey, diff --git a/sdk/src/v0.4/utils/priceMath.ts b/sdk/src/v0.4/utils/priceMath.ts new file mode 100644 index 00000000..305c3375 --- /dev/null +++ b/sdk/src/v0.4/utils/priceMath.ts @@ -0,0 +1,85 @@ +import BN from "bn.js"; + +const BN_TEN = new BN(10); +const PRICE_SCALE = BN_TEN.pow(new BN(12)); +const PRICE_SCALE_NUMBER = 1e12; + +export class PriceMath { + public static getAmmPriceFromReserves( + baseReserves: BN, + quoteReserves: BN + ): BN { + return quoteReserves.mul(PRICE_SCALE).div(baseReserves); + } + + public static getChainAmount(humanAmount: number, decimals: number): BN { + // you have to do it this weird way because BN can't be constructed with + // numbers larger than 2**50 + const [integerPart, fractionalPart = ""] = humanAmount + .toString() + .split("."); + return new BN(integerPart + fractionalPart) + .mul(new BN(10).pow(new BN(decimals))) + .div(new BN(10).pow(new BN(fractionalPart.length))); + } + + public static getHumanAmount(chainAmount: BN, decimals: number): number { + return chainAmount.toNumber() / 10 ** decimals; + } + + public static getHumanPrice( + ammPrice: BN, + baseDecimals: number, + quoteDecimals: number + ): number { + let decimalScalar = BN_TEN.pow(new BN(quoteDecimals - baseDecimals).abs()); + + let price1e12 = + quoteDecimals > baseDecimals + ? ammPrice.div(decimalScalar) + : ammPrice.mul(decimalScalar); + + return price1e12.toNumber() / 1e12; + } + + public static getAmmPrice( + humanPrice: number, + baseDecimals: number, + quoteDecimals: number + ): BN { + let price1e12 = new BN(humanPrice * PRICE_SCALE_NUMBER); + + let decimalScalar = BN_TEN.pow(new BN(quoteDecimals - baseDecimals).abs()); + + let scaledPrice = + quoteDecimals > baseDecimals + ? price1e12.mul(decimalScalar) + : price1e12.div(decimalScalar); + + return scaledPrice; + } + + public static getAmmPrices( + baseDecimals: number, + quoteDecimals: number, + ...prices: number[] + ): BN[] { + // Map through each price, scaling it using the scalePrice method + return prices.map((price) => + this.getAmmPrice(price, baseDecimals, quoteDecimals) + ); + } + + public static scale(number: number, decimals: number): BN { + return new BN(number * 10 ** decimals); + // return new BN(number).mul(new BN(10).pow(new BN(decimals))); + } + + public static addSlippage(chainAmount: BN, slippageBps: BN): BN { + return chainAmount.mul(slippageBps.addn(10_000)).divn(10_000); + } + + public static subtractSlippage(chainAmount: BN, slippageBps: BN): BN { + return chainAmount.mul(new BN(10_000).sub(slippageBps)).divn(10_000); + } +} diff --git a/sdk/tsconfig.json b/sdk/tsconfig.json index ddceb6e9..ba75c571 100644 --- a/sdk/tsconfig.json +++ b/sdk/tsconfig.json @@ -4,17 +4,20 @@ "mocha", "chai" ], + "paths": { + "@metadaoproject/futarchy/v0.4": ["./dist/v0.4/types/index.d.ts"] + }, "typeRoots": [ "./node_modules/@types" ], "lib": [ "esnext" ], - "module": "commonjs", + "module": "NodeNext", "target": "esnext", "esModuleInterop": true, "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "nodenext", "sourceMap": true, "outDir": "dist", "strict": true, @@ -22,6 +25,7 @@ }, "include": [ "src/*", + "src/v0.4/*" ], "exclude": [ "node_modules", diff --git a/sdk/yarn.lock b/sdk/yarn.lock index cc59b3f3..159e0282 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -1303,7 +1303,7 @@ type-detect@^4.0.0, type-detect@^4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -typescript@^4.3.5: +typescript@^4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== diff --git a/tests/conditionalVault/main.test.ts b/tests/conditionalVault/main.test.ts index 10240843..b2aeff7c 100644 --- a/tests/conditionalVault/main.test.ts +++ b/tests/conditionalVault/main.test.ts @@ -13,9 +13,9 @@ export default function suite() { it("scalar grant market", scalarGrantMarket); describe("#initialize_question", initializeQuestion); describe("#initialize_conditional_vault", initializeConditionalVault); - describe("#resolve_question", resolveQuestion); - describe("#split_tokens", splitTokens); - describe("#merge_tokens", mergeTokens); - describe("#redeem_tokens", redeemTokens); - describe("#add_metadata_to_conditional_tokens", addMetadataToConditionalTokens); + // describe("#resolve_question", resolveQuestion); + // describe("#split_tokens", splitTokens); + // describe("#merge_tokens", mergeTokens); + // describe("#redeem_tokens", redeemTokens); + // describe("#add_metadata_to_conditional_tokens", addMetadataToConditionalTokens); } diff --git a/tests/conditionalVault/unit/initializeConditionalVault.test.ts b/tests/conditionalVault/unit/initializeConditionalVault.test.ts index e123f0dd..dcebb627 100644 --- a/tests/conditionalVault/unit/initializeConditionalVault.test.ts +++ b/tests/conditionalVault/unit/initializeConditionalVault.test.ts @@ -1,9 +1,9 @@ import { - sha256, ConditionalVaultClient, getVaultAddr, getConditionalTokenMintAddr, -} from "@metadaoproject/futarchy"; +} from "@metadaoproject/futarchy/v0.4"; +import { sha256 } from "@metadaoproject/futarchy"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { createMint, getMint } from "spl-token-bankrun"; diff --git a/tests/conditionalVault/unit/initializeQuestion.test.ts b/tests/conditionalVault/unit/initializeQuestion.test.ts index f107306d..db29a812 100644 --- a/tests/conditionalVault/unit/initializeQuestion.test.ts +++ b/tests/conditionalVault/unit/initializeQuestion.test.ts @@ -1,11 +1,12 @@ import { - sha256, - ConditionalVaultClient, - getQuestionAddr, + sha256 } from "@metadaoproject/futarchy"; +// const { ConditionalVaultClient, getQuestionAddr } = futarchy; import { Keypair } from "@solana/web3.js"; import { assert } from "chai"; import { expectError } from "../../utils"; +import { ConditionalVaultClient, getQuestionAddr } from "@metadaoproject/futarchy/v0.4"; +// import { getQuestionAddr } from "@metadaoproject/futarchy/dist/v0.4"; export default function suite() { let vaultClient: ConditionalVaultClient; diff --git a/tests/main.test.ts b/tests/main.test.ts index 80862dfe..786251eb 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -1,6 +1,6 @@ -import conditionalVault from "./conditionalVault/main.test"; -import amm from "./amm/main.test"; -import autocrat from "./autocrat/autocrat"; +import conditionalVault from "./conditionalVault/main.test.js"; +// import amm from "./amm/main.test"; +// import autocrat from "./autocrat/autocrat"; import { startAnchor } from "solana-bankrun"; import { BankrunProvider } from "anchor-bankrun"; @@ -9,7 +9,14 @@ import { AmmClient, AutocratClient, ConditionalVaultClient, -} from "@metadaoproject/futarchy"; +} from "@metadaoproject/futarchy/v0.4"; +// import { +// // AmmClient, +// // AutocratClient, +// // ConditionalVaultClient, +// getVersion, +// VersionKey +// } from "@metadaoproject/futarchy"; import { PublicKey, Keypair } from "@solana/web3.js"; import { createAssociatedTokenAccount, @@ -32,6 +39,9 @@ const MPL_TOKEN_METADATA_PROGRAM_ID = toWeb3JsPublicKey( ); before(async function () { + // const version: VersionKey = "0.4"; + // const { AmmClient, AutocratClient, ConditionalVaultClient } = getVersion(version); + this.context = await startAnchor( "./", // [], @@ -130,5 +140,5 @@ before(async function () { }); describe("conditional_vault", conditionalVault); -describe("amm", amm); -describe("autocrat", autocrat); +// describe("amm", amm); +// describe("autocrat", autocrat); diff --git a/tsconfig.json b/tsconfig.json index 1bd44cb4..f1dd71d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,8 +11,9 @@ "es2015", "dom" ], - "module": "commonjs", + "module": "nodenext", "target": "ES2020", + "moduleResolution": "nodenext", "esModuleInterop": true, "resolveJsonModule": true, "paths": { From ef7d0944cac850fa76d574021afa14ba4b32d43b Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Wed, 28 Aug 2024 00:00:00 +0000 Subject: [PATCH 47/52] Use new sdk version --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index b45c2a99..c4c0925a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ }, "dependencies": { "@coral-xyz/anchor": "0.29.0", - "@metadaoproject/futarchy": "0.3.0-alpha.16", + "@metadaoproject/futarchy": "0.4.0-alpha.0", "@metaplex-foundation/mpl-token-metadata": "^3.2.0", "@metaplex-foundation/umi": "^0.9.1", "@metaplex-foundation/umi-bundle-defaults": "^0.9.1", diff --git a/yarn.lock b/yarn.lock index 463564e7..46e2f6eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -629,10 +629,10 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@metadaoproject/futarchy@0.3.0-alpha.16": - version "0.3.0-alpha.16" - resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.3.0-alpha.16.tgz#f1f49e4f9d13976c922a55133627023168624728" - integrity sha512-5YDXUcdrr09KboWdX6E35MXGyHCvEqm7WHdaBo4s1HaFdN2HegddjONuvDbgwaa3e3XwkEzYw7ueSeAH27g06g== +"@metadaoproject/futarchy@0.4.0-alpha.0": + version "0.4.0-alpha.0" + resolved "https://registry.yarnpkg.com/@metadaoproject/futarchy/-/futarchy-0.4.0-alpha.0.tgz#50a4c4f8cc57a047dbc05e44f512a1e008c54f5c" + integrity sha512-iURmMXOkzD5Wjduvsi7/5zKx6AB2YJa1AG/Vwh8HZjLHGT3cjGQ2lf36ugpHryImDRg1RAPO+l8WjXgLVNZwCw== dependencies: "@coral-xyz/anchor" "^0.29.0" "@noble/hashes" "^1.4.0" From 8e839f73bf9f566330ce1ea1a9be3a33fec462e3 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Wed, 28 Aug 2024 00:00:00 +0000 Subject: [PATCH 48/52] Add v0.3 --- sdk/package.json | 1 + tests/amm/integration/ammLifecycle.test.ts | 21 ++++++------ tests/amm/unit/addLiquidity.test.ts | 25 +++++++------- tests/amm/unit/initializeAmm.test.ts | 9 ++--- tests/amm/unit/removeLiquidity.test.ts | 21 ++++++------ tests/amm/unit/swap.test.ts | 33 ++++++++++--------- tests/autocrat/autocrat.ts | 17 +++++----- tests/conditionalVault/main.test.ts | 28 ++++++++-------- .../addMetadataToConditionalTokens.test.ts | 5 +-- .../conditionalVault/unit/mergeTokens.test.ts | 12 ++++--- .../unit/redeemTokens.test.ts | 6 ++-- .../unit/resolveQuestion.test.ts | 5 +-- .../conditionalVault/unit/splitTokens.test.ts | 16 +++++---- tests/main.test.ts | 8 ++--- 14 files changed, 111 insertions(+), 96 deletions(-) diff --git a/sdk/package.json b/sdk/package.json index ae1b2bdf..5cfc2255 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -10,6 +10,7 @@ ], "exports": { ".": "./dist/index.js", + "./v0.3": "./dist/v0.3/index.js", "./v0.4": "./dist/v0.4/index.js" }, "scripts": { diff --git a/tests/amm/integration/ammLifecycle.test.ts b/tests/amm/integration/ammLifecycle.test.ts index 57e5ac7f..d4a9768b 100644 --- a/tests/amm/integration/ammLifecycle.test.ts +++ b/tests/amm/integration/ammLifecycle.test.ts @@ -2,7 +2,7 @@ import { AmmClient, getAmmAddr, getAmmLpMintAddr, -} from "@metadaoproject/futarchy"; +} from "@metadaoproject/futarchy/v0.4"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { @@ -14,7 +14,8 @@ import { } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; -import { expectError } from "../../utils"; +import { expectError } from "../../utils.js"; +import { BN } from "bn.js"; export default async function () { let ammClient: AmmClient; @@ -55,8 +56,8 @@ export default async function () { // 2. Add initial liquidity await ammClient.addLiquidity(amm, 1000, 2); const ammAfterInitialLiquidity = await ammClient.getAmm(amm); - assert.isTrue(ammAfterInitialLiquidity.baseAmount.gt(new anchor.BN(0))); - assert.isTrue(ammAfterInitialLiquidity.quoteAmount.gt(new anchor.BN(0))); + assert.isTrue(ammAfterInitialLiquidity.baseAmount.gt(new BN(0))); + assert.isTrue(ammAfterInitialLiquidity.quoteAmount.gt(new BN(0))); // 3. Perform swaps await ammClient.swap(amm, { buy: {} }, 100, 0.1); @@ -77,9 +78,9 @@ export default async function () { amm, META, USDC, - new anchor.BN(Number(userLpBalance) / 2), - new anchor.BN(0), - new anchor.BN(0) + new BN(Number(userLpBalance) / 2), + new BN(0), + new BN(0) ) .rpc(); @@ -94,9 +95,9 @@ export default async function () { amm, META, USDC, - new anchor.BN(userLpBalance), - new anchor.BN(0), - new anchor.BN(0) + new BN(userLpBalance), + new BN(0), + new BN(0) ) .rpc(); diff --git a/tests/amm/unit/addLiquidity.test.ts b/tests/amm/unit/addLiquidity.test.ts index 4abc370c..c32db1b0 100644 --- a/tests/amm/unit/addLiquidity.test.ts +++ b/tests/amm/unit/addLiquidity.test.ts @@ -2,7 +2,7 @@ import { AmmClient, getAmmAddr, getAmmLpMintAddr, -} from "@metadaoproject/futarchy"; +} from "@metadaoproject/futarchy/v0.4"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { @@ -12,8 +12,9 @@ import { getAccount, } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; -import { expectError } from "../../utils"; +import { expectError } from "../../utils.js"; import * as token from "@solana/spl-token"; +import { BN } from "bn.js"; export default function suite() { let ammClient: AmmClient; @@ -54,16 +55,16 @@ export default function suite() { amm, META, USDC, - new anchor.BN(5000 * 10 ** 6), - new anchor.BN(6 * 10 ** 9), - new anchor.BN(0) + new BN(5000 * 10 ** 6), + new BN(6 * 10 ** 9), + new BN(0) ) .rpc(); const storedAmm = await ammClient.getAmm(amm); - assert.isTrue(storedAmm.baseAmount.eq(new anchor.BN(6 * 10 ** 9))); - assert.isTrue(storedAmm.quoteAmount.eq(new anchor.BN(5000 * 10 ** 6))); + assert.isTrue(storedAmm.baseAmount.eq(new BN(6 * 10 ** 9))); + assert.isTrue(storedAmm.quoteAmount.eq(new BN(5000 * 10 ** 6))); const lpMint = await getAccount( this.banksClient, @@ -82,16 +83,16 @@ export default function suite() { amm, META, USDC, - new anchor.BN(5000 * 10 ** 6), - new anchor.BN(6 * 10 ** 9), - new anchor.BN(0) + new BN(5000 * 10 ** 6), + new BN(6 * 10 ** 9), + new BN(0) ) .rpc(); const storedAmm = await ammClient.getAmm(amm); - assert.isTrue(storedAmm.baseAmount.eq(new anchor.BN(6 * 10 ** 9))); - assert.isTrue(storedAmm.quoteAmount.eq(new anchor.BN(5000 * 10 ** 6))); + assert.isTrue(storedAmm.baseAmount.eq(new BN(6 * 10 ** 9))); + assert.isTrue(storedAmm.quoteAmount.eq(new BN(5000 * 10 ** 6))); // const lpMint = await getAccount( // this.banksClient, diff --git a/tests/amm/unit/initializeAmm.test.ts b/tests/amm/unit/initializeAmm.test.ts index 13fce2cd..be1dbbfb 100644 --- a/tests/amm/unit/initializeAmm.test.ts +++ b/tests/amm/unit/initializeAmm.test.ts @@ -3,12 +3,13 @@ import { getAmmAddr, getAmmLpMintAddr, PriceMath, -} from "@metadaoproject/futarchy"; +} from "@metadaoproject/futarchy/v0.4"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { createMint } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; -import { expectError } from "../../utils"; +import { expectError } from "../../utils.js"; +import { BN } from "bn.js"; export default function suite() { let ammClient: AmmClient; @@ -34,8 +35,8 @@ export default function suite() { }); it("creates an amm", async function () { - let expectedInitialObservation = new anchor.BN(500_000_000_000); - let expectedMaxObservationChangePerUpdate = new anchor.BN(10_000_000_000); + let expectedInitialObservation = new BN(500_000_000_000); + let expectedMaxObservationChangePerUpdate = new BN(10_000_000_000); let bump: number; let amm: PublicKey; diff --git a/tests/amm/unit/removeLiquidity.test.ts b/tests/amm/unit/removeLiquidity.test.ts index 48a1d05b..19d7261b 100644 --- a/tests/amm/unit/removeLiquidity.test.ts +++ b/tests/amm/unit/removeLiquidity.test.ts @@ -14,8 +14,9 @@ import { } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; -import { expectError } from "../../utils"; +import { expectError } from "../../utils.js"; import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { BN } from "bn.js"; export default function suite() { let ammClient: AmmClient; @@ -63,9 +64,9 @@ export default function suite() { amm, META, USDC, - new anchor.BN(0), - new anchor.BN(0), - new anchor.BN(0) + new BN(0), + new BN(0), + new BN(0) ) .rpc() .then(callbacks[0], callbacks[1]); @@ -90,9 +91,9 @@ export default function suite() { amm, META, USDC, - new anchor.BN(userLpAccountStart.amount.toString()).divn(2), - new anchor.BN(0), - new anchor.BN(0) + new BN(userLpAccountStart.amount.toString()).divn(2), + new BN(0), + new BN(0) ) .rpc(); @@ -136,9 +137,9 @@ export default function suite() { amm, META, USDC, - new anchor.BN(userLpAccountStart.amount.toString()), - new anchor.BN(1 * 10 ** 9), - new anchor.BN(10 * 10 ** 6) + new BN(userLpAccountStart.amount.toString()), + new BN(1 * 10 ** 9), + new BN(10 * 10 ** 6) ) .rpc(); diff --git a/tests/amm/unit/swap.test.ts b/tests/amm/unit/swap.test.ts index 0263a38c..5614217d 100644 --- a/tests/amm/unit/swap.test.ts +++ b/tests/amm/unit/swap.test.ts @@ -2,7 +2,7 @@ import { AmmClient, getAmmAddr, getAmmLpMintAddr, -} from "@metadaoproject/futarchy"; +} from "@metadaoproject/futarchy/v0.4"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { @@ -13,8 +13,9 @@ import { getMint, } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; -import { expectError } from "../../utils"; -import { advanceBySlots } from "../../utils"; +import { expectError } from "../../utils.js"; +import { advanceBySlots } from "../../utils.js"; +import { BN } from "bn.js"; import { getAssociatedTokenAddressSync } from "@solana/spl-token"; export default function suite() { @@ -54,9 +55,9 @@ export default function suite() { amm, META, USDC, - new anchor.BN(10_000 * 10 ** 6), - new anchor.BN(10 * 10 ** 9), - new anchor.BN(0) + new BN(10_000 * 10 ** 6), + new BN(10 * 10 ** 9), + new BN(0) ) .rpc(); }); @@ -84,14 +85,14 @@ export default function suite() { const storedAmm = await ammClient.getAmm(amm); let sim = ammClient.simulateSwap( - new anchor.BN(100 * 10 ** 6), + new BN(100 * 10 ** 6), { buy: {} }, storedAmm.baseAmount, storedAmm.quoteAmount ); assert.equal( sim.expectedOut.toString(), - new anchor.BN(expectedOut * 10 ** 9).toString() + new BN(expectedOut * 10 ** 9).toString() ); let callbacks = expectError( @@ -157,8 +158,8 @@ export default function suite() { META, USDC, { sell: {} }, - new anchor.BN(startingBaseSwapAmount), - new anchor.BN(1) + new BN(startingBaseSwapAmount), + new BN(1) ) .rpc(); @@ -175,8 +176,8 @@ export default function suite() { META, USDC, { buy: {} }, - new anchor.BN(quoteReceived), - new anchor.BN(1) + new BN(quoteReceived), + new BN(1) ) .rpc(); @@ -203,8 +204,8 @@ export default function suite() { META, USDC, { buy: {} }, - new anchor.BN(startingQuoteSwapAmount), - new anchor.BN(1) + new BN(startingQuoteSwapAmount), + new BN(1) ) .rpc(); @@ -220,8 +221,8 @@ export default function suite() { META, USDC, { sell: {} }, - new anchor.BN(baseReceived), - new anchor.BN(1) + new BN(baseReceived), + new BN(1) ) .rpc(); diff --git a/tests/autocrat/autocrat.ts b/tests/autocrat/autocrat.ts index 09bbd90b..3f3ab477 100644 --- a/tests/autocrat/autocrat.ts +++ b/tests/autocrat/autocrat.ts @@ -1,5 +1,6 @@ import * as anchor from "@coral-xyz/anchor"; -import { BN, Program } from "@coral-xyz/anchor"; +import { Program } from "@coral-xyz/anchor"; +import { BN } from "bn.js"; import * as token from "@solana/spl-token"; import { getAssociatedTokenAddressSync } from "@solana/spl-token"; import { MEMO_PROGRAM_ID } from "@solana/spl-memo"; @@ -20,16 +21,16 @@ import { getAccount, } from "spl-token-bankrun"; -import { advanceBySlots, expectError } from "../utils"; -import { Autocrat, IDL as AutocratIDL } from "../../target/types/autocrat"; +import { advanceBySlots, expectError } from "../utils.js"; +import { Autocrat, IDL as AutocratIDL } from "../../target/types/autocrat.js"; import { ConditionalVault, IDL as ConditionalVaultIDL, -} from "../../target/types/conditional_vault"; +} from "../../target/types/conditional_vault.js"; import { AutocratMigrator, IDL as AutocratMigratorIDL, -} from "../../target/types/autocrat_migrator"; +} from "../../target/types/autocrat_migrator.js"; const { PublicKey, Keypair } = anchor.web3; @@ -40,12 +41,12 @@ import { getAmmAddr, getAmmLpMintAddr, getVaultAddr, -} from "@metadaoproject/futarchy"; -import { PriceMath } from "@metadaoproject/futarchy"; +} from "@metadaoproject/futarchy/v0.4"; +import { PriceMath } from "@metadaoproject/futarchy/v0.4"; import { AutocratClient, ConditionalVaultClient, -} from "@metadaoproject/futarchy"; +} from "@metadaoproject/futarchy/v0.4"; import { ComputeBudgetInstruction, ComputeBudgetProgram, diff --git a/tests/conditionalVault/main.test.ts b/tests/conditionalVault/main.test.ts index b2aeff7c..58429b1e 100644 --- a/tests/conditionalVault/main.test.ts +++ b/tests/conditionalVault/main.test.ts @@ -1,21 +1,21 @@ -import initializeQuestion from "./unit/initializeQuestion.test"; -import initializeConditionalVault from "./unit/initializeConditionalVault.test"; -import resolveQuestion from "./unit/resolveQuestion.test"; -import splitTokens from "./unit/splitTokens.test"; -import mergeTokens from "./unit/mergeTokens.test"; -import redeemTokens from "./unit/redeemTokens.test"; -import addMetadataToConditionalTokens from "./unit/addMetadataToConditionalTokens.test"; -import binaryPredictionMarket from "./integration/binaryPredictionMarket.test"; -import scalarGrantMarket from "./integration/scalarGrantMarket.test"; +import initializeQuestion from "./unit/initializeQuestion.test.js"; +import initializeConditionalVault from "./unit/initializeConditionalVault.test.js"; +import resolveQuestion from "./unit/resolveQuestion.test.js"; +import splitTokens from "./unit/splitTokens.test.js"; +import mergeTokens from "./unit/mergeTokens.test.js"; +import redeemTokens from "./unit/redeemTokens.test.js"; +import addMetadataToConditionalTokens from "./unit/addMetadataToConditionalTokens.test.js"; +import binaryPredictionMarket from "./integration/binaryPredictionMarket.test.js"; +import scalarGrantMarket from "./integration/scalarGrantMarket.test.js"; export default function suite() { it("binary prediction market", binaryPredictionMarket); it("scalar grant market", scalarGrantMarket); describe("#initialize_question", initializeQuestion); describe("#initialize_conditional_vault", initializeConditionalVault); - // describe("#resolve_question", resolveQuestion); - // describe("#split_tokens", splitTokens); - // describe("#merge_tokens", mergeTokens); - // describe("#redeem_tokens", redeemTokens); - // describe("#add_metadata_to_conditional_tokens", addMetadataToConditionalTokens); + describe("#resolve_question", resolveQuestion); + describe("#split_tokens", splitTokens); + describe("#merge_tokens", mergeTokens); + describe("#redeem_tokens", redeemTokens); + describe("#add_metadata_to_conditional_tokens", addMetadataToConditionalTokens); } diff --git a/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts b/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts index 4730fb83..9231df29 100644 --- a/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts +++ b/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts @@ -1,9 +1,10 @@ -import { sha256, ConditionalVaultClient, getConditionalTokenMintAddr, getMetadataAddr } from "@metadaoproject/futarchy"; +import { sha256 } from "@metadaoproject/futarchy"; +import { ConditionalVaultClient, getConditionalTokenMintAddr, getMetadataAddr } from "@metadaoproject/futarchy/v0.4"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { createMint } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; -import { expectError } from "../../utils"; +import { expectError } from "../../utils.js"; import { Metadata, deserializeMetadata, getMetadataAccountDataSerializer } from "@metaplex-foundation/mpl-token-metadata"; export default function suite() { diff --git a/tests/conditionalVault/unit/mergeTokens.test.ts b/tests/conditionalVault/unit/mergeTokens.test.ts index 507ccaf5..cd7a104d 100644 --- a/tests/conditionalVault/unit/mergeTokens.test.ts +++ b/tests/conditionalVault/unit/mergeTokens.test.ts @@ -1,4 +1,5 @@ -import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { sha256 } from "@metadaoproject/futarchy"; +import { ConditionalVaultClient } from "@metadaoproject/futarchy/v0.4"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { @@ -9,7 +10,8 @@ import { } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; -import { expectError } from "../../utils"; +import { expectError } from "../../utils.js"; +import { BN } from "bn.js"; export default function suite() { let vaultClient: ConditionalVaultClient; @@ -63,7 +65,7 @@ export default function suite() { question, vault, underlyingTokenMint, - new anchor.BN(1000), + new BN(1000), 2 ) .rpc(); @@ -82,7 +84,7 @@ export default function suite() { question, vault, underlyingTokenMint, - new anchor.BN(600), + new BN(600), 2 ) .rpc(); @@ -105,7 +107,7 @@ export default function suite() { ); await vaultClient - .mergeTokensIx(question, vault, underlyingTokenMint, new anchor.BN(2000), 2) + .mergeTokensIx(question, vault, underlyingTokenMint, new BN(2000), 2) .rpc() .then(callbacks[0], callbacks[1]); }); diff --git a/tests/conditionalVault/unit/redeemTokens.test.ts b/tests/conditionalVault/unit/redeemTokens.test.ts index afaa80f0..2790aa0f 100644 --- a/tests/conditionalVault/unit/redeemTokens.test.ts +++ b/tests/conditionalVault/unit/redeemTokens.test.ts @@ -1,4 +1,5 @@ -import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { sha256 } from "@metadaoproject/futarchy"; +import { ConditionalVaultClient } from "@metadaoproject/futarchy/v0.4"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { @@ -9,6 +10,7 @@ import { } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; +import { BN } from "bn.js"; export default function suite() { let vaultClient: ConditionalVaultClient; @@ -64,7 +66,7 @@ export default function suite() { question, vault, underlyingTokenMint, - new anchor.BN(1000), + new BN(1000), 2 ) .rpc(); diff --git a/tests/conditionalVault/unit/resolveQuestion.test.ts b/tests/conditionalVault/unit/resolveQuestion.test.ts index 1c15cd79..c00a01a1 100644 --- a/tests/conditionalVault/unit/resolveQuestion.test.ts +++ b/tests/conditionalVault/unit/resolveQuestion.test.ts @@ -1,7 +1,8 @@ -import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { sha256 } from "@metadaoproject/futarchy"; +import { ConditionalVaultClient } from "@metadaoproject/futarchy/v0.4"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; -import { expectError } from "../../utils"; +import { expectError } from "../../utils.js"; export default function suite() { let vaultClient: ConditionalVaultClient; diff --git a/tests/conditionalVault/unit/splitTokens.test.ts b/tests/conditionalVault/unit/splitTokens.test.ts index 078b92fb..840320d1 100644 --- a/tests/conditionalVault/unit/splitTokens.test.ts +++ b/tests/conditionalVault/unit/splitTokens.test.ts @@ -1,4 +1,5 @@ -import { sha256, ConditionalVaultClient } from "@metadaoproject/futarchy"; +import { sha256 } from "@metadaoproject/futarchy"; +import { ConditionalVaultClient } from "@metadaoproject/futarchy/v0.4"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { @@ -10,7 +11,8 @@ import { } from "spl-token-bankrun"; import * as anchor from "@coral-xyz/anchor"; import * as token from "@solana/spl-token"; -import { expectError } from "../../utils"; +import { expectError } from "../../utils.js"; +import { BN } from "bn.js"; export default function suite() { let vaultClient: ConditionalVaultClient; @@ -52,7 +54,7 @@ export default function suite() { question, vault, underlyingTokenMint, - new anchor.BN(1000), + new BN(1000), 2 ) .rpc(); @@ -83,7 +85,7 @@ export default function suite() { ); await vaultClient.vaultProgram.methods - .splitTokens(new anchor.BN(1000)) + .splitTokens(new BN(1000)) .accounts({ question, authority: this.payer.publicKey, @@ -126,7 +128,7 @@ export default function suite() { // Attempt to split tokens using the original vault but with the malicious vault's conditional token accounts await vaultClient.vaultProgram.methods - .splitTokens(new anchor.BN(1000)) + .splitTokens(new BN(1000)) .accounts({ question, authority: this.payer.publicKey, @@ -165,7 +167,7 @@ export default function suite() { ); await vaultClient - .splitTokensIx(question, vault, underlyingTokenMint, new anchor.BN(1000), 2) + .splitTokensIx(question, vault, underlyingTokenMint, new BN(1000), 2) .accounts({ vaultUnderlyingTokenAccount: invalidVaultUnderlyingTokenAccount, }) @@ -186,7 +188,7 @@ export default function suite() { ); await vaultClient.vaultProgram.methods - .splitTokens(new anchor.BN(1000)) + .splitTokens(new BN(1000)) .accounts({ question, authority: this.payer.publicKey, diff --git a/tests/main.test.ts b/tests/main.test.ts index 786251eb..7a6926b1 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -1,6 +1,6 @@ import conditionalVault from "./conditionalVault/main.test.js"; -// import amm from "./amm/main.test"; -// import autocrat from "./autocrat/autocrat"; +import amm from "./amm/main.test.js"; +import autocrat from "./autocrat/autocrat.js"; import { startAnchor } from "solana-bankrun"; import { BankrunProvider } from "anchor-bankrun"; @@ -140,5 +140,5 @@ before(async function () { }); describe("conditional_vault", conditionalVault); -// describe("amm", amm); -// describe("autocrat", autocrat); +describe("amm", amm); +describe("autocrat", autocrat); From a0d5942e466159e18f882008e424568d76831ee2 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Wed, 28 Aug 2024 00:00:00 +0000 Subject: [PATCH 49/52] Fix metadata URI --- .../add_metadata_to_conditional_tokens.rs | 11 ++++------ sdk/src/index.ts | 20 ------------------- sdk/src/v0.4/ConditionalVaultClient.ts | 4 ++-- sdk/src/v0.4/types/conditional_vault.ts | 4 ++-- .../addMetadataToConditionalTokens.test.ts | 5 ++--- 5 files changed, 10 insertions(+), 34 deletions(-) diff --git a/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs b/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs index dd603f23..0f2f50d5 100644 --- a/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs +++ b/programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs @@ -10,7 +10,7 @@ pub mod proph3t_deployer { pub struct AddMetadataToConditionalTokensArgs { pub name: String, pub symbol: String, - pub image: String, + pub uri: String, } #[derive(Accounts)] @@ -75,12 +75,9 @@ impl AddMetadataToConditionalTokens<'_> { create_metadata_accounts_v3( CpiContext::new(cpi_program, cpi_accounts).with_signer(signer_seeds), DataV2 { - name: args.name.clone(), - symbol: args.symbol.clone(), - uri: format!( - "data:,{{\"name\":\"{}\",\"symbol\":\"{}\",\"image\":\"{}\"}}", - args.name, args.symbol, args.image - ), + name: args.name, + symbol: args.symbol, + uri: args.uri, seller_fee_basis_points: 0, creators: None, collection: None, diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 0e363be3..bfa636c7 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -1,21 +1 @@ -// export * from "./v0.3"; -// export * from "./v0.4"; - -// import * as v0_3 from "./v0.3"; -// import * as v0_4 from "./v0.4"; - -// export const versions = { -// "0.3": v0_3, -// "0.4": v0_4, -// } as const; - -// export type VersionKey = keyof typeof versions; - -// export function getVersion(version: VersionKey) { -// return versions[version]; -// } - export { sha256 } from "@noble/hashes/sha256"; -// export { v0_3, v0_4 }; - -// export * from "./common/utils"; diff --git a/sdk/src/v0.4/ConditionalVaultClient.ts b/sdk/src/v0.4/ConditionalVaultClient.ts index f798eb93..c480b545 100644 --- a/sdk/src/v0.4/ConditionalVaultClient.ts +++ b/sdk/src/v0.4/ConditionalVaultClient.ts @@ -424,7 +424,7 @@ export class ConditionalVaultClient { index: number, name: string, symbol: string, - image: string + uri: string // underlyingTokenMint: PublicKey, // proposalNumber: number, // onFinalizeUri: string, @@ -457,7 +457,7 @@ export class ConditionalVaultClient { .addMetadataToConditionalTokens({ name, symbol, - image, + uri, }) .accounts({ payer: this.provider.publicKey, diff --git a/sdk/src/v0.4/types/conditional_vault.ts b/sdk/src/v0.4/types/conditional_vault.ts index 7bdf16f6..7acfbc7e 100644 --- a/sdk/src/v0.4/types/conditional_vault.ts +++ b/sdk/src/v0.4/types/conditional_vault.ts @@ -367,7 +367,7 @@ export type ConditionalVault = { type: "string"; }, { - name: "image"; + name: "uri"; type: "string"; } ]; @@ -870,7 +870,7 @@ export const IDL: ConditionalVault = { type: "string", }, { - name: "image", + name: "uri", type: "string", }, ], diff --git a/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts b/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts index 9231df29..efecd3f7 100644 --- a/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts +++ b/tests/conditionalVault/unit/addMetadataToConditionalTokens.test.ts @@ -47,7 +47,7 @@ export default function suite() { i, `Outcome ${i}`, `OUT${i}`, - `https://example.com/image${i}.png` + `https://example.com/uri${i}.png` ).rpc(); } } @@ -65,8 +65,7 @@ export default function suite() { const metadata = metadataSerializer.deserialize(storedMetadata.data)[0]; assert.equal(metadata.name, `Outcome ${i}`); assert.equal(metadata.symbol, `OUT${i}`); - const expectedUri = `data:,{"name":"${metadata.name}","symbol":"${metadata.symbol}","image":"https://example.com/image${i}.png"}`; - assert.equal(metadata.uri, expectedUri); + assert.equal(metadata.uri, `https://example.com/uri${i}.png`); } } From 569b04dd8287e5495a9df30457f948adc2848499 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Wed, 28 Aug 2024 00:00:00 +0000 Subject: [PATCH 50/52] v0.4.0-alpha.1 --- sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/package.json b/sdk/package.json index 5cfc2255..f74e86e4 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@metadaoproject/futarchy", - "version": "0.4.0-alpha.0", + "version": "0.4.0-alpha.1", "type": "module", "main": "dist/index.js", "module": "dist/index.js", From 7d376cfe8564b0027302f167df17007ad399357f Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Wed, 28 Aug 2024 00:00:00 +0000 Subject: [PATCH 51/52] Use new version of SDK --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c4c0925a..250696d6 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ }, "dependencies": { "@coral-xyz/anchor": "0.29.0", - "@metadaoproject/futarchy": "0.4.0-alpha.0", + "@metadaoproject/futarchy": "0.4.0-alpha.1", "@metaplex-foundation/mpl-token-metadata": "^3.2.0", "@metaplex-foundation/umi": "^0.9.1", "@metaplex-foundation/umi-bundle-defaults": "^0.9.1", From cdd9812d0588d99b270d19f45423e4519d9a8c82 Mon Sep 17 00:00:00 2001 From: metaproph3t Date: Thu, 29 Aug 2024 00:00:00 +0000 Subject: [PATCH 52/52] Use `require!` over `assert!` --- programs/amm/src/error.rs | 2 ++ .../amm/src/instructions/add_liquidity.rs | 2 +- .../amm/src/instructions/crank_that_twap.rs | 2 +- .../amm/src/instructions/remove_liquidity.rs | 4 +-- programs/amm/src/instructions/swap.rs | 2 +- programs/amm/src/state/amm.rs | 28 +++++++++---------- .../src/instructions/finalize_proposal.rs | 8 +++--- programs/conditional_vault/src/error.rs | 2 ++ .../src/instructions/merge_tokens.rs | 18 ++++++------ .../src/instructions/redeem_tokens.rs | 20 +++++++------ .../src/instructions/split_tokens.rs | 11 ++++---- programs/conditional_vault/src/lib.rs | 1 - .../src/state/conditional_vault.rs | 2 +- 13 files changed, 55 insertions(+), 47 deletions(-) diff --git a/programs/amm/src/error.rs b/programs/amm/src/error.rs index 193867e4..f584cfda 100644 --- a/programs/amm/src/error.rs +++ b/programs/amm/src/error.rs @@ -2,6 +2,8 @@ use anchor_lang::prelude::*; #[error_code] pub enum AmmError { + #[msg("An assertion failed")] + AssertFailed, #[msg("Can't get a TWAP before some observations have been stored")] NoSlotsPassed, #[msg("Can't swap through a pool without token reserves on either side")] diff --git a/programs/amm/src/instructions/add_liquidity.rs b/programs/amm/src/instructions/add_liquidity.rs index c0bc8c39..00b1e461 100644 --- a/programs/amm/src/instructions/add_liquidity.rs +++ b/programs/amm/src/instructions/add_liquidity.rs @@ -46,7 +46,7 @@ impl AddOrRemoveLiquidity<'_> { AmmError::InsufficientBalance ); - amm.update_twap(Clock::get()?.slot); + amm.update_twap(Clock::get()?.slot)?; // airlifted from uniswap v1: // https://github.com/Uniswap/v1-contracts/blob/c10c08d81d6114f694baa8bd32f555a40f6264da/contracts/uniswap_exchange.vy#L48 diff --git a/programs/amm/src/instructions/crank_that_twap.rs b/programs/amm/src/instructions/crank_that_twap.rs index 8a8b10d1..3e4b4f39 100644 --- a/programs/amm/src/instructions/crank_that_twap.rs +++ b/programs/amm/src/instructions/crank_that_twap.rs @@ -12,7 +12,7 @@ impl CrankThatTwap<'_> { pub fn handle(ctx: Context) -> Result<()> { let CrankThatTwap { amm } = ctx.accounts; - amm.update_twap(Clock::get()?.slot); + amm.update_twap(Clock::get()?.slot)?; Ok(()) } diff --git a/programs/amm/src/instructions/remove_liquidity.rs b/programs/amm/src/instructions/remove_liquidity.rs index b7e04e90..20d3209e 100644 --- a/programs/amm/src/instructions/remove_liquidity.rs +++ b/programs/amm/src/instructions/remove_liquidity.rs @@ -41,13 +41,13 @@ impl AddOrRemoveLiquidity<'_> { require!(lp_tokens_to_burn > 0, AmmError::ZeroLiquidityRemove); - amm.update_twap(Clock::get()?.slot); + amm.update_twap(Clock::get()?.slot)?; // airlifted from uniswap v1: // https://github.com/Uniswap/v1-contracts/blob/c10c08d81d6114f694baa8bd32f555a40f6264da/contracts/uniswap_exchange.vy#L83 let total_liquidity = lp_mint.supply; - assert!(total_liquidity > 0); + require_gt!(total_liquidity, 0, AmmError::AssertFailed); let (base_to_withdraw, quote_to_withdraw) = amm.get_base_and_quote_withdrawable(lp_tokens_to_burn, total_liquidity); diff --git a/programs/amm/src/instructions/swap.rs b/programs/amm/src/instructions/swap.rs index b978ee41..a3bdec4f 100644 --- a/programs/amm/src/instructions/swap.rs +++ b/programs/amm/src/instructions/swap.rs @@ -78,7 +78,7 @@ impl Swap<'_> { require!(input_amount > 0, AmmError::ZeroSwapAmount); - amm.update_twap(Clock::get()?.slot); + amm.update_twap(Clock::get()?.slot)?; let output_amount = amm.swap(input_amount, swap_type)?; diff --git a/programs/amm/src/state/amm.rs b/programs/amm/src/state/amm.rs index 25a9624b..e7221bbd 100644 --- a/programs/amm/src/state/amm.rs +++ b/programs/amm/src/state/amm.rs @@ -166,7 +166,7 @@ impl Amm { let slots_passed = (self.oracle.last_updated_slot - self.created_at_slot) as u128; require_neq!(slots_passed, 0, AmmError::NoSlotsPassed); - assert!(self.oracle.aggregator != 0); + require!(self.oracle.aggregator != 0, AmmError::AssertFailed); Ok(self.oracle.aggregator / slots_passed) } @@ -175,7 +175,7 @@ impl Amm { /// have been made. /// /// Returns an observation if one was recorded. - pub fn update_twap(&mut self, current_slot: Slot) -> Option { + pub fn update_twap(&mut self, current_slot: Slot) -> Result> { let oracle = &mut self.oracle; // a manipulator is likely to be "bursty" with their usage, such as a // validator who abuses their slots to manipulate the TWAP. @@ -196,11 +196,11 @@ impl Amm { // that trades near $1500 and you allow $25 updates per minute, it can double // over an hour. if current_slot < oracle.last_updated_slot + ONE_MINUTE_IN_SLOTS { - return None; + return Ok(None); } if self.base_amount == 0 || self.quote_amount == 0 { - return None; + return Ok(None); } // we store prices as quote units / base units scaled by 1e12. @@ -244,32 +244,32 @@ impl Amm { initial_observation: oracle.initial_observation, }; - assert!(new_oracle.last_updated_slot > oracle.last_updated_slot); + require!(new_oracle.last_updated_slot > oracle.last_updated_slot, AmmError::AssertFailed); // assert that the new observation is between price and last observation match price.cmp(&oracle.last_observation) { Ordering::Greater => { - assert!(new_observation > oracle.last_observation); - assert!(new_observation <= price); + require!(new_observation > oracle.last_observation, AmmError::AssertFailed); + require!(new_observation <= price, AmmError::AssertFailed); } Ordering::Equal => { - assert!(new_observation == price); + require!(new_observation == price, AmmError::AssertFailed); } Ordering::Less => { - assert!(new_observation < oracle.last_observation); - assert!(new_observation >= price); + require!(new_observation < oracle.last_observation, AmmError::AssertFailed); + require!(new_observation >= price, AmmError::AssertFailed); } } *oracle = new_oracle; - Some(new_observation) + Ok(Some(new_observation)) } pub fn invariant(&self) -> Result<()> { let oracle = &self.oracle; - assert!(oracle.last_price <= MAX_PRICE); - assert!(oracle.last_observation <= MAX_PRICE); + require!(oracle.last_price <= MAX_PRICE, AmmError::AssertFailed); + require!(oracle.last_observation <= MAX_PRICE, AmmError::AssertFailed); Ok(()) } @@ -359,7 +359,7 @@ mod simple_amm_tests { let slots_until_overflow = u128::MAX / (u64::MAX as u128 * PRICE_SCALE); amm.update_twap(slots_until_overflow as u64); - assert!(amm.oracle.aggregator > MAX_PRICE * 18_400_000); + require!(amm.oracle.aggregator > MAX_PRICE * 18_400_000); assert_ne!(amm.oracle.aggregator, u128::MAX); amm_clone.update_twap(slots_until_overflow as u64 + 1); diff --git a/programs/autocrat/src/instructions/finalize_proposal.rs b/programs/autocrat/src/instructions/finalize_proposal.rs index 71709731..94190296 100644 --- a/programs/autocrat/src/instructions/finalize_proposal.rs +++ b/programs/autocrat/src/instructions/finalize_proposal.rs @@ -178,12 +178,12 @@ impl FinalizeProposal<'_> { // match new_proposal_state { // ProposalState::Passed => { - // assert!(base_vault.status == VaultStatus::Finalized); - // assert!(quote_vault.status == VaultStatus::Finalized); + // require!(base_vault.status == VaultStatus::Finalized); + // require!(quote_vault.status == VaultStatus::Finalized); // } // ProposalState::Failed => { - // assert!(base_vault.status == VaultStatus::Reverted); - // assert!(quote_vault.status == VaultStatus::Reverted); + // require!(base_vault.status == VaultStatus::Reverted); + // require!(quote_vault.status == VaultStatus::Reverted); // } // _ => unreachable!("Encountered an unexpected proposal state"), // } diff --git a/programs/conditional_vault/src/error.rs b/programs/conditional_vault/src/error.rs index a7dab727..2c810f4d 100644 --- a/programs/conditional_vault/src/error.rs +++ b/programs/conditional_vault/src/error.rs @@ -2,6 +2,8 @@ use super::*; #[error_code] pub enum VaultError { + #[msg("An assertion failed")] + AssertFailed, #[msg("Insufficient underlying token balance to mint this amount of conditional tokens")] InsufficientUnderlyingTokens, #[msg("Insufficient conditional token balance to merge this `amount`")] diff --git a/programs/conditional_vault/src/instructions/merge_tokens.rs b/programs/conditional_vault/src/instructions/merge_tokens.rs index 5e86ebdc..8d48b0fa 100644 --- a/programs/conditional_vault/src/instructions/merge_tokens.rs +++ b/programs/conditional_vault/src/instructions/merge_tokens.rs @@ -65,13 +65,15 @@ impl<'info, 'c: 'info> InteractWithVault<'info> { ctx.accounts.user_underlying_token_account.reload()?; ctx.accounts.vault_underlying_token_account.reload()?; - assert!( - ctx.accounts.user_underlying_token_account.amount - == pre_user_underlying_balance + amount + require_eq!( + ctx.accounts.user_underlying_token_account.amount, + pre_user_underlying_balance + amount, + VaultError::AssertFailed ); - assert!( - ctx.accounts.vault_underlying_token_account.amount - == pre_vault_underlying_balance - amount + require_eq!( + ctx.accounts.vault_underlying_token_account.amount, + pre_vault_underlying_balance - amount, + VaultError::AssertFailed ); for (mint, expected_supply) in conditional_token_mints @@ -79,7 +81,7 @@ impl<'info, 'c: 'info> InteractWithVault<'info> { .zip(expected_future_supplies.iter()) { mint.reload()?; - assert!(mint.supply == *expected_supply); + require_eq!(mint.supply, *expected_supply, VaultError::AssertFailed); } for (account, expected_balance) in user_conditional_token_accounts @@ -87,7 +89,7 @@ impl<'info, 'c: 'info> InteractWithVault<'info> { .zip(expected_future_balances.iter()) { account.reload()?; - assert!(account.amount == *expected_balance); + require_eq!(account.amount, *expected_balance, VaultError::AssertFailed); } ctx.accounts.vault.invariant( diff --git a/programs/conditional_vault/src/instructions/redeem_tokens.rs b/programs/conditional_vault/src/instructions/redeem_tokens.rs index 389a0a86..dd1c300b 100644 --- a/programs/conditional_vault/src/instructions/redeem_tokens.rs +++ b/programs/conditional_vault/src/instructions/redeem_tokens.rs @@ -83,24 +83,26 @@ impl<'info, 'c: 'info> InteractWithVault<'info> { total_redeemable, )?; - assert!(max_redeemable >= total_redeemable); + require_gte!(max_redeemable, total_redeemable, VaultError::AssertFailed); ctx.accounts.user_underlying_token_account.reload()?; ctx.accounts.vault_underlying_token_account.reload()?; - assert!( - ctx.accounts.user_underlying_token_account.amount - == user_underlying_balance_before + total_redeemable + require_eq!( + ctx.accounts.user_underlying_token_account.amount, + user_underlying_balance_before + total_redeemable, + VaultError::AssertFailed ); - assert!( - ctx.accounts.vault_underlying_token_account.amount - == vault_underlying_balance_before - total_redeemable + require_eq!( + ctx.accounts.vault_underlying_token_account.amount, + vault_underlying_balance_before - total_redeemable, + VaultError::AssertFailed ); for acc in user_conditional_token_accounts.iter_mut() { acc.reload()?; - assert!(acc.amount == 0); + require_eq!(acc.amount, 0, VaultError::AssertFailed); } for (mint, expected_supply) in conditional_token_mints @@ -108,7 +110,7 @@ impl<'info, 'c: 'info> InteractWithVault<'info> { .zip(expected_future_supplies.iter()) { mint.reload()?; - assert!(mint.supply == *expected_supply); + require_eq!(mint.supply, *expected_supply, VaultError::AssertFailed); } ctx.accounts.vault.invariant( diff --git a/programs/conditional_vault/src/instructions/split_tokens.rs b/programs/conditional_vault/src/instructions/split_tokens.rs index 26e80762..b3592121 100644 --- a/programs/conditional_vault/src/instructions/split_tokens.rs +++ b/programs/conditional_vault/src/instructions/split_tokens.rs @@ -58,19 +58,20 @@ impl<'info, 'c: 'info> InteractWithVault<'info> { } ctx.accounts.vault_underlying_token_account.reload()?; - assert!( - ctx.accounts.vault_underlying_token_account.amount - == pre_vault_underlying_balance + amount + require_eq!( + ctx.accounts.vault_underlying_token_account.amount, + pre_vault_underlying_balance + amount, + VaultError::AssertFailed ); for (i, mint) in conditional_token_mints.iter_mut().enumerate() { mint.reload()?; - assert!(mint.supply == pre_conditional_mint_supplies[i] + amount); + require_eq!(mint.supply, pre_conditional_mint_supplies[i] + amount, VaultError::AssertFailed); } for (i, acc) in user_conditional_token_accounts.iter_mut().enumerate() { acc.reload()?; - assert!(acc.amount == pre_conditional_user_balances[i] + amount); + require_eq!(acc.amount, pre_conditional_user_balances[i] + amount, VaultError::AssertFailed); } ctx.accounts.vault.invariant( diff --git a/programs/conditional_vault/src/lib.rs b/programs/conditional_vault/src/lib.rs index 839bcb74..818ca252 100644 --- a/programs/conditional_vault/src/lib.rs +++ b/programs/conditional_vault/src/lib.rs @@ -7,7 +7,6 @@ use anchor_spl::{ associated_token::AssociatedToken, token::{self, Burn, Mint, MintTo, Token, TokenAccount, Transfer}, }; -// use mpl_token_metadata::state::DataV2; pub mod error; pub mod instructions; diff --git a/programs/conditional_vault/src/state/conditional_vault.rs b/programs/conditional_vault/src/state/conditional_vault.rs index 3bd4f2da..994c171d 100644 --- a/programs/conditional_vault/src/state/conditional_vault.rs +++ b/programs/conditional_vault/src/state/conditional_vault.rs @@ -51,7 +51,7 @@ impl ConditionalVault { .sum::() }; - assert!(vault_underlying_balance >= max_possible_liability); + require_gte!(vault_underlying_balance, max_possible_liability, VaultError::AssertFailed); Ok(()) }