Skip to content

Commit

Permalink
Merge branch 'solana-labs:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
FoxKnight-io authored May 9, 2024
2 parents 2758303 + b8503ec commit fac0b12
Show file tree
Hide file tree
Showing 325 changed files with 34,080 additions and 10,722 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ DEVNET_RPC=https://mango.devnet.rpcpool.com

DEFAULT_GOVERNANCE_PROGRAM_ID=GTesTBiEWE32WHXXE2S4XbZvA5CrEc4xs6ZgRe895dP

NEXT_PUBLIC_JUPTER_SWAP_API_ENDPOINT=https://public.jupiterapi.com
NEXT_PUBLIC_API_ENDPOINT=https://api.realms.today/graphql
NEXT_PUBLIC_DISCORD_APPLICATION_CLIENT_ID=1042836142560645130
NEXT_PUBLIC_DISCORD_MATCHDAY_CLIENT_ID=1044361939322683442
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-main-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
cache: yarn

- name: Cache dependencies
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
lts/gallium
lts/iron
2 changes: 2 additions & 0 deletions @types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export interface EndpointInfo {
name: EndpointTypes
url: string
}

export type GovernanceRole = 'council' | 'community';
250 changes: 250 additions & 0 deletions DriftStakeVoterPlugin/DriftVoterClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
import { BN, Program, Provider } from '@coral-xyz/anchor'
import { Client } from '@solana/governance-program-library'
import {
getTokenOwnerRecordAddress,
SYSTEM_PROGRAM_ID,
} from '@solana/spl-governance'
import { PublicKey, TransactionInstruction } from '@solana/web3.js'
import { DriftStakeVoter, IDL } from './idl/driftStakeVoter'
import { IDL as DriftIDL } from './idl/drift'
import {
getInsuranceFundStakeAccountPublicKey,
getInsuranceFundVaultPublicKey,
getSpotMarketPublicKey,
unstakeSharesToAmountWithOpenRequest,
} from './driftSdk'
import { fetchTokenAccountByPubkey } from '@hooks/queries/tokenAccount'
import { DRIFT_STAKE_VOTER_PLUGIN } from './constants'
import { fetchRealmByPubkey } from '@hooks/queries/realm'
import queryClient from '@hooks/queries/queryClient'

