import assert from "assert"; import { splBinaryOraclePairProgram } from "@coral-xyz/spl-binary-oracle-pair"; import { splTokenProgram } from "@coral-xyz/spl-token"; import { BN } from "@coral-xyz/anchor"; import { Keypair, PublicKey, SYSVAR_CLOCK_PUBKEY, SYSVAR_RENT_PUBKEY, } from "@solana/web3.js"; import { SPL_BINARY_ORACLE_PAIR_PROGRAM_ID, SPL_TOKEN_PROGRAM_ID, } from "../constants"; import { createMint, createTokenAccount, getProvider, loadKp, sendAndConfirmTx, sleep, test, } from "../utils"; export async function binaryOraclePairTests() { const provider = await getProvider(); const program = splBinaryOraclePairProgram({ provider, programId: SPL_BINARY_ORACLE_PAIR_PROGRAM_ID, }); const tokenProgram = splTokenProgram({ provider, programId: SPL_TOKEN_PROGRAM_ID, }); const kp = await loadKp(); let mintEndSlot: number; let decideEndSlot: number; const userTransferAuthorityKp = new Keypair(); let poolPk: PublicKey; let poolAuthorityPk: PublicKey; let poolDepositMintPk: PublicKey; let poolDepositTokenAccountPk: PublicKey; let tokenPassMintPk: PublicKey; let tokenFailMintPk: PublicKey; let userPoolTokenAccountPk: PublicKey; let userTokenPassAccountPk: PublicKey; let userTokenFailAccountPk: PublicKey; async function initPool() { const poolKp = new Keypair(); poolPk = poolKp.publicKey; const createPoolAccountIx = await program.account.pool.createInstruction( poolKp ); // Accounts that are expected to be initialized poolDepositMintPk = await createMint(); // Accounts that are expected to be uninitialized const depositTokenAccountKp = new Keypair(); poolDepositTokenAccountPk = depositTokenAccountKp.publicKey; const createDepositTokenAccountIx = await tokenProgram.account.account.createInstruction( depositTokenAccountKp ); const tokenPassMintKp = new Keypair(); tokenPassMintPk = tokenPassMintKp.publicKey; const createTokenPassMintIx = await tokenProgram.account.mint.createInstruction(tokenPassMintKp); const tokenFailMintKp = new Keypair(); tokenFailMintPk = tokenFailMintKp.publicKey; const createTokenFailMintIx = await tokenProgram.account.mint.createInstruction(tokenFailMintKp); const [authorityPk, bump] = await PublicKey.findProgramAddress( [poolPk.toBuffer()], program.programId ); poolAuthorityPk = authorityPk; // Get current slot const currentSlot = await provider.connection.getSlot(); mintEndSlot = currentSlot + 10; decideEndSlot = currentSlot + 20; const initPoolIx = await program.methods .initPool(new BN(mintEndSlot), new BN(decideEndSlot), bump) .accounts({ pool: poolPk, authority: poolAuthorityPk, decider: kp.publicKey, depositTokenMint: poolDepositMintPk, depositAccount: poolDepositTokenAccountPk, tokenPassMint: tokenPassMintPk, tokenFailMint: tokenFailMintPk, rent: SYSVAR_RENT_PUBKEY, tokenProgram: tokenProgram.programId, }) .instruction(); await sendAndConfirmTx( [ createPoolAccountIx, createDepositTokenAccountIx, createTokenPassMintIx, createTokenFailMintIx, initPoolIx, ], [kp, poolKp, depositTokenAccountKp, tokenPassMintKp, tokenFailMintKp] ); } async function deposit() { userPoolTokenAccountPk = await createTokenAccount(poolDepositMintPk); userTokenPassAccountPk = await createTokenAccount(tokenPassMintPk); userTokenFailAccountPk = await createTokenAccount(tokenFailMintPk); const depositAmount = 100; const mintIx = await tokenProgram.methods .mintTo(new BN(depositAmount)) .accounts({ account: userPoolTokenAccountPk, mint: poolDepositMintPk, owner: kp.publicKey, }) .instruction(); const approveIx = await tokenProgram.methods .approve(new BN(depositAmount)) .accounts({ delegate: userTransferAuthorityKp.publicKey, owner: kp.publicKey, source: userPoolTokenAccountPk, }) .instruction(); const depositIx = await program.methods .deposit(new BN(depositAmount)) .accounts({ pool: poolPk, authority: poolAuthorityPk, userTransferAuthority: userTransferAuthorityKp.publicKey, userTokenAccount: userPoolTokenAccountPk, poolDepositTokenAccount: poolDepositTokenAccountPk, tokenPassMint: tokenPassMintPk, tokenFailMint: tokenFailMintPk, tokenPassDestinationAccount: userTokenPassAccountPk, tokenFailDestinationAccount: userTokenFailAccountPk, clock: SYSVAR_CLOCK_PUBKEY, tokenProgram: tokenProgram.programId, }) .instruction(); await sendAndConfirmTx( [mintIx, approveIx, depositIx], [kp, userTransferAuthorityKp] ); } async function withdraw() { const withdrawAmount = 50; const approveUserPassAccountIx = await tokenProgram.methods .approve(new BN(withdrawAmount)) .accounts({ delegate: userTransferAuthorityKp.publicKey, owner: kp.publicKey, source: userTokenPassAccountPk, }) .instruction(); const approveUserFailAccountIx = await tokenProgram.methods .approve(new BN(withdrawAmount)) .accounts({ delegate: userTransferAuthorityKp.publicKey, owner: kp.publicKey, source: userTokenFailAccountPk, }) .instruction(); const withdrawIx = await program.methods .withdraw(new BN(withdrawAmount)) .accounts({ pool: poolPk, authority: poolAuthorityPk, userTransferAuthority: userTransferAuthorityKp.publicKey, poolDepositTokenAccount: poolDepositTokenAccountPk, tokenPassUserAccount: userTokenPassAccountPk, tokenFailUserAccount: userTokenFailAccountPk, tokenPassMint: tokenPassMintPk, tokenFailMint: tokenFailMintPk, userTokenDestinationAccount: userPoolTokenAccountPk, clock: SYSVAR_CLOCK_PUBKEY, tokenProgram: tokenProgram.programId, }) .instruction(); await sendAndConfirmTx( [approveUserPassAccountIx, approveUserFailAccountIx, withdrawIx], [kp, userTransferAuthorityKp] ); } async function decide() { // Call only succeeds once and if current slot > mint_end slot AND < decide_end slot const currentSlot = await provider.connection.getSlot(); if (currentSlot > mintEndSlot) { if (currentSlot < decideEndSlot) { await program.methods .decide(true) .accounts({ pool: poolPk, decider: kp.publicKey, clock: SYSVAR_CLOCK_PUBKEY, }) .rpc(); } } else { await sleep(); await decide(); } } async function fetchPool() { const pool = await program.account.pool.fetch(poolPk); assert(pool.decider.equals(kp.publicKey)); assert(pool.tokenPassMint.equals(tokenPassMintPk)); assert(pool.tokenFailMint.equals(tokenFailMintPk)); assert(pool.tokenProgramId.equals(tokenProgram.programId)); assert(pool.mintEndSlot.toNumber() === mintEndSlot); assert(pool.decideEndSlot.toNumber() === decideEndSlot); } await test(initPool); await test(deposit); await test(withdraw); await test(decide); await test(fetchPool); }