Skip to content

Commit

Permalink
[frontend] Try to fix API (#339)
Browse files Browse the repository at this point in the history
* Test

* Go

* Try another one

* Try this

* Clean it

* try again

* Test

* EDge test

* Another one

* Another one

* Go

* Update import

* Continue

* Finish refactor

* Refactor complete

* Increase max duration

* CMC works

* CMC works

* Cleanup

* Increase timeout

* Logs

* Revert "Logs"

This reverts commit 26aa610.

* Revert some stuff

* Do it
  • Loading branch information
guibescos authored Jan 26, 2024
1 parent 67f3c71 commit e4e8851
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 273 deletions.
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))
}
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

2 comments on commit e4e8851

@vercel
Copy link

@vercel vercel bot commented on e4e8851 Jan 26, 2024

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

@vercel
Copy link

@vercel vercel bot commented on e4e8851 Jan 26, 2024

Choose a reason for hiding this comment

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

Please sign in to comment.