-
Notifications
You must be signed in to change notification settings - Fork 556
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/vunax/governance-ui
- Loading branch information
Showing
387 changed files
with
39,865 additions
and
8,839 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
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 |
---|---|---|
@@ -1 +1 @@ | ||
lts/gallium | ||
lts/iron |
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 @@ | ||
Please deploy |
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,259 @@ | ||
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 | ||
|
||
async _fetchRegistrar(realm: PublicKey, mint: PublicKey) { | ||
const { registrar: registrarPk } = this.getRegistrarPDA(realm, mint) | ||
const registrar = await queryClient.fetchQuery( | ||
['Drift', 'Plugin Registrar', registrarPk], | ||
() => this.program.account.registrar.fetch(registrarPk) | ||
) | ||
return registrar | ||
} | ||
|
||
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 = await this._fetchRegistrar(realm, mint) | ||
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._fetchRegistrar(realm, mint) | ||
const spotMarketIndex = registrar.spotMarketIndex // could just hardcode spotmarket pk | ||
const driftProgramId = registrar.driftProgramId // likewise | ||
const drift = new Program(DriftIDL, driftProgramId, this.program.provider) | ||
|
||
//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 | ||
|
||
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() | ||
} | ||
} |
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,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> | ||
</> | ||
) | ||
} |
Oops, something went wrong.