diff --git a/javascript/solana.js/src/accounts/leaseAccount.ts b/javascript/solana.js/src/accounts/leaseAccount.ts index 49fd598..3e5635b 100644 --- a/javascript/solana.js/src/accounts/leaseAccount.ts +++ b/javascript/solana.js/src/accounts/leaseAccount.ts @@ -454,7 +454,7 @@ export class LeaseAccount extends Account { params: { amount: number; unwrap?: boolean; - withdrawWallet?: PublicKey; + withdrawWallet: PublicKey; withdrawAuthority?: Keypair; } ): Promise { @@ -464,20 +464,9 @@ export class LeaseAccount extends Account { const withdrawAuthority = params.withdrawAuthority ? params.withdrawAuthority.publicKey : payer; - const withdrawWallet = params.withdrawWallet - ? params.withdrawWallet - : this.program.mint.getAssociatedAddress(withdrawAuthority); + const withdrawWallet = params.withdrawWallet; - // create token wallet if it doesnt exist - const withdrawWalletAccountInfo = - await this.program.connection.getAccountInfo(withdrawWallet); - if (withdrawWalletAccountInfo === null) { - const [createUserTxn] = this.program.mint.createAssocatedUserInstruction( - payer, - withdrawAuthority - ); - txns.push(createUserTxn); - } + // // create token wallet if it doesnt exist const lease = await this.loadData(); const accountInfos = await this.program.connection.getMultipleAccountsInfo([ @@ -558,7 +547,7 @@ export class LeaseAccount extends Account { public async withdraw(params: { amount: number; unwrap?: boolean; - withdrawWallet?: PublicKey; + withdrawWallet: PublicKey; withdrawAuthority?: Keypair; }): Promise { const withdrawTxn = await this.withdrawInstruction( diff --git a/javascript/solana.js/test/aggregator.spec.ts b/javascript/solana.js/test/aggregator.spec.ts index 28090bc..6a01a5e 100644 --- a/javascript/solana.js/test/aggregator.spec.ts +++ b/javascript/solana.js/test/aggregator.spec.ts @@ -232,6 +232,7 @@ describe('Aggregator Tests', () => { await leaseAccount.withdraw({ amount: 1, unwrap: true, + withdrawWallet: userTokenAddress, }); const finalBalance = await leaseAccount.getBalance(); @@ -257,88 +258,6 @@ describe('Aggregator Tests', () => { } }); - it('Transfers aggregator to a new queue', async () => { - const newQueueAuthority = Keypair.generate(); - - const [newQueue] = await sbv2.QueueAccount.create(ctx.program, { - name: 'new-queue', - metadata: '', - authority: newQueueAuthority.publicKey, - queueSize: 1, - reward: 0, - minStake: 0, - oracleTimeout: 86400, - slashingEnabled: false, - unpermissionedFeeds: true, - unpermissionedVrf: true, - enableBufferRelayers: false, - }); - - const [aggregatorAccount] = await queueAccount.createFeed({ - queueAuthority: queueAuthority, - batchSize: 1, - minRequiredOracleResults: 1, - minRequiredJobResults: 1, - minUpdateDelaySeconds: 60, - fundAmount: 1.25, - enable: true, - jobs: [ - { - weight: 1, - data: OracleJob.encodeDelimited( - OracleJob.fromObject({ - tasks: [ - { - valueTask: { - value: 1, - }, - }, - ], - }) - ).finish(), - }, - ], - }); - await aggregatorAccount.loadData(); - - await aggregatorAccount.transfer({ - newQueue, - enable: true, - queueAuthority: newQueueAuthority, - }); - - const aggregator = await aggregatorAccount.loadData(); - assert( - aggregator.queuePubkey.equals(newQueue.publicKey), - `Aggregator queue mismatch, expected ${newQueue.publicKey}, received ${aggregator.queuePubkey}` - ); - - const [newLeaseAccount] = LeaseAccount.fromSeed( - ctx.program, - newQueue.publicKey, - aggregatorAccount.publicKey - ); - const balance = await newLeaseAccount.getBalance(); - assert( - balance === 1.25, - `Aggregator did not transfer full lease balance, expected 1.25, received ${balance}` - ); - - const [newPermissionAccount] = PermissionAccount.fromSeed( - ctx.program, - newQueueAuthority.publicKey, - newQueue.publicKey, - aggregatorAccount.publicKey - ); - const newPermission = await newPermissionAccount.loadData(); - assert( - newPermission.permissions === PermitOracleQueueUsage.discriminator + 1, - `Aggregator permission mismatch, expected ${ - PermitOracleQueueUsage.discriminator + 1 - }, received ${newPermission.permissions}` - ); - }); - it("Adds job, updates it's config, then removes it from aggregator", async () => { const aggregatorKeypair = Keypair.generate(); const aggregatorAuthority = Keypair.generate(); diff --git a/javascript/solana.js/test/transfer.spec.ts b/javascript/solana.js/test/transfer.spec.ts new file mode 100644 index 0000000..82a9873 --- /dev/null +++ b/javascript/solana.js/test/transfer.spec.ts @@ -0,0 +1,239 @@ +/* eslint-disable no-unused-vars */ +import 'mocha'; + +import { setupTest, TestContext } from './utilts'; +import { Keypair } from '@solana/web3.js'; +import { AggregatorAccount, CrankAccount, QueueAccount } from '../src'; +import { OracleJob } from '@switchboard-xyz/common'; +import { assert } from 'console'; + +describe('Transfer Tests', () => { + let ctx: TestContext; + + // const freshUser = Keypair.generate(); + + const origQueueAuthority = Keypair.generate(); + let origQueueAccount: QueueAccount; + let origCrankAccount: CrankAccount; + + const newQueueAuthority = Keypair.generate(); + let newQueueAccount: QueueAccount; + let newCrankAccount: CrankAccount; + + const aggregatorAuthority = Keypair.generate(); + let aggregatorAccount: AggregatorAccount; + + before(async () => { + ctx = await setupTest(); + + const [accounts, signatures] = await ctx.program.createNetwork({ + name: 'Queue-1', + reward: 0, + minStake: 0, + unpermissionedFeeds: false, + unpermissionedVrf: false, + authority: origQueueAuthority.publicKey, + oracles: [ + { name: 'Oracle-1', enable: true, queueAuthority: origQueueAuthority }, + ], + cranks: [{ name: 'Crank-1', maxRows: 100 }], + }); + if (accounts.oracles.length < 1) { + throw new Error(`Failed to create an oracle`); + } + if (accounts.cranks.length < 1) { + throw new Error(`Failed to create a crank`); + } + origQueueAccount = accounts.queueAccount; + origCrankAccount = accounts.cranks[0]; + await accounts.oracles[0].account.heartbeat({ + queueAccount: accounts.queueAccount, + queueAuthority: origQueueAuthority.publicKey, + }); + + const [accounts2, signatures2] = await ctx.program.createNetwork({ + name: 'Queue-2', + reward: 0, + minStake: 0, + unpermissionedFeeds: false, + unpermissionedVrf: false, + authority: newQueueAuthority.publicKey, + oracles: [ + { name: 'Oracle-2', enable: true, queueAuthority: newQueueAuthority }, + ], + cranks: [{ name: 'Crank-2', maxRows: 100 }], + }); + if (accounts2.oracles.length < 1) { + throw new Error(`Failed to create an oracle`); + } + if (accounts2.cranks.length < 1) { + throw new Error(`Failed to create a crank`); + } + newQueueAccount = accounts2.queueAccount; + newCrankAccount = accounts2.cranks[0]; + await accounts2.oracles[0].account.heartbeat({ + queueAccount: accounts2.queueAccount, + queueAuthority: newQueueAuthority.publicKey, + }); + }); + + it('Creates an aggregator on the orig queue and crank', async () => { + [aggregatorAccount] = await origQueueAccount.createFeed({ + name: 'Aggregator-1', + authority: aggregatorAuthority, + batchSize: 1, + minRequiredOracleResults: 1, + minRequiredJobResults: 1, + minUpdateDelaySeconds: 10, + crankPubkey: origCrankAccount.publicKey, + fundAmount: 0.65, + enable: true, + queueAuthority: origQueueAuthority, + jobs: [ + { + data: OracleJob.encodeDelimited( + OracleJob.fromObject({ + tasks: [ + { + valueTask: { + value: 1, + }, + }, + ], + }) + ).finish(), + }, + ], + }); + + const aggregator = await aggregatorAccount.loadData(); + const accounts = await aggregatorAccount.fetchAccounts( + aggregator, + origQueueAccount + ); + + assert( + accounts.aggregator.data.queuePubkey.equals(origQueueAccount.publicKey), + `Incorrect queue, expected ${origQueueAccount.publicKey}, received ${accounts.aggregator.data.queuePubkey}` + ); + assert( + accounts.aggregator.data.crankPubkey.equals(origCrankAccount.publicKey), + `Incorrect crank, expected ${origCrankAccount.publicKey}, received ${accounts.aggregator.data.crankPubkey}` + ); + assert( + accounts.lease.balance === 0.65, + `Incorrect lease balance, expected 0.65, received ${accounts.lease.balance}` + ); + assert( + accounts.permission.data.permissions === 2, + `Incorrect permissions, expected PermitOracleQueueUsage (2), received ${accounts.permission.data.permissions}` + ); + }); + + it('Transfers the aggregator to a new queue and crank along with its balances', async () => { + if (!aggregatorAccount) { + throw new Error(`No aggregatorAccount to transfer`); + } + + const [permissionAccount, leaseAccount, signatures] = + await aggregatorAccount.transferQueue({ + newQueue: newQueueAccount, + loadAmount: 1, + authority: aggregatorAuthority, + newCrank: newCrankAccount, + enable: true, + queueAuthority: newQueueAuthority, + }); + + const accounts = await aggregatorAccount.fetchAccounts(); + + assert( + accounts.permission.publicKey.equals(permissionAccount.publicKey), + `Incorrect permission account, expected ${permissionAccount.publicKey}, received ${accounts.permission.publicKey}` + ); + + assert( + accounts.aggregator.data.queuePubkey.equals(newQueueAccount.publicKey), + `Incorrect queue, expected ${newQueueAccount.publicKey}, received ${accounts.aggregator.data.queuePubkey}` + ); + assert( + accounts.aggregator.data.crankPubkey.equals(newCrankAccount.publicKey), + `Incorrect crank, expected ${newCrankAccount.publicKey}, received ${accounts.aggregator.data.crankPubkey}` + ); + assert( + accounts.lease.balance === 1.65, + `Incorrect lease balance, expected 1.65, received ${accounts.lease.balance}` + ); + assert( + accounts.permission.data.permissions === 2, + `Incorrect permissions, expected PermitOracleQueueUsage (2), received ${accounts.permission.data.permissions}` + ); + }); + + it('Transfers an aggregator to a new queue in sequence', async () => { + const [aggregatorAccount] = await origQueueAccount.createFeed({ + name: 'Aggregator-2', + authority: aggregatorAuthority, + batchSize: 1, + minRequiredOracleResults: 1, + minRequiredJobResults: 1, + minUpdateDelaySeconds: 10, + crankPubkey: origCrankAccount.publicKey, + fundAmount: 0.25, + enable: true, + queueAuthority: origQueueAuthority, + jobs: [ + { + data: OracleJob.encodeDelimited( + OracleJob.fromObject({ + tasks: [ + { + valueTask: { + value: 1, + }, + }, + ], + }) + ).finish(), + }, + ], + }); + + const [permissionAccount, leaseAccount] = + await aggregatorAccount.transferQueuePart1({ + newQueue: newQueueAccount, + loadAmount: 0.75, + }); + + await aggregatorAccount.transferQueuePart2({ + newQueue: newQueueAccount, + enable: true, + queueAuthority: newQueueAuthority, + }); + + await aggregatorAccount.transferQueuePart3({ + newQueue: newQueueAccount, + authority: aggregatorAuthority, + newCrank: newCrankAccount, + }); + + const accounts = await aggregatorAccount.fetchAccounts(); + + assert( + accounts.aggregator.data.queuePubkey.equals(newQueueAccount.publicKey), + `Incorrect queue, expected ${newQueueAccount.publicKey}, received ${accounts.aggregator.data.queuePubkey}` + ); + assert( + accounts.aggregator.data.crankPubkey.equals(newCrankAccount.publicKey), + `Incorrect crank, expected ${newCrankAccount.publicKey}, received ${accounts.aggregator.data.crankPubkey}` + ); + assert( + accounts.lease.balance === 1, + `Incorrect lease balance, expected 1.0, received ${accounts.lease.balance}` + ); + assert( + accounts.permission.data.permissions === 2, + `Incorrect permissions, expected PermitOracleQueueUsage (2), received ${accounts.permission.data.permissions}` + ); + }); +});