export class DriftVoterClient extends Client<DriftStakeVoter> {
readonly requiresInputVoterWeight = true

constructor(
public program: Program<DriftStakeVoter>,
public devnet: boolean
) {
super(program, devnet)
}

async calculateMaxVoterWeight(
_realm: PublicKey,
_mint: PublicKey
): Promise<BN | null> {
console.log(
'drift voter client was just asked to calculate max voter weight'
)
const { result: realm } = await fetchRealmByPubkey(
this.program.provider.connection,
_realm
)
console.log('drift voter client realm', realm)
return realm?.account.config?.communityMintMaxVoteWeightSource.value ?? null // TODO this code should not actually be called because this is not a max voter weight plugin
}

async calculateVoterWeight(
voter: PublicKey,
realm: PublicKey,
mint: PublicKey,
inputVoterWeight: BN
): Promise<BN | null> {
const { registrar: registrarPk } = this.getRegistrarPDA(realm, mint)
const registrar = await this.program.account.registrar.fetch(registrarPk)
const spotMarketIndex = registrar.spotMarketIndex // could just hardcode spotmarket pk
const driftProgramId = registrar.driftProgramId // likewise
const drift = new Program(DriftIDL, driftProgramId, this.program.provider)
const spotMarketPk = await getSpotMarketPublicKey(
driftProgramId,
spotMarketIndex
)
const insuranceFundVaultPk = await getInsuranceFundVaultPublicKey(
driftProgramId,
spotMarketIndex
)
const insuranceFundStakePk = await getInsuranceFundStakeAccountPublicKey(
driftProgramId,
voter,
spotMarketIndex
)

const insuranceFundStake = await queryClient.fetchQuery({
queryKey: ['Insurance Fund Stake', insuranceFundStakePk.toString()],
queryFn: async () =>
drift.account.insuranceFundStake.fetchNullable(insuranceFundStakePk),
})

if (insuranceFundStake === null) {
console.log('drift voter client', 'no insurance fund stake account found')
return inputVoterWeight
}

const spotMarket = await queryClient.fetchQuery({
queryKey: ['Drift Spot Market', spotMarketPk.toString()],
queryFn: async () => drift.account.spotMarket.fetchNullable(spotMarketPk),
})

if (spotMarket === null) {
console.log('Drift spot market not found: ' + spotMarketPk.toString())
return inputVoterWeight
}

const insuranceFundVault = await fetchTokenAccountByPubkey(
this.program.provider.connection,
insuranceFundVaultPk
)
if (insuranceFundVault.result === undefined) {
console.log(
'Insurance fund vault not found: ' + insuranceFundVaultPk.toString()
)
return inputVoterWeight
}

const nShares = insuranceFundStake.ifShares
const withdrawRequestShares = insuranceFundStake.lastWithdrawRequestShares
const withdrawRequestAmount = insuranceFundStake.lastWithdrawRequestValue
const totalIfShares = spotMarket.insuranceFund.totalShares
const insuranceFundVaultBalance = insuranceFundVault.result?.amount

const amount = unstakeSharesToAmountWithOpenRequest(
nShares,
withdrawRequestShares,
withdrawRequestAmount,
totalIfShares,
insuranceFundVaultBalance
)

return amount.add(inputVoterWeight)
}

async updateVoterWeightRecord(
voter: PublicKey,
realm: PublicKey,
mint: PublicKey
//action?: VoterWeightAction | undefined,
//inputRecordCallback?: (() => Promise<PublicKey>) | undefined
): Promise<{
pre: TransactionInstruction[]
post?: TransactionInstruction[] | undefined
}> {
const connection = this.program.provider.connection
const { result: realmAccount } = await fetchRealmByPubkey(connection, realm)
if (!realmAccount) throw new Error('Realm not found')
const tokenOwnerRecordPk = await getTokenOwnerRecordAddress(
realmAccount?.owner,
realm,
mint,
voter
)
const { voterWeightPk } = await this.getVoterWeightRecordPDA(
realm,
mint,
voter
)
const { registrar: registrarPk } = this.getRegistrarPDA(realm, mint)
const registrar = await this.program.account.registrar.fetch(registrarPk)
const spotMarketIndex = registrar.spotMarketIndex // could just hardcode spotmarket pk
const driftProgramId = registrar.driftProgramId // likewise
//const drift = new Program(DriftIDL, driftProgramId, this.program.provider)
const spotMarketPk = await getSpotMarketPublicKey(
driftProgramId,
spotMarketIndex
)
const insuranceFundVaultPk = await getInsuranceFundVaultPublicKey(
driftProgramId,
spotMarketIndex
)
const insuranceFundStakePk = await getInsuranceFundStakeAccountPublicKey(
driftProgramId,
voter,
spotMarketIndex
)

const spotMarket = await queryClient.fetchQuery({
queryKey: ['Drift Spot Market', spotMarketPk.toString()],
queryFn: async () => drift.account.spotMarket.fetchNullable(spotMarketPk),
})
const spotMarketPkOrNull = spotMarket === null ? null : spotMarketPk

const insuranceFundVault = await fetchTokenAccountByPubkey(
this.program.provider.connection,
insuranceFundVaultPk
)
const insuranceFundVaultPkOrNull =
insuranceFundVault.found === false ? null : insuranceFundVaultPk

const drift = new Program(DriftIDL, driftProgramId, this.program.provider)
let insuranceFundStake:
| Awaited<ReturnType<typeof drift.account.insuranceFundStake.fetch>>
| undefined
try {
insuranceFundStake = await drift.account.insuranceFundStake.fetch(
insuranceFundStakePk
)
} catch (e) {
console.log('drift voter client', 'no insurance fund stake account found')
insuranceFundStake = undefined
}
const stakePkOrNull =
insuranceFundStake === undefined ? null : insuranceFundStakePk

const ix = await this.program.methods
.updateVoterWeightRecord()
.accountsStrict({
voterWeightRecord: voterWeightPk,
registrar: registrarPk,
driftProgram: driftProgramId,
spotMarket: spotMarketPkOrNull,
insuranceFundStake: stakePkOrNull,
insuranceFundVault: insuranceFundVaultPkOrNull,
tokenOwnerRecord: tokenOwnerRecordPk,
})
.instruction()

return { pre: [ix] }
}

// NO-OP
async createMaxVoterWeightRecord(): Promise<TransactionInstruction | null> {
return null
}

// NO-OP
async updateMaxVoterWeightRecord(): Promise<TransactionInstruction | null> {
return null
}

static async connect(
provider: Provider,
programId = new PublicKey(DRIFT_STAKE_VOTER_PLUGIN),
devnet = false
): Promise<DriftVoterClient> {
return new DriftVoterClient(
new Program<DriftStakeVoter>(IDL, programId, provider),
devnet
)
}

async createVoterWeightRecord(
voter: PublicKey,
realm: PublicKey,
mint: PublicKey
): Promise<TransactionInstruction | null> {
const { voterWeightPk } = await this.getVoterWeightRecordPDA(
realm,
mint,
voter
)
const { registrar } = this.getRegistrarPDA(realm, mint)

return this.program.methods
.createVoterWeightRecord(voter)
.accounts({
voterWeightRecord: voterWeightPk,
registrar,
payer: voter,
systemProgram: SYSTEM_PROGRAM_ID,
})
.instruction()
}
}
57 changes: 57 additions & 0 deletions DriftStakeVoterPlugin/components/DriftDeposit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { BigNumber } from 'bignumber.js'
import { useRealmQuery } from '@hooks/queries/realm'
import { useMintInfoByPubkeyQuery } from '@hooks/queries/mintInfo'
import useUserGovTokenAccountQuery from '@hooks/useUserGovTokenAccount'
import { DepositTokensButton } from '@components/DepositTokensButton'
import Button from '@components/Button'
import { DRIFT_GOVERNANCE_TICKER } from 'DriftStakeVoterPlugin/constants'

/** Contextual deposit, shows only if relevant */
export const DriftDeposit = ({ role }: { role: 'community' | 'council' }) => {
const realm = useRealmQuery().data?.result
const mint =
role === 'community'
? realm?.account.communityMint
: realm?.account.config.councilMint

const mintInfo = useMintInfoByPubkeyQuery(mint).data?.result
const userAta = useUserGovTokenAccountQuery(role).data?.result

const depositAmount = userAta?.amount
? new BigNumber(userAta.amount.toString())
: new BigNumber(0)

return !depositAmount.isGreaterThan(0) ? null : (
<>
<div className="mt-3 text-xs text-white/50">
You have{' '}
{mintInfo
? depositAmount.shiftedBy(-mintInfo.decimals).toFormat()
: depositAmount.toFormat()}{' '}
more {DRIFT_GOVERNANCE_TICKER} in your wallet. You can stake it with
Drift or deposit it into Realms to increase your voting power.
</div>
<div className="mt-3 flex flex-row justify-start flex-wrap gap-2 max-w-full">
<Button
onClick={
// navigate to https://app.drift.trade/earn/stake in new tab
() => window.open('https://app.drift.trade/earn/stake', '_blank')
}
style={{
flex: '1 1 60px',
color: 'rgba(3, 10, 19, 1)',
backgroundImage:
'linear-gradient(114.67deg, hsla(0, 0%, 100%, .2) 16.59%, transparent 56.74%), linear-gradient(137.87deg, #f6f063 0, hsla(2, 64%, 67%, 0) 30%), linear-gradient(83.36deg, #ff3873 3.72%, #9162f6 46.75%, #3fe5ff 94.51%)',
}}
>
<span className="whitespace-nowrap">Stake with Drift</span>
</Button>
<DepositTokensButton
role={role}
as="secondary"
style={{ flex: '1 1 40px' }}
/>
</div>
</>
)
}
Loading

0 comments on commit fac0b12

Please sign in to comment.