import "mocha"; import { SwitchboardPermission } from "../src/generated/index.js"; import { OracleAccount, PermissionAccount, QueueAccount, TransactionObject, types, } from "../src/index.js"; import * as sbv2 from "../src/index.js"; import { setupTest, TestContext } from "./utils.js"; import { Keypair, LAMPORTS_PER_SOL, SystemProgram } from "@solana/web3.js"; import assert from "assert"; describe("OracleAccount Tests", () => { let ctx: TestContext; const queueAuthority = Keypair.generate(); const oracleAuthority = Keypair.generate(); let queueAccount: QueueAccount; let queue: types.OracleQueueAccountData; let oracleAccount: OracleAccount; let oracle: types.OracleAccountData; let oraclePermissionAccount: PermissionAccount; before(async () => { ctx = await setupTest(); [queueAccount] = await sbv2.QueueAccount.create(ctx.program, { name: "q1", metadata: "", queueSize: 2, reward: 0.0025, minStake: 2, oracleTimeout: 60, slashingEnabled: false, unpermissionedFeeds: false, unpermissionedVrf: true, enableBufferRelayers: false, authority: queueAuthority.publicKey, }); queue = await queueAccount.loadData(); assert( queue.authority.equals(queueAuthority.publicKey), "Incorrect queue authority" ); const transferToOracleAuthorityTxn = new TransactionObject( ctx.payer.publicKey, [ SystemProgram.transfer({ fromPubkey: ctx.payer.publicKey, toPubkey: oracleAuthority.publicKey, lamports: LAMPORTS_PER_SOL, }), ], [] ); await ctx.program.signAndSend(transferToOracleAuthorityTxn); }); it("Creates an oracle account without permissions enabled", async () => { [oracleAccount] = await queueAccount.createOracle({ name: "oracle-1", metadata: "oracle-1", queueAuthority, enable: false, authority: oracleAuthority, }); oracle = await oracleAccount.loadData(); assert( oracle.oracleAuthority.equals(oracleAuthority.publicKey), "Incorrect oracle authority" ); [oraclePermissionAccount] = PermissionAccount.fromSeed( ctx.program, queueAuthority.publicKey, queueAccount.publicKey, oracleAccount.publicKey ); const oraclePermissions = await oraclePermissionAccount.loadData(); assert( oraclePermissions.permissions === 0, `Incorrect oracle permissions, expected PERMIT_NONE (0), received ${oraclePermissions.permissions}` ); }); it("Oracle fails to heartbeat if permissions are not enabled", async () => { await assert.rejects(async () => { await oracleAccount.heartbeat({ queueAccount, authority: oracleAuthority, }); }, new RegExp(/PermissionDenied|6035|0x1793/g)); }); it("Queue authority grants the oracle permissions", async () => { await oraclePermissionAccount.set({ queueAuthority, enable: true, permission: new SwitchboardPermission.PermitOracleHeartbeat(), }); const oraclePermissions = await oraclePermissionAccount.loadData(); assert( oraclePermissions.permissions === new SwitchboardPermission.PermitOracleHeartbeat().discriminator, `Incorrect oracle permissions, expected PERMIT_ORACLE_HEARTBEAT (${ new SwitchboardPermission.PermitOracleHeartbeat().discriminator }), received ${oraclePermissions.permissions}` ); }); it("Oracle deposits funds to its staking wallet", async () => { const queueMinStake = ctx.program.mint.fromTokenAmountBN(queue.minStake); await oracleAccount.stake({ stakeAmount: queueMinStake, }); const oracleBalance = await oracleAccount.fetchBalance(); assert( oracleBalance === queueMinStake, `Oracle stake amount mismatch, expected ${queueMinStake}, received ${oracleBalance}` ); }); it("Oracle heartbeats on-chain", async () => { await oracleAccount.heartbeat({ queueAccount, authority: oracleAuthority }); }); it("Oracle withdraws from staking wallet and unwraps funds", async () => { const queueMinStake = ctx.program.mint.fromTokenAmountBN(queue.minStake); const withdrawAmount = queueMinStake / 10; const initialOracleStakingBalance = await oracleAccount.fetchBalance(); const initialOracleAuthorityBalance = (await ctx.program.connection.getBalance(oracleAuthority.publicKey)) / LAMPORTS_PER_SOL; await oracleAccount.withdraw({ amount: withdrawAmount, authority: oracleAuthority, unwrap: true, }); const finalOracleStakingBalance = await oracleAccount.fetchBalance(); const finalOracleAuthorityBalance = (await ctx.program.connection.getBalance(oracleAuthority.publicKey)) / LAMPORTS_PER_SOL; // round to two decimal places const stakingBalanceDiff = ctx.round( finalOracleStakingBalance - initialOracleStakingBalance, 2 ); const authorityBalanceDiff = ctx.round( finalOracleAuthorityBalance - initialOracleAuthorityBalance, 2 ); assert( -1 * withdrawAmount === stakingBalanceDiff, `Oracle unstake difference mismatch, expected ${withdrawAmount}, received ${stakingBalanceDiff}` ); assert( withdrawAmount === authorityBalanceDiff, `Oracle authority balance difference mismatch, expected ${withdrawAmount}, received ${authorityBalanceDiff}` ); }); });