Skip to content

Commit

Permalink
add pyth voter weight plugin (solana-labs#654)
Browse files Browse the repository at this point in the history
* add pyth voter weight

* fix treasury error when on localnet

* remove unused vars and refactor

* enable experiments.layers for wasm

* add devnet staking address to pyth plugin
  • Loading branch information
cctdaniel authored May 5, 2022
1 parent 7a45e68 commit 4d76e75
Show file tree
Hide file tree
Showing 12 changed files with 333 additions and 75 deletions.
16 changes: 9 additions & 7 deletions Strategies/store/marketStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
TokenInfo,
} from '@blockworks-foundation/mango-client'
import { ConnectionContext } from '@utils/connection'
import { Connection } from '@solana/web3.js'

export interface MarketStore extends State {
groupConfig?: GroupConfig
Expand All @@ -30,30 +31,31 @@ export interface MarketStore extends State {
const useMarketStore = create<MarketStore>((set, _get) => ({
loadMarket: async (connection: ConnectionContext, cluster: string) => {
const GROUP = cluster === 'devnet' ? 'devnet.2' : 'mainnet.1'
const mangoConnection =
cluster === 'localnet'
? new Connection(Config.ids().cluster_urls.mainnet)
: connection.current
const groupConfig = Config.ids().getGroupWithName(GROUP)!
const DEFAULT_MARKET = 'SOL'
const DEFAULT_MARKET_INDEX = getMarketIndexBySymbol(
groupConfig,
DEFAULT_MARKET
)
const marketConfig = groupConfig?.perpMarkets[DEFAULT_MARKET_INDEX]
const client = new MangoClient(
connection.current,
groupConfig.mangoProgramId
)
const client = new MangoClient(mangoConnection, groupConfig.mangoProgramId)
const group = await client.getMangoGroup(groupConfig.publicKey)

const [perpMarket] = await Promise.all([
group.loadPerpMarket(
connection.current,
mangoConnection,
marketConfig.marketIndex,
marketConfig.baseDecimals,
marketConfig.quoteDecimals
),
group.loadRootBanks(connection.current),
group.loadRootBanks(mangoConnection),
])

const cache = await group.loadCache(connection.current)
const cache = await group.loadCache(mangoConnection)
const indexPrice = group.getPriceUi(marketConfig.marketIndex, cache)
set((s: MarketStore) => {
s.groupConfig = groupConfig
Expand Down
78 changes: 45 additions & 33 deletions components/TokenBalance/TokenBalanceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ import useVotePluginsClientStore from 'stores/useVotePluginsClientStore'
import Link from 'next/link'
import useNftPluginStore from 'NftVotePlugin/store/nftPluginStore'
import { vsrPluginsPks } from '@hooks/useVotingPlugins'
import {
LOCALNET_REALM_ID as PYTH_LOCALNET_REALM_ID,
PythBalance,
} from 'pyth-staking-api'

const TokenBalanceCard = ({ proposal }: { proposal?: Option<Proposal> }) => {
const { councilMint, mint, realm, symbol } = useRealm()
Expand Down Expand Up @@ -157,6 +161,7 @@ const TokenDeposit = ({
realmTokenAccount,
ownTokenRecord,
ownCouncilTokenRecord,
ownVoterWeight,
councilTokenAccount,
proposals,
governances,
Expand Down Expand Up @@ -366,7 +371,9 @@ const TokenDeposit = ({
: ''

const availableTokens =
depositTokenRecord && mint
realmInfo?.realmId.toBase58() === PYTH_LOCALNET_REALM_ID.toBase58()
? new PythBalance(ownVoterWeight.votingPower!).toString()
: depositTokenRecord && mint
? fmtMintAmount(
mint,
depositTokenRecord.account.governingTokenDepositAmount
Expand All @@ -393,38 +400,43 @@ const TokenDeposit = ({
</div>
</div>

<p
className={`mt-2 opacity-70 mb-4 ml-1 text-xs ${
canShowAvailableTokensMessage ? 'block' : 'hidden'
}`}
>
You have {tokensToShow} tokens available to {canExecuteAction}.
</p>

<div className="flex flex-col sm:flex-row sm:space-x-4 space-y-4 sm:space-y-0 mt-6">
<Button
tooltipMessage={depositTooltipContent}
className="sm:w-1/2"
disabled={!connected || !hasTokensInWallet}
onClick={depositAllTokens}
>
Deposit
</Button>

<Button
tooltipMessage={withdrawTooltipContent}
className="sm:w-1/2"
disabled={
!connected ||
!hasTokensDeposited ||
(!councilVote && toManyCommunityOutstandingProposalsForUser) ||
toManyCouncilOutstandingProposalsForUse
}
onClick={withdrawAllTokens}
>
Withdraw
</Button>
</div>
{realmInfo?.realmId.toBase58() ===
PYTH_LOCALNET_REALM_ID.toBase58() ? null : (
<>
<p
className={`mt-2 opacity-70 mb-4 ml-1 text-xs ${
canShowAvailableTokensMessage ? 'block' : 'hidden'
}`}
>
You have {tokensToShow} tokens available to {canExecuteAction}.
</p>

<div className="flex flex-col sm:flex-row sm:space-x-4 space-y-4 sm:space-y-0 mt-6">
<Button
tooltipMessage={depositTooltipContent}
className="sm:w-1/2"
disabled={!connected || !hasTokensInWallet}
onClick={depositAllTokens}
>
Deposit
</Button>

<Button
tooltipMessage={withdrawTooltipContent}
className="sm:w-1/2"
disabled={
!connected ||
!hasTokensDeposited ||
(!councilVote && toManyCommunityOutstandingProposalsForUser) ||
toManyCouncilOutstandingProposalsForUse
}
onClick={withdrawAllTokens}
>
Withdraw
</Button>
</div>
</>
)}
{config?.account.communityVoterWeightAddin &&
vsrPluginsPks.includes(
config?.account.communityVoterWeightAddin.toBase58()
Expand Down
11 changes: 10 additions & 1 deletion components/VotePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getProgramVersionForRealm } from '@models/registry/api'
import useVotePluginsClientStore from 'stores/useVotePluginsClientStore'
import { useRouter } from 'next/router'
import useNftPluginStore from 'NftVotePlugin/store/nftPluginStore'
import { LOCALNET_REALM_ID as PYTH_LOCALNET_REALM_ID } from 'pyth-staking-api'

const VotePanel = () => {
const [showVoteModal, setShowVoteModal] = useState(false)
Expand Down Expand Up @@ -181,9 +182,17 @@ const VotePanel = () => {
: !ownVoteRecord?.account.isRelinquished

const isPanelVisible = (isVoting || isVoteCast) && isVisibleToWallet

const isRelinquishVotePanelVisible = !(
realmInfo?.realmId.toBase58() === PYTH_LOCALNET_REALM_ID.toBase58() &&
isVoteCast &&
connected &&
!isVoting
)

return (
<>
{isPanelVisible && (
{isPanelVisible && isRelinquishVotePanelVisible && (
<div className="bg-bkg-2 p-4 md:p-6 rounded-lg space-y-4">
<h3 className="mb-4 text-center">{actionLabel}</h3>

Expand Down
36 changes: 34 additions & 2 deletions hooks/useRealm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,28 @@ import { ProgramAccount, TokenOwnerRecord } from '@solana/spl-governance'
import { isPublicKey } from '@tools/core/pubkey'
import { useRouter } from 'next/router'
import useNftPluginStore from 'NftVotePlugin/store/nftPluginStore'
import { useMemo, useState } from 'react'
import { PythBalance } from 'pyth-staking-api'
import { useEffect, useMemo, useState } from 'react'
import useVotePluginsClientStore from 'stores/useVotePluginsClientStore'
import useDepositStore from 'VoteStakeRegistry/stores/useDepositStore'
import {
createUnchartedRealmInfo,
getCertifiedRealmInfo,
RealmInfo,
} from '../models/registry/api'
import {
PythVoterWeight,
VoteNftWeight,
VoteRegistryVoterWeight,
VoterWeight,
} from '../models/voteWeights'

import useWalletStore from '../stores/useWalletStore'
import { nftPluginsPks, vsrPluginsPks } from './useVotingPlugins'
import {
nftPluginsPks,
vsrPluginsPks,
pythPluginsPks,
} from './useVotingPlugins'

export default function useRealm() {
const router = useRouter()
Expand All @@ -39,6 +46,25 @@ export default function useRealm() {
} = useWalletStore((s) => s.selectedRealm)
const votingPower = useDepositStore((s) => s.state.votingPower)
const nftVotingPower = useNftPluginStore((s) => s.state.votingPower)

const pythClient = useVotePluginsClientStore((s) => s.state.pythClient)
const [pythVoterWeight, setPythVoterWeight] = useState<PythBalance>()

useEffect(() => {
const getPythVoterWeight = async () => {
if (connected && wallet?.publicKey && pythClient) {
const sa = await pythClient.stakeConnection.getMainAccount(
wallet.publicKey
)
const vw = sa?.getVoterWeight(
await pythClient.stakeConnection.getTime()
)
setPythVoterWeight(vw)
}
}
getPythVoterWeight()
}, [connected])

const [realmInfo, setRealmInfo] = useState<RealmInfo | undefined>(undefined)
useMemo(async () => {
let realmInfo = isPublicKey(symbol as string)
Expand Down Expand Up @@ -119,11 +145,13 @@ export default function useRealm() {
currentPluginPk && vsrPluginsPks.includes(currentPluginPk?.toBase58())
const isNftMode =
currentPluginPk && nftPluginsPks.includes(currentPluginPk?.toBase58())
const pythVotingPower = pythVoterWeight?.toBN() || new BN(0)
const ownVoterWeight = getVoterWeight(
currentPluginPk,
ownTokenRecord,
votingPower,
nftVotingPower,
pythVotingPower,
ownCouncilTokenRecord
)
return {
Expand Down Expand Up @@ -157,6 +185,7 @@ const getVoterWeight = (
ownTokenRecord: ProgramAccount<TokenOwnerRecord> | undefined,
votingPower: BN,
nftVotingPower: BN,
pythVotingPower: BN,
ownCouncilTokenRecord: ProgramAccount<TokenOwnerRecord> | undefined
) => {
if (currentPluginPk) {
Expand All @@ -170,6 +199,9 @@ const getVoterWeight = (
nftVotingPower
)
}
if (pythPluginsPks.includes(currentPluginPk.toBase58())) {
return new PythVoterWeight(ownTokenRecord, pythVotingPower)
}
}
return new VoterWeight(ownTokenRecord, ownCouncilTokenRecord)
}
39 changes: 34 additions & 5 deletions hooks/useVotingPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import useVotePluginsClientStore from 'stores/useVotePluginsClientStore'
import { getMaxVoterWeightRecord } from '@solana/spl-governance'
import { getNftMaxVoterWeightRecord } from 'NftVotePlugin/sdk/accounts'
import { notify } from '@utils/notifications'
import {
LOCALNET_STAKING_ADDRESS as PYTH_LOCALNET_STAKING_ADDRESS,
DEVNET_STAKING_ADDRESS as PYTH_DEVNET_STAKING_ADDRESS,
} from 'pyth-staking-api'

export const vsrPluginsPks: string[] = [
'4Q6WW2ouZ6V3iaNm56MTd5n2tnTm4C5fiH8miFHnAFHo',
]
Expand All @@ -17,26 +22,31 @@ export const nftPluginsPks: string[] = [
'GnftV5kLjd67tvHpNGyodwWveEKivz3ZWvvE3Z4xi2iw',
]

export const pythPluginsPks: string[] = [
PYTH_LOCALNET_STAKING_ADDRESS.toBase58(),
PYTH_DEVNET_STAKING_ADDRESS.toBase58(),
]

export function useVotingPlugins() {
const { realm, config } = useRealm()
const {
handleSetVsrRegistrar,
handleSetVsrClient,
handleSetNftClient,
handleSetNftRegistrar,
handleSetPythClient,
handleSetCurrentRealmVotingClient,
} = useVotePluginsClientStore()
const {
setVotingNfts,
setMaxVoterWeight,
setIsLoadingNfts,
} = useNftPluginStore()
const { setVotingNfts, setMaxVoterWeight, setIsLoadingNfts } =
useNftPluginStore()

const wallet = useWalletStore((s) => s.current)
const connection = useWalletStore((s) => s.connection)
const connected = useWalletStore((s) => s.connected)
const vsrClient = useVotePluginsClientStore((s) => s.state.vsrClient)
const nftClient = useVotePluginsClientStore((s) => s.state.nftClient)
const pythClient = useVotePluginsClientStore((s) => s.state.pythClient)

const currentClient = useVotePluginsClientStore(
(s) => s.state.currentRealmVotingClient
)
Expand Down Expand Up @@ -113,6 +123,7 @@ export function useVotingPlugins() {
useEffect(() => {
handleSetVsrClient(wallet, connection)
handleSetNftClient(wallet, connection)
handleSetPythClient(wallet, connection)
}, [connection.endpoint])

useEffect(() => {
Expand Down Expand Up @@ -148,18 +159,36 @@ export function useVotingPlugins() {
}
}
}

const handlePythPlugin = () => {
if (
pythClient &&
currentPluginPk &&
pythPluginsPks.includes(currentPluginPk.toBase58())
) {
if (connected) {
handleSetCurrentRealmVotingClient({
client: pythClient,
realm,
walletPk: wallet?.publicKey,
})
}
}
}
if (
!currentClient ||
currentClient.realm?.pubkey.toBase58() !== realm?.pubkey.toBase58() ||
currentClient.walletPk?.toBase58() !== wallet?.publicKey?.toBase58()
) {
handleNftplugin()
handleVsrPlugin()
handlePythPlugin()
}
}, [
currentPluginPk?.toBase58(),
vsrClient?.program.programId.toBase58(),
nftClient?.program.programId.toBase58(),
pythClient?.program.programId.toBase58(),
realm?.pubkey.toBase58(),
connection.endpoint,
connected,
Expand Down
Loading

0 comments on commit 4d76e75

Please sign in to comment.