-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Continue * bump solana * Fix * Next * Please work * Vesting-api * Seems to work * Fix * Please work * Add env variable * Rename * Rename * Comment
- Loading branch information
Showing
4 changed files
with
511 additions
and
252 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
ENDPOINT="https://api.devnet.solana.com" | ||
BACKEND_ENDPOINT="https://api.devnet.solana.com" | ||
CLUSTER="devnet" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import { NextApiRequest, NextApiResponse } from 'next' | ||
import { PythBalance } from '@pythnetwork/staking/app/pythBalance' | ||
import BN from 'bn.js' | ||
import { STAKING_ADDRESS } from '@pythnetwork/staking/app/constants' | ||
import { Connection, Keypair, PublicKey } from '@solana/web3.js' | ||
import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes' | ||
import { Program, AnchorProvider } from '@coral-xyz/anchor' | ||
import NodeWallet from '@coral-xyz/anchor/dist/cjs/nodewallet' | ||
import { Staking } from '@pythnetwork/staking/lib/target/types/staking' | ||
import idl from '@pythnetwork/staking/target/idl/staking.json' | ||
import { splTokenProgram } from '@coral-xyz/spl-token' | ||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token' | ||
|
||
const connection = new Connection(process.env.BACKEND_ENDPOINT!) | ||
const provider = new AnchorProvider( | ||
connection, | ||
new NodeWallet(new Keypair()), | ||
{} | ||
) | ||
const stakingProgram = new Program<Staking>( | ||
idl as Staking, | ||
STAKING_ADDRESS, | ||
provider | ||
) | ||
const tokenProgram = splTokenProgram({ | ||
programId: TOKEN_PROGRAM_ID, | ||
provider: provider as any, | ||
}) | ||
|
||
export default async function handlerLockedAccounts( | ||
req: NextApiRequest, | ||
res: NextApiResponse | ||
) { | ||
const { owner } = req.query | ||
|
||
if (owner == undefined || owner instanceof Array) { | ||
res.status(400).json({ | ||
error: "Must provide the 'owner' query parameters", | ||
}) | ||
} else { | ||
const stakeAccounts = await geStakeAccounts( | ||
connection, | ||
new PublicKey(owner) | ||
) | ||
res.status(200).json( | ||
await Promise.all( | ||
stakeAccounts.map((account) => { | ||
return getStakeAccountDetails(account) | ||
}) | ||
) | ||
) | ||
} | ||
} | ||
|
||
async function getStakeAccountDetails(positionAccountAddress: PublicKey) { | ||
const configAccountAddress = PublicKey.findProgramAddressSync( | ||
[Buffer.from('config')], | ||
STAKING_ADDRESS | ||
)[0] | ||
const configAccountData = await stakingProgram.account.globalConfig.fetch( | ||
configAccountAddress | ||
) | ||
|
||
const metadataAccountAddress = PublicKey.findProgramAddressSync( | ||
[Buffer.from('stake_metadata'), positionAccountAddress.toBuffer()], | ||
STAKING_ADDRESS | ||
)[0] | ||
const metadataAccountData = | ||
await stakingProgram.account.stakeAccountMetadataV2.fetch( | ||
metadataAccountAddress | ||
) | ||
|
||
const lock = metadataAccountData.lock | ||
|
||
const custodyAccountAddress = PublicKey.findProgramAddressSync( | ||
[Buffer.from('custody'), positionAccountAddress.toBuffer()], | ||
STAKING_ADDRESS | ||
)[0] | ||
|
||
const custodyAccountData = await tokenProgram.account.account.fetch( | ||
custodyAccountAddress | ||
) | ||
|
||
return { | ||
custodyAccount: custodyAccountAddress.toBase58(), | ||
actualAmount: new PythBalance(custodyAccountData.amount).toString(), | ||
lock: getLockSummary(lock, configAccountData.pythTokenListTime), | ||
} | ||
} | ||
|
||
async function geStakeAccounts(connection: Connection, owner: PublicKey) { | ||
const response = await connection.getProgramAccounts(STAKING_ADDRESS, { | ||
encoding: 'base64', | ||
filters: [ | ||
{ | ||
memcmp: { | ||
offset: 0, | ||
bytes: bs58.encode(Buffer.from('55c3f14f7cc04f0b', 'hex')), // Positions account discriminator | ||
}, | ||
}, | ||
{ | ||
memcmp: { | ||
offset: 8, | ||
bytes: owner.toBase58(), | ||
}, | ||
}, | ||
], | ||
}) | ||
return response.map((account) => { | ||
return account.pubkey | ||
}) | ||
} | ||
|
||
export function getLockSummary(lock: any, listTime: BN | null) { | ||
if (lock.fullyVested) { | ||
return { type: 'fullyUnlocked' } | ||
} else if (lock.periodicVestingAfterListing) { | ||
return { | ||
type: 'periodicUnlockingAfterListing', | ||
schedule: getUnlockEvents( | ||
listTime, | ||
lock.periodicVestingAfterListing.periodDuration, | ||
lock.periodicVestingAfterListing.numPeriods, | ||
lock.periodicVestingAfterListing.initialBalance | ||
), | ||
} | ||
} else if (lock.periodicVesting) { | ||
return { | ||
type: 'periodicUnlocking', | ||
schedule: getUnlockEvents( | ||
lock.periodicVesting.startDate, | ||
lock.periodicVesting.periodDuration, | ||
lock.periodicVesting.numPeriods, | ||
lock.periodicVesting.initialBalance | ||
), | ||
} | ||
} | ||
} | ||
|
||
export function getUnlockEvents( | ||
startData: BN | null, | ||
periodDuration: BN, | ||
numberOfPeriods: BN, | ||
initialBalance: BN | ||
) { | ||
if (startData) { | ||
return Array(numberOfPeriods.toNumber()) | ||
.fill(0) | ||
.map((_, i) => { | ||
return { | ||
date: startData.add(periodDuration.muln(i + 1)).toString(), | ||
amount: new PythBalance( | ||
initialBalance.divn(numberOfPeriods.toNumber()) | ||
).toString(), | ||
} | ||
}) | ||
} | ||
return [] | ||
} |
Oops, something went wrong.
a4b350c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
staking-devnet – ./
governance-nu.vercel.app
staking-devnet-pyth-web.vercel.app
staking-devnet-git-main-pyth-web.vercel.app
a4b350c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
staking – ./
staking-git-main-pyth-web.vercel.app
staking-pyth-web.vercel.app
staking-rho-nine.vercel.app
staking.pyth.network