From 2da784e00f3d07f7750ae39e254422c3fab84371 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 13 Nov 2023 14:18:52 -0800 Subject: [PATCH 1/3] add approval page --- frontend/pages/approve.tsx | 116 ++++++++++++++++++++++++ staking/app/StakeConnection.ts | 20 ++++ staking/programs/staking/src/context.rs | 2 +- staking/target/idl/staking.json | 2 +- staking/target/types/staking.ts | 4 +- staking/tests/split_vesting_account.ts | 18 ++++ 6 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 frontend/pages/approve.tsx diff --git a/frontend/pages/approve.tsx b/frontend/pages/approve.tsx new file mode 100644 index 00000000..c7ccdab1 --- /dev/null +++ b/frontend/pages/approve.tsx @@ -0,0 +1,116 @@ +import type { NextPage } from 'next' +import Layout from '../components/Layout' +import SEO from '../components/SEO' +import { + useAnchorWallet, + useConnection, + useWallet, +} from '@solana/wallet-adapter-react' +import { + PythBalance, + StakeConnection, + STAKING_ADDRESS, +} from '@pythnetwork/staking' +import { useEffect, useState } from 'react' +import { utils, Wallet } from '@project-serum/anchor' +import toast from 'react-hot-toast' +import { capitalizeFirstLetter } from '../utils/capitalizeFirstLetter' +import { PublicKey } from '@solana/web3.js' +import { wasm } from '@pythnetwork/staking/app/StakeConnection' +import { useRouter } from 'next/router' + +const ApproveSplit: NextPage = () => { + const { connection } = useConnection() + const anchorWallet = useAnchorWallet() + const { publicKey, connected } = useWallet() + + const [stakeConnection, setStakeConnection] = useState() + + const [splitAccountOwner, setSplitAccountOwner] = useState() + const [amount, setAmount] = useState() + const [recipient, setRecipient] = useState() + + useEffect(() => { + const initialize = async () => { + try { + const stakeConnection = await StakeConnection.createStakeConnection( + connection, + anchorWallet as Wallet, + STAKING_ADDRESS + ) + setStakeConnection(stakeConnection) + } catch (e) { + toast.error(capitalizeFirstLetter(e.message)) + } + } + if (!connected) { + setStakeConnection(undefined) + } else { + initialize() + } + }, [connected]) + + const router = useRouter() + const { key } = router.query + + useEffect(() => { + const helper = async () => { + if (stakeConnection !== undefined) { + const splitAccountOwner: PublicKey = new PublicKey(key!) + const stakeAccount = (await stakeConnection!.getMainAccount( + splitAccountOwner + ))! + + const splitRequestAccount = PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode('split_request'), + stakeAccount.address.toBuffer(), + ], + stakeConnection!.program.programId + )[0] + const splitRequest = + await stakeConnection!.program.account.splitRequest.fetch( + splitRequestAccount + ) + + setSplitAccountOwner(splitAccountOwner) + setAmount(new PythBalance(splitRequest.amount)) + setRecipient(splitRequest.recipient) + } + } + helper() + }, [stakeConnection]) + + const approveSplit = async () => { + console.log(amount) + console.log(recipient) + const stakeAccount = (await stakeConnection!.getMainAccount( + splitAccountOwner! + ))! + await stakeConnection!.acceptSplit(stakeAccount, amount!, recipient!) + } + + return ( + + +

Approve a split to {key}

+

+ {splitAccountOwner != undefined + ? `account owner: ${splitAccountOwner}` + : 'no owner'} +

+

{amount != undefined ? `amount: ${amount}` : 'no amount'}

+

+ {recipient != undefined ? `recipient: ${recipient}` : 'no recipient'} +

+ +
+ ) +} + +export default ApproveSplit diff --git a/staking/app/StakeConnection.ts b/staking/app/StakeConnection.ts index 4e4e4b18..a510a7bc 100644 --- a/staking/app/StakeConnection.ts +++ b/staking/app/StakeConnection.ts @@ -873,6 +873,26 @@ export class StakeConnection { .rpc(); } + public async getSplitRequest( + stakeAccount: StakeAccount + ): Promise<{ balance: PythBalance; recipient: PublicKey } | undefined> { + const splitRequestAccount = PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode("split_request"), + stakeAccount.address.toBuffer(), + ], + this.program.programId + )[0]; + const splitRequest = await this.program.account.splitRequest.fetch( + splitRequestAccount + ); + + return { + balance: new PythBalance(splitRequest.amount), + recipient: splitRequest.recipient, + }; + } + public async acceptSplit( stakeAccount: StakeAccount, amount: PythBalance, diff --git a/staking/programs/staking/src/context.rs b/staking/programs/staking/src/context.rs index 7d734080..9793f1e5 100644 --- a/staking/programs/staking/src/context.rs +++ b/staking/programs/staking/src/context.rs @@ -320,7 +320,7 @@ pub struct AcceptSplit<'info> { pub source_stake_account_positions: AccountLoader<'info, positions::PositionData>, #[account(mut, seeds = [STAKE_ACCOUNT_METADATA_SEED.as_bytes(), source_stake_account_positions.key().as_ref()], bump = source_stake_account_metadata.metadata_bump)] pub source_stake_account_metadata: Box>, - #[account(seeds = [SPLIT_REQUEST.as_bytes(), source_stake_account_positions.key().as_ref()], bump)] + #[account(mut, seeds = [SPLIT_REQUEST.as_bytes(), source_stake_account_positions.key().as_ref()], bump)] pub source_stake_account_split_request: Box>, #[account( mut, diff --git a/staking/target/idl/staking.json b/staking/target/idl/staking.json index bc74f6c2..c6066a57 100644 --- a/staking/target/idl/staking.json +++ b/staking/target/idl/staking.json @@ -949,7 +949,7 @@ }, { "name": "sourceStakeAccountSplitRequest", - "isMut": false, + "isMut": true, "isSigner": false, "pda": { "seeds": [ diff --git a/staking/target/types/staking.ts b/staking/target/types/staking.ts index 42fe3a48..6549bddd 100644 --- a/staking/target/types/staking.ts +++ b/staking/target/types/staking.ts @@ -949,7 +949,7 @@ export type Staking = { }, { "name": "sourceStakeAccountSplitRequest", - "isMut": false, + "isMut": true, "isSigner": false, "pda": { "seeds": [ @@ -2920,7 +2920,7 @@ export const IDL: Staking = { }, { "name": "sourceStakeAccountSplitRequest", - "isMut": false, + "isMut": true, "isSigner": false, "pda": { "seeds": [ diff --git a/staking/tests/split_vesting_account.ts b/staking/tests/split_vesting_account.ts index b34a886f..0898ce70 100644 --- a/staking/tests/split_vesting_account.ts +++ b/staking/tests/split_vesting_account.ts @@ -179,12 +179,30 @@ describe("split vesting account", async () => { aliceConnection.userPublicKey() ); + let request = await samConnection.getSplitRequest(stakeAccount); + assert.equal( + JSON.stringify(request), + JSON.stringify({ + balance: PythBalance.fromString("33"), + recipient: aliceConnection.userPublicKey(), + }) + ); + await pdaConnection.acceptSplit( stakeAccount, PythBalance.fromString("33"), aliceConnection.userPublicKey() ); + request = await samConnection.getSplitRequest(stakeAccount); + assert.equal( + JSON.stringify(request), + JSON.stringify({ + balance: PythBalance.fromString("0"), + recipient: aliceConnection.userPublicKey(), + }) + ); + await assertMainAccountBalance( samConnection, VestingAccountState.UnvestedTokensFullyUnlocked, From dd574fd72c3f6e41224b19ed32654ed7550f301e Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 13 Nov 2023 15:43:35 -0800 Subject: [PATCH 2/3] cleanup --- frontend/pages/approve.tsx | 39 ++++++++++------------------ staking/app/StakeConnection.ts | 2 +- staking/programs/staking/src/wasm.rs | 1 + 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/frontend/pages/approve.tsx b/frontend/pages/approve.tsx index c7ccdab1..7447c865 100644 --- a/frontend/pages/approve.tsx +++ b/frontend/pages/approve.tsx @@ -8,6 +8,7 @@ import { } from '@solana/wallet-adapter-react' import { PythBalance, + StakeAccount, StakeConnection, STAKING_ADDRESS, } from '@pythnetwork/staking' @@ -16,7 +17,6 @@ import { utils, Wallet } from '@project-serum/anchor' import toast from 'react-hot-toast' import { capitalizeFirstLetter } from '../utils/capitalizeFirstLetter' import { PublicKey } from '@solana/web3.js' -import { wasm } from '@pythnetwork/staking/app/StakeConnection' import { useRouter } from 'next/router' const ApproveSplit: NextPage = () => { @@ -26,7 +26,7 @@ const ApproveSplit: NextPage = () => { const [stakeConnection, setStakeConnection] = useState() - const [splitAccountOwner, setSplitAccountOwner] = useState() + const [stakeAccount, setStakeAccount] = useState() const [amount, setAmount] = useState() const [recipient, setRecipient] = useState() @@ -61,42 +61,29 @@ const ApproveSplit: NextPage = () => { splitAccountOwner ))! - const splitRequestAccount = PublicKey.findProgramAddressSync( - [ - utils.bytes.utf8.encode('split_request'), - stakeAccount.address.toBuffer(), - ], - stakeConnection!.program.programId - )[0] - const splitRequest = - await stakeConnection!.program.account.splitRequest.fetch( - splitRequestAccount - ) + const { balance, recipient } = await stakeConnection.getSplitRequest( + stakeAccount + ) - setSplitAccountOwner(splitAccountOwner) - setAmount(new PythBalance(splitRequest.amount)) - setRecipient(splitRequest.recipient) + setStakeAccount(stakeAccount) + setAmount(balance) + setRecipient(recipient) } } helper() }, [stakeConnection]) const approveSplit = async () => { - console.log(amount) - console.log(recipient) - const stakeAccount = (await stakeConnection!.getMainAccount( - splitAccountOwner! - ))! - await stakeConnection!.acceptSplit(stakeAccount, amount!, recipient!) + await stakeConnection!.acceptSplit(stakeAccount!, amount!, recipient!) } return ( -

Approve a split to {key}

+

Approve a split request from {key}

- {splitAccountOwner != undefined - ? `account owner: ${splitAccountOwner}` + {stakeAccount != undefined + ? `stake account address: ${stakeAccount.address}` : 'no owner'}

{amount != undefined ? `amount: ${amount}` : 'no amount'}

@@ -107,7 +94,7 @@ const ApproveSplit: NextPage = () => { className="rounded-full p-2 hover:bg-hoverGray" onClick={() => approveSplit()} > - this is a button + Click to approve
) diff --git a/staking/app/StakeConnection.ts b/staking/app/StakeConnection.ts index a510a7bc..c378ed21 100644 --- a/staking/app/StakeConnection.ts +++ b/staking/app/StakeConnection.ts @@ -878,7 +878,7 @@ export class StakeConnection { ): Promise<{ balance: PythBalance; recipient: PublicKey } | undefined> { const splitRequestAccount = PublicKey.findProgramAddressSync( [ - utils.bytes.utf8.encode("split_request"), + utils.bytes.utf8.encode(wasm.Constants.SPLIT_REQUEST()), stakeAccount.address.toBuffer(), ], this.program.programId diff --git a/staking/programs/staking/src/wasm.rs b/staking/programs/staking/src/wasm.rs index 8bf4a5ce..98711e39 100644 --- a/staking/programs/staking/src/wasm.rs +++ b/staking/programs/staking/src/wasm.rs @@ -297,6 +297,7 @@ reexport_seed_const!(TARGET_SEED); reexport_seed_const!(MAX_VOTER_RECORD_SEED); reexport_seed_const!(VOTING_TARGET_SEED); reexport_seed_const!(DATA_TARGET_SEED); +reexport_seed_const!(SPLIT_REQUEST); #[wasm_bindgen] impl Constants { From 9e1a9dd5606a70d1d9c38060cccb4feaf2beb3fe Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 13 Nov 2023 15:49:13 -0800 Subject: [PATCH 3/3] fix --- frontend/pages/approve.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/pages/approve.tsx b/frontend/pages/approve.tsx index 7447c865..0c274a17 100644 --- a/frontend/pages/approve.tsx +++ b/frontend/pages/approve.tsx @@ -61,9 +61,9 @@ const ApproveSplit: NextPage = () => { splitAccountOwner ))! - const { balance, recipient } = await stakeConnection.getSplitRequest( + const { balance, recipient } = (await stakeConnection.getSplitRequest( stakeAccount - ) + ))! setStakeAccount(stakeAccount) setAmount(balance)