Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[frontend] Try to fix API #339

Merged
merged 24 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@solana/wallet-adapter-wallets": "^0.19.16",
"@solana/web3.js": "^1.87.5",
"@tippyjs/react": "^4.2.6",
"axios": "^1.6.7",
"dotenv": "^16.0.0",
"next": "12.2.5",
"prop-types": "^15.8.1",
Expand Down
32 changes: 32 additions & 0 deletions frontend/pages/api/getAllStakingAccounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes'
import { PublicKey } from '@solana/web3.js'
import axios from 'axios'

// The JSON payload is too big when using the @solana/web3.js getProgramAccounts
// We get around this by using the base64+ztsd encoding instead of base64 that @solana/web3.js uses
export async function getAllStakeAccounts(url: string): Promise<PublicKey[]> {
const response = await axios({
method: 'post',
url: url,
data: {
jsonrpc: '2.0',
id: 1,
method: 'getProgramAccounts',
params: [
'pytS9TjG1qyAZypk7n8rw8gfW9sUaqqYyMhJQ4E7JCQ',
{
encoding: 'base64+zstd',
filters: [
{
memcmp: {
offset: 0,
bytes: bs58.encode(Buffer.from('55c3f14f7cc04f0b', 'hex')), // Positions account discriminator
},
},
],
},
],
},
})
return response.data.result.map((x: any) => new PublicKey(x.pubkey))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to paginate the response ? that could be a good fix.

}
74 changes: 11 additions & 63 deletions frontend/pages/api/v1/all_locked_accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ 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, IdlAccounts } from '@coral-xyz/anchor'
import {
getAllMetadataAccounts,
getCustodyAccountAddress,
hasStandardLockup,
} from '@pythnetwork/staking/app/api_utils'
import { Connection, Keypair } from '@solana/web3.js'
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'
import {
getCustodyAccountAddress,
getMetadataAccountAddress,
} from './locked_accounts'

const ONE_YEAR = new BN(3600 * 24 * 365)
import { getAllStakeAccounts } from '../getAllStakingAccounts'

const connection = new Connection(process.env.BACKEND_ENDPOINT!)
const RPC_URL = process.env.BACKEND_ENDPOINT!
const connection = new Connection(RPC_URL)
const provider = new AnchorProvider(
connection,
new NodeWallet(new Keypair()),
Expand All @@ -37,7 +37,7 @@ export default async function handlerAllLockedAccounts(
req: NextApiRequest,
res: NextApiResponse
) {
const allStakeAccounts = await getAllStakeAccounts(connection)
const allStakeAccounts = await getAllStakeAccounts(RPC_URL)

const allMetadataAccounts = await getAllMetadataAccounts(
stakingProgram,
Expand Down Expand Up @@ -86,55 +86,3 @@ export default async function handlerAllLockedAccounts(
res.setHeader('Cache-Control', 'max-age=0, s-maxage=3600')
res.status(200).json(data)
}

function hasStandardLockup(
metadataAccountData: IdlAccounts<Staking>['stakeAccountMetadataV2']
) {
return (
metadataAccountData.lock.periodicVestingAfterListing &&
metadataAccountData.lock.periodicVestingAfterListing.numPeriods.eq(
new BN(4)
) &&
metadataAccountData.lock.periodicVestingAfterListing.periodDuration.eq(
ONE_YEAR
)
)
}
export async function getAllStakeAccounts(connection: Connection) {
const response = await connection.getProgramAccounts(STAKING_ADDRESS, {
encoding: 'base64',
filters: [
{
memcmp: {
offset: 0,
bytes: bs58.encode(Buffer.from('55c3f14f7cc04f0b', 'hex')), // Positions account discriminator
},
},
],
})
return response.map((account) => {
return account.pubkey
})
}

export async function getAllMetadataAccounts(
stakingProgram: Program<Staking>,
stakeAccounts: PublicKey[]
): Promise<(IdlAccounts<Staking>['stakeAccountMetadataV2'] | null)[]> {
const metadataAccountAddresses = stakeAccounts.map((account) =>
getMetadataAccountAddress(account)
)
return stakingProgram.account.stakeAccountMetadataV2.fetchMultiple(
metadataAccountAddresses
)
}

export async function getAllCustodyAccounts(
tokenProgram: any,
stakeAccounts: PublicKey[]
) {
const allCustodyAccountAddresses = stakeAccounts.map((account) =>
getCustodyAccountAddress(account)
)
return tokenProgram.account.account.fetchMultiple(allCustodyAccountAddresses)
}
80 changes: 12 additions & 68 deletions frontend/pages/api/v1/cmc/supply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@ 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 { Program, AnchorProvider, IdlAccounts } from '@coral-xyz/anchor'
import { Connection, Keypair } from '@solana/web3.js'
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'
import { getConfig } from './../locked_accounts'
import {
getAllCustodyAccounts,
getCurrentlyLockedAmount,
getTotalSupply,
getAllMetadataAccounts,
getAllStakeAccounts,
} from '../all_locked_accounts'

const PYTH_TOKEN = new PublicKey('HZ1JovNiVvGrGNiiYvEozEVgZ58xaU3RKwX8eACQBCt3')
getConfig,
getAllCustodyAccounts,
} from '@pythnetwork/staking/app/api_utils'
import { getAllStakeAccounts } from 'pages/api/getAllStakingAccounts'

const connection = new Connection(process.env.BACKEND_ENDPOINT!)
const RPC_URL = process.env.BACKEND_ENDPOINT!
const connection = new Connection(RPC_URL)
const provider = new AnchorProvider(
connection,
new NodeWallet(new Keypair()),
Expand Down Expand Up @@ -49,12 +50,13 @@ export default async function handlerSupply(
res.status(200).send((await getTotalSupply(tokenProgram)).toString(false))
} else if (q === 'circulatingSupply') {
const configAccountData = await getConfig(stakingProgram)
const allStakeAccounts = await getAllStakeAccounts(connection)
const allStakeAccounts = await getAllStakeAccounts(RPC_URL)

const allMetadataAccounts = await getAllMetadataAccounts(
stakingProgram,
allStakeAccounts
)

const allCustodyAccounts = await getAllCustodyAccounts(
tokenProgram,
allStakeAccounts
Expand Down Expand Up @@ -88,61 +90,3 @@ export default async function handlerSupply(
})
}
}

function getCurrentlyLockedAmount(
metadataAccountData: IdlAccounts<Staking>['stakeAccountMetadataV2'],
configAccountData: IdlAccounts<Staking>['globalConfig']
): PythBalance {
const lock = metadataAccountData.lock
const listTime = configAccountData.pythTokenListTime
if (lock.fullyVested) {
return PythBalance.zero()
} else if (lock.periodicVestingAfterListing) {
if (!listTime) {
return new PythBalance(lock.periodicVestingAfterListing.initialBalance)
} else {
return getCurrentlyLockedAmountPeriodic(
listTime,
lock.periodicVestingAfterListing.periodDuration,
lock.periodicVestingAfterListing.numPeriods,
lock.periodicVestingAfterListing.initialBalance
)
}
} else if (lock.periodicVesting) {
return getCurrentlyLockedAmountPeriodic(
lock.periodicVesting.startDate,
lock.periodicVesting.periodDuration,
lock.periodicVesting.numPeriods,
lock.periodicVesting.initialBalance
)
} else {
throw new Error('Should be unreachable')
}
}

function getCurrentlyLockedAmountPeriodic(
startDate: BN,
periodDuration: BN,
numPeriods: BN,
initialBalance: BN
): PythBalance {
const currentTimestamp = new BN(Math.floor(Date.now() / 1000))
if (currentTimestamp.lte(startDate)) {
return new PythBalance(initialBalance)
} else {
const periodsElapsed = currentTimestamp.sub(startDate).div(periodDuration)
if (periodsElapsed.gte(numPeriods)) {
return PythBalance.zero()
} else {
const remainingPeriods = numPeriods.sub(periodsElapsed)
return new PythBalance(
remainingPeriods.mul(initialBalance).div(numPeriods)
)
}
}
}

async function getTotalSupply(tokenProgram: any): Promise<PythBalance> {
const pythTokenMintData = await tokenProgram.account.mint.fetch(PYTH_TOKEN)
return new PythBalance(pythTokenMintData.supply)
}
Loading
Loading