Fix/duplicated account keys (#1221)

* typing for governance select

* Fix select duplicated key

* change axios.request by axios.post

* nit, remove unused import

* nit

* Fix typing issues
This commit is contained in:
Grégory NEUT 2022-11-17 02:35:15 +04:00 committed by GitHub
parent 615de7b806
commit e4f224e6aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 596 additions and 433 deletions

View File

@ -1,5 +1,12 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"cSpell.words": ["Addin", "blockworks", "lamports", "solana", "VERCEL"],
"cSpell.words": [
"Addin",
"blockworks",
"lamports",
"solana",
"VERCEL",
"WSOL"
],
"editor.formatOnSave": true
}

View File

@ -47,7 +47,7 @@ const Clawback = ({
governedTokenAccountsWithoutNfts,
governancesArray,
} = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [voters, setVoters] = useState<Voter[]>([])
const [deposits, setDeposits] = useState<DepositWithMintAccount[]>([])
const [form, setForm] = useState<ClawbackForm>({

View File

@ -51,7 +51,7 @@ const Grant = ({
const wallet = useWalletStore((s) => s.current)
const { realm, tokenRecords, realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [startDate, setStartDate] = useState(dayjs().format('DD-MM-YYYY'))
const [endDate, setEndDate] = useState('')
const [useableGrantMints, setUseableGrantMints] = useState<string[]>([])

View File

@ -19,17 +19,17 @@ const GovernedAccountSelect = ({
governance,
label,
noMaxWidth,
autoselectFirst = true,
autoSelectFirst = true,
}: {
onChange
value
error?
governedAccounts: AssetAccount[]
shouldBeGoverned?
shouldBeGoverned?: boolean
governance?: ProgramAccount<Governance> | null | undefined
label?
noMaxWidth?: boolean
autoselectFirst?: boolean
autoSelectFirst?: boolean
}) => {
function getLabel(value: AssetAccount) {
if (value) {
@ -114,7 +114,7 @@ const GovernedAccountSelect = ({
)
}
useEffect(() => {
if (governedAccounts.length == 1 && autoselectFirst) {
if (governedAccounts.length == 1 && autoSelectFirst) {
//wait for microtask queue to be empty
setTimeout(() => {
onChange(governedAccounts[0])

View File

@ -11,6 +11,7 @@ export default function useGovernanceAssets() {
const governedTokenAccounts: AssetAccount[] = useGovernanceAssetsStore(
(s) => s.governedTokenAccounts
)
const assetAccounts = useGovernanceAssetsStore((s) =>
s.assetAccounts.filter((x) => x.type !== AccountType.AuxiliaryToken)
)

View File

@ -1,4 +1,3 @@
import { MintInfo } from '@blockworks-foundation/mango-v4'
import { getCertifiedRealmInfos, RealmInfo } from '@models/registry/api'
import { AssetType, TokenOwnerRecordAsset } from '@models/treasury/Asset'
import {
@ -11,7 +10,11 @@ import {
} from '@solana/spl-governance'
import { AccountInfo, PublicKey } from '@solana/web3.js'
import { ConnectionContext } from '@utils/connection'
import { parseMintAccountData, TokenProgramAccount } from '@utils/tokens'
import {
MintAccount,
parseMintAccountData,
TokenProgramAccount,
} from '@utils/tokens'
import axios from 'axios'
const getAccountInfoFromRaw = (raw: any) => {
@ -112,7 +115,7 @@ const getTokenOwnerRecordsForWallet = async (
tokenOwnerRecordAccountInfo
) as ProgramAccount<TokenOwnerRecord>
const mintAccount: TokenProgramAccount<MintInfo> = {
const mintAccount: TokenProgramAccount<MintAccount> = {
publicKey: cacheMap[id].realmInfo.communityMint!, // already checking above if present
account: parseMintAccountData(communityMintAccountInfo.data),
}

View File

@ -10,7 +10,7 @@ const GovernanceAccountSelect = ({
governanceAccounts = [],
label,
noMaxWidth,
autoselectFirst = true,
autoSelectFirst = true,
}: {
onChange
value
@ -18,10 +18,10 @@ const GovernanceAccountSelect = ({
governanceAccounts: ProgramAccount<Governance>[]
label?
noMaxWidth?: boolean
autoselectFirst?: boolean
autoSelectFirst?: boolean
}) => {
useEffect(() => {
if (governanceAccounts.length == 1 && autoselectFirst) {
if (governanceAccounts.length == 1 && autoSelectFirst) {
//wait for microtask queue to be empty
setTimeout(() => {
onChange(governanceAccounts[0])

View File

@ -19,43 +19,45 @@ const GovernedAccountSelect = ({
governance,
label,
noMaxWidth,
autoselectFirst = true,
autoSelectFirst = true,
}: {
onChange
value
error?
onChange: (value: unknown) => void
value?: AssetAccount | null
error?: string
governedAccounts: AssetAccount[]
shouldBeGoverned?
governance?: ProgramAccount<Governance> | null | undefined
label?
shouldBeGoverned?: boolean | null
governance?: ProgramAccount<Governance> | null
label?: string
noMaxWidth?: boolean
autoselectFirst?: boolean
autoSelectFirst?: boolean
}) => {
function getLabel(value: AssetAccount) {
if (value) {
const accountType = value.governance.account.accountType
if (value.isSol || value.isToken) {
return getTokenAccountLabelComponent(
value.isSol
? getSolAccountLabel(value)
: getTokenAccountLabelInfo(value)
)
} else {
switch (accountType) {
case GovernanceAccountType.MintGovernanceV1:
case GovernanceAccountType.MintGovernanceV2:
return getMintAccountLabelComponent(getMintAccountLabelInfo(value))
case GovernanceAccountType.ProgramGovernanceV1:
case GovernanceAccountType.ProgramGovernanceV2:
return getProgramAccountLabel(value.governance)
default:
return value.governance.account.governedAccount.toBase58()
}
}
} else {
function getLabel(value?: AssetAccount | null) {
if (!value) {
return null
}
const accountType = value.governance.account.accountType
if (value.isSol || value.isToken) {
return getTokenAccountLabelComponent(
value.isSol
? getSolAccountLabel(value)
: getTokenAccountLabelInfo(value)
)
}
switch (accountType) {
case GovernanceAccountType.MintGovernanceV1:
case GovernanceAccountType.MintGovernanceV2:
return getMintAccountLabelComponent(getMintAccountLabelInfo(value))
case GovernanceAccountType.ProgramGovernanceV1:
case GovernanceAccountType.ProgramGovernanceV2:
return getProgramAccountLabel(value.governance)
default:
return value.governance.account.governedAccount.toBase58()
}
}
//TODO refactor both methods (getMintAccountLabelComponent, getTokenAccountLabelComponent) make it more common
function getMintAccountLabelComponent({
account,
@ -114,7 +116,7 @@ const GovernedAccountSelect = ({
)
}
useEffect(() => {
if (governedAccounts.length == 1 && autoselectFirst) {
if (governedAccounts.length == 1 && autoSelectFirst) {
//wait for microtask queue to be empty
setTimeout(() => {
onChange(governedAccounts[0])

View File

@ -34,7 +34,7 @@ const StakeAccountSelect = ({
stakeAccounts = [],
label,
noMaxWidth,
autoselectFirst = true,
autoSelectFirst = true,
}: {
onChange
value
@ -44,7 +44,7 @@ const StakeAccountSelect = ({
governance?: ProgramAccount<Governance> | null | undefined
label?
noMaxWidth?: boolean
autoselectFirst?: boolean
autoSelectFirst?: boolean
}) => {
function getLabel(value: StakeAccount) {
if (value) {
@ -81,7 +81,7 @@ const StakeAccountSelect = ({
)
}
useEffect(() => {
if (stakeAccounts.length == 1 && autoselectFirst) {
if (stakeAccounts.length == 1 && autoSelectFirst) {
//wait for microtask queue to be empty
setTimeout(() => {
onChange(stakeAccounts[0])

View File

@ -37,7 +37,7 @@ const CastleDeposit = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
// Store CastleDepositForm state

View File

@ -37,7 +37,7 @@ const CastleWithdraw = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
// Store CastleWithdrawForm state

View File

@ -38,7 +38,7 @@ const ChangeDonation = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<SplTokenTransferForm>({
destinationAccount: '',

View File

@ -43,7 +43,7 @@ const CloseTokenAccount = ({
const { realm } = useRealm()
const wallet = useWalletStore((s) => s.current)
const connection = useWalletStore((s) => s.connection)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const [form, setForm] = useState<CloseTokenAccountForm>()
const [formErrors, setFormErrors] = useState({})

View File

@ -36,7 +36,7 @@ const CreateAssociatedTokenAccount = ({
const { assetAccounts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<CreateAssociatedTokenAccountForm>({})
const [formErrors, setFormErrors] = useState({})

View File

@ -29,7 +29,7 @@ const CreateTokenMetadata = ({
const mintGovernancesWithMintInfo = assetAccounts.filter(
(x) => x.type === AccountType.MINT
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<CreateTokenMetadataForm>({
name: '',

View File

@ -30,7 +30,7 @@ const CustomBase64 = ({
const { ownVoterWeight } = useRealm()
const wallet = useWalletStore((s) => s.current)
const { assetAccounts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<Base64InstructionForm>({
governedAccount: undefined,
base64: '',

View File

@ -22,7 +22,7 @@ const Empty = ({
})
const { ownVoterWeight } = useRealm()
const { assetAccounts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [formErrors, setFormErrors] = useState({})
const { handleSetInstructions } = useContext(NewProposalContext)
const handleSetForm = ({ propertyName, value }) => {

View File

@ -59,7 +59,7 @@ const DepositForm = ({
// eslint-disable-next-line react-hooks/exhaustive-deps -- TODO please fix, it can cause difficult bugs. You might wanna check out https://bobbyhadz.com/blog/react-hooks-exhaustive-deps for info. -@asktree
}, [])
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [
form,

View File

@ -59,7 +59,7 @@ const WithdrawForm = ({
// eslint-disable-next-line react-hooks/exhaustive-deps -- TODO please fix, it can cause difficult bugs. You might wanna check out https://bobbyhadz.com/blog/react-hooks-exhaustive-deps for info. -@asktree
}, [])
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [
form,

View File

@ -29,7 +29,7 @@ export interface InstructionInput {
max?: number
step?: number
onBlur?: () => void
shouldBeGoverned?: false | ProgramAccount<Governance> | null
shouldBeGoverned?: boolean
governance?: ProgramAccount<Governance> | null
options?: any[]
hide?: boolean | (() => boolean)
@ -111,7 +111,7 @@ const InstructionInput = ({
case InstructionInputType.GOVERNED_ACCOUNT:
return (
<GovernedAccountSelect
autoselectFirst={false}
autoSelectFirst={false}
label={input.label}
governedAccounts={input.options!}
onChange={(value) => {

View File

@ -27,7 +27,7 @@ const FriktionClaimPendingDeposit = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<FriktionClaimPendingDepositForm>({
governedTokenAccount: undefined,

View File

@ -27,7 +27,7 @@ const FriktionClaimPendingWithdraw = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<FriktionClaimPendingWithdrawForm>({
governedTokenAccount: undefined,

View File

@ -30,7 +30,7 @@ const FriktionDeposit = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<FriktionDepositForm>({
amount: undefined,

View File

@ -30,7 +30,7 @@ const FriktionWithdraw = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<FriktionWithdrawForm>({
amount: undefined,

View File

@ -41,7 +41,7 @@ const ConfigureGatewayPlugin = ({
const gatewayClient = useVotePluginsClientStore((s) => s.state.gatewayClient)
const { assetAccounts } = useGovernanceAssets()
const wallet = useWalletStore((s) => s.current)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<ConfigureGatewayForm>()
const [formErrors, setFormErrors] = useState({})
const { handleSetInstructions } = useContext(NewProposalContext)

View File

@ -42,7 +42,7 @@ const CreateGatewayPluginRegistrar = ({
const gatewayClient = useVotePluginsClientStore((s) => s.state.gatewayClient)
const { assetAccounts } = useGovernanceAssets()
const wallet = useWalletStore((s) => s.current)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<CreateGatewayRegistrarForm>()
const [formErrors, setFormErrors] = useState({})
const { handleSetInstructions } = useContext(NewProposalContext)

View File

@ -30,7 +30,7 @@ const GoblinGoldDeposit = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<GoblinGoldDepositForm>({

View File

@ -34,7 +34,7 @@ const GoblinGoldWithdraw = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<GoblinGoldWithdrawForm>({

View File

@ -79,7 +79,7 @@ const MangoClaimTokens = ({
const [table, setTable] = useState<TableInfo[]>([])
const connection = useWalletStore((s) => s.connection)
const groupName = connection.cluster === 'devnet' ? 'devnet.2' : 'mainnet.1'
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoClaimTokens>({
governedAccount: null,

View File

@ -32,7 +32,7 @@ const MakeAddOracle = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeAddOracleForm>({
governedAccount: undefined,

View File

@ -49,7 +49,7 @@ const MakeAddSpotMarket = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeAddSpotMarketForm>({
governedAccount: undefined,

View File

@ -35,7 +35,7 @@ const MakeChangeMaxAccounts = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeChangeMaxAccountsForm>({
governedAccount: undefined,

View File

@ -38,7 +38,7 @@ const MakeChangePerpMarket = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeChangePerpMarketForm>({
governedAccount: undefined,

View File

@ -41,7 +41,7 @@ const MakeChangeQuoteParams = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeChangeSpotMarketForm>({
governedAccount: undefined,

View File

@ -37,7 +37,7 @@ const MakeChangeReferralFeeParams = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeChangeReferralFeeParams>({
governedAccount: undefined,

View File

@ -37,7 +37,7 @@ const MakeChangeReferralFeeParams2 = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeChangeReferralFeeParams2>({
governedAccount: undefined,

View File

@ -43,7 +43,7 @@ const MakeChangeSpotMarket = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeChangeSpotMarketForm>({
governedAccount: undefined,

View File

@ -45,7 +45,7 @@ const MakeCreatePerpMarket = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeCreatePerpMarketForm>({
governedAccount: undefined,

View File

@ -38,7 +38,7 @@ const MakeDepositToMangoAccount = ({
const { governedTokenAccounts } = useGovernanceAssets()
const tokenAccounts = governedTokenAccounts.filter((x) => x.isToken)
const connection = useWalletStore((s) => s.connection)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoDepositToMangoAccountForm>({
governedAccount: null,

View File

@ -40,7 +40,7 @@ const MakeDepositToMangoAccountCsv = ({
const { governedTokenAccounts } = useGovernanceAssets()
const tokenAccounts = governedTokenAccounts.filter((x) => x.isToken)
const connection = useWalletStore((s) => s.connection)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
// It state will contain the error when
// correct file extension is not used

View File

@ -39,7 +39,7 @@ const MakeRemoveOracle = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoRemoveOracleForm>({
governedAccount: null,

View File

@ -42,7 +42,7 @@ const MakeRemovePerpMarket = ({
(x) => x.type === AccountType.PROGRAM
)
const connection = useWalletStore((s) => s.connection)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoRemovePerpMarketForm>({
governedAccount: null,

View File

@ -42,7 +42,7 @@ const MakeRemoveSpotMarket = ({
(x) => x.type === AccountType.PROGRAM
)
const connection = useWalletStore((s) => s.connection)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoRemoveSpotMarketForm>({
governedAccount: null,

View File

@ -41,7 +41,7 @@ const MakeSetMarketMode = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoMakeSetMarketModeForm>({
governedAccount: null,

View File

@ -41,7 +41,7 @@ const MakeSwapSpotMarket = ({
(x) => x.type === AccountType.PROGRAM
)
const connection = useWalletStore((s) => s.connection.current)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MangoSwapSpotMarketForm>({
governedAccount: null,

View File

@ -56,7 +56,7 @@ const EditToken = ({
(x) => x.type === AccountType.PROGRAM
)
const { connection } = useWalletStore()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<EditTokenForm>({
governedAccount: null,

View File

@ -54,7 +54,7 @@ const PerpCreate = ({
(x) => x.type === AccountType.PROGRAM
)
const { connection } = useWalletStore()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<PerpCreateForm>({
governedAccount: null,

View File

@ -52,7 +52,7 @@ const PerpEdit = ({
(x) => x.type === AccountType.PROGRAM
)
const { connection } = useWalletStore()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<PerpEditForm>({
governedAccount: null,

View File

@ -43,7 +43,7 @@ const EditToken = ({
(x) => x.type === AccountType.PROGRAM
)
const { connection } = useWalletStore()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<Serum3RegisterMarketForm>({
governedAccount: null,

View File

@ -56,7 +56,7 @@ const TokenRegister = ({
(x) => x.type === AccountType.PROGRAM
)
const { connection } = useWalletStore()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<RegisterTokenForm>({
governedAccount: null,

View File

@ -40,7 +40,7 @@ const TokenRegisterTrustless = ({
(x) => x.type === AccountType.SOL
)
const { connection } = useWalletStore()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<TokenRegisterTrustlessForm>({
governedAccount: null,

View File

@ -83,7 +83,7 @@ const MeanCreateAccountComponent = ({ index, governance }: Props) => {
// governedTokenAccount
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
// instruction

View File

@ -40,7 +40,7 @@ const MeanFundAccountComponent = ({ index, governance }: Props) => {
// governedTokenAccount
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
// instruction

View File

@ -35,7 +35,7 @@ const Mint = ({
const mintGovernancesWithMintInfo = assetAccounts.filter(
(x) => x.type === AccountType.MINT
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<MintForm>({
destinationAccount: '',

View File

@ -45,7 +45,7 @@ const ConfigureNftPluginCollection = ({
const nftClient = useVotePluginsClientStore((s) => s.state.nftClient)
const { assetAccounts } = useGovernanceAssets()
const wallet = useWalletStore((s) => s.current)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<ConfigureCollectionForm>()
const [formErrors, setFormErrors] = useState({})
const { handleSetInstructions } = useContext(NewProposalContext)

View File

@ -37,7 +37,7 @@ const CreateNftPluginMaxVoterWeightRecord = ({
const nftClient = useVotePluginsClientStore((s) => s.state.nftClient)
const { assetAccounts } = useGovernanceAssets()
const wallet = useWalletStore((s) => s.current)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<CreateNftMaxVoterWeightRecord>()
const [formErrors, setFormErrors] = useState({})
const { handleSetInstructions } = useContext(NewProposalContext)

View File

@ -34,7 +34,7 @@ const CreateNftPluginRegistrar = ({
const nftClient = useVotePluginsClientStore((s) => s.state.nftClient)
const { assetAccounts } = useGovernanceAssets()
const wallet = useWalletStore((s) => s.current)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<CreateNftRegistrarForm>()
const [formErrors, setFormErrors] = useState({})
const { handleSetInstructions } = useContext(NewProposalContext)

View File

@ -42,7 +42,7 @@ const RealmConfig = ({
}) => {
const { realm, mint, realmInfo } = useRealm()
const wallet = useWalletStore((s) => s.current)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const { assetAccounts } = useGovernanceAssets()
const realmAuthority = assetAccounts.find(
(x) =>

View File

@ -69,7 +69,7 @@ const SagaPreOrder = ({
token.extensions.token?.account.owner.toBase58()
)
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<SagaPhoneForm>({
governedAccount: null,

View File

@ -38,7 +38,7 @@ const CreateObligationAccount = ({
}
// eslint-disable-next-line react-hooks/rules-of-hooks -- TODO this is potentially quite serious! please fix next time the file is edited, -@asktree
const { assetAccounts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
// eslint-disable-next-line react-hooks/rules-of-hooks -- TODO this is potentially quite serious! please fix next time the file is edited, -@asktree
const [form, setForm] = useState<CreateSolendObligationAccountForm>({})

View File

@ -42,7 +42,7 @@ const DepositReserveLiquidityAndObligationCollateral = ({
return <>This instruction does not support {connection.cluster}</>
}
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [
form,

View File

@ -40,7 +40,7 @@ const InitObligationAccount = ({
return <>This instruction does not support {connection.cluster}</>
}
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
// eslint-disable-next-line react-hooks/rules-of-hooks -- TODO this is potentially quite serious! please fix next time the file is edited, -@asktree
const [form, setForm] = useState<InitSolendObligationAccountForm>({})

View File

@ -41,7 +41,7 @@ const RefreshObligation = ({
return <>This instruction does not support {connection.cluster}</>
}
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
// eslint-disable-next-line react-hooks/rules-of-hooks -- TODO this is potentially quite serious! please fix next time the file is edited, -@asktree
const [form, setForm] = useState<RefreshObligationForm>({})

View File

@ -35,7 +35,7 @@ const RefreshReserve = ({
const { realmInfo } = useRealm()
const { assetAccounts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<RefreshReserveForm>({})

View File

@ -43,7 +43,7 @@ const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({
return <>This instruction does not support {connection.cluster}</>
}
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [
form,

View File

@ -36,7 +36,7 @@ const SplTokenTransfer = ({
const wallet = useWalletStore((s) => s.current)
const { realmInfo } = useRealm()
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<SplTokenTransferForm>({
destinationAccount: '',

View File

@ -51,7 +51,7 @@ const CancelStream = ({
const strmProgram = new PublicKey(STREAMFLOW_PROGRAM_ID)
const { assetAccounts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = strmProgram
const [form, setForm] = useState<CancelStreamForm>({

View File

@ -117,7 +117,7 @@ const CreateStream = ({
const { assetAccounts } = useGovernanceAssets()
const treasuryInfo = useTreasuryInfo()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = strmProgram
const [releaseUnitIdx, setReleaseUnitIdx] = useState<number>(0)
const [startOnApproval, setStartOnApproval] = useState<boolean>(true)

View File

@ -31,7 +31,7 @@ const TransferDomainName = ({
governance: ProgramAccount<Governance> | null
}) => {
const connection = useWalletStore((s) => s.connection.current)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const { handleSetInstructions } = useContext(NewProposalContext)
const { assetAccounts } = useGovernanceAssets()

View File

@ -27,7 +27,7 @@ const UpdateTokenMetadata = ({
const mintGovernancesWithMintInfo = assetAccounts.filter(
(x) => x.type === AccountType.MINT
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<UpdateTokenMetadataForm>({
name: '',

View File

@ -32,7 +32,7 @@ const DeactivateValidatorStake = ({
const connection = useWalletStore((s) => s.connection)
const programId: PublicKey = StakeProgram.programId
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<ValidatorDeactivateStakeForm>({
stakingAccount: undefined,

View File

@ -35,7 +35,7 @@ const StakeValidator = ({
const connection = useWalletStore((s) => s.connection)
const programId: PublicKey = StakeProgram.programId
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const wallet = useWalletStore((s) => s.current)
const [form, setForm] = useState<ValidatorStakingForm>({

View File

@ -36,7 +36,7 @@ const WithdrawValidatorStake = ({
const connection = useWalletStore((s) => s.connection)
const programId: PublicKey = StakeProgram.programId
const { governedTokenAccountsWithoutNfts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<ValidatorWithdrawStakeForm>({
stakingAccount: undefined,

View File

@ -34,7 +34,7 @@ const CreateVsrRegistrar = ({
}) => {
const { realm, realmInfo } = useRealm()
const { assetAccounts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<CreateVsrRegistrarForm>()
const [formErrors, setFormErrors] = useState({})
const { handleSetInstructions } = useContext(NewProposalContext)

View File

@ -51,7 +51,7 @@ const VotingMintConfig = ({
}) => {
const { realm } = useRealm()
const { assetAccounts } = useGovernanceAssets()
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const [form, setForm] = useState<ConfigureCollectionForm>()
const [formErrors, setFormErrors] = useState({})
const { handleSetInstructions } = useContext(NewProposalContext)

View File

@ -37,7 +37,7 @@ const ProgramUpgrade = ({
const governedProgramAccounts = assetAccounts.filter(
(x) => x.type === AccountType.PROGRAM
)
const shouldBeGoverned = index !== 0 && governance
const shouldBeGoverned = !!(index !== 0 && governance)
const programId: PublicKey | undefined = realmInfo?.programId
const [form, setForm] = useState<ProgramUpgradeForm>({
governedAccount: undefined,

View File

@ -1,12 +1,20 @@
import create, { State } from 'zustand'
import axios from 'axios'
import {
getNativeTreasuryAddress,
Governance,
GovernanceAccountType,
Realm,
TOKEN_PROGRAM_ID,
ProgramAccount,
} from '@solana/spl-governance'
import { ProgramAccount } from '@solana/spl-governance'
import {
ParsedAccountData,
PublicKey,
AccountInfo as AccountInfoGeneric,
} from '@solana/web3.js'
import { AccountInfo, MintInfo } from '@solana/spl-token'
import { TokenAccountLayout } from '@blockworks-foundation/mango-client'
import {
AUXILIARY_TOKEN_ACCOUNTS,
DEFAULT_NATIVE_SOL_MINT,
@ -18,17 +26,14 @@ import {
import {
AccountInfoGen,
getMultipleAccountInfoChunked,
MintAccount,
parseMintAccountData,
parseTokenAccountData,
TokenAccount,
TokenProgramAccount,
} from '@utils/tokens'
import { ParsedAccountData, PublicKey } from '@solana/web3.js'
import { AccountInfo, MintInfo } from '@solana/spl-token'
import { AccountInfo as AccountInfoGeneric } from '@solana/web3.js'
import { TokenAccountLayout } from '@blockworks-foundation/mango-client'
import tokenService from '@utils/services/token'
import { ConnectionContext } from '@utils/connection'
import axios from 'axios'
import {
AccountType,
AccountTypeGeneric,
@ -40,7 +45,7 @@ import {
AccountTypeToken,
AssetAccount,
} from '@utils/uiTypes/assets'
import group from '@utils/group'
import { chunks } from '@utils/helpers'
const tokenAccountOwnerOffset = 32
@ -49,11 +54,13 @@ interface SolAccInfo {
acc: any
nativeSolAddress: PublicKey
}
interface GovernanceAssetsStore extends State {
governancesArray: ProgramAccount<Governance>[]
governedTokenAccounts: AssetAccount[]
assetAccounts: AssetAccount[]
loadGovernedAccounts: boolean
setGovernancesArray: (
connection: ConnectionContext,
realm: ProgramAccount<Realm>,
@ -81,21 +88,32 @@ const defaultState = {
const useGovernanceAssetsStore = create<GovernanceAssetsStore>((set, _get) => ({
...defaultState,
setGovernancesArray: (connection, realm, governances) => {
const array = Object.keys(governances)
setGovernancesArray: (
connection: ConnectionContext,
realm: ProgramAccount<Realm>,
governances: {
[governance: string]: ProgramAccount<Governance>
}
) => {
const array: ProgramAccount<Governance>[] = Object.keys(governances)
.filter((gpk) => !HIDDEN_GOVERNANCES.has(gpk))
.map((key) => governances[key])
set((s) => {
s.governancesArray = array
})
_get().getGovernedAccounts(connection, realm)
},
getGovernedAccounts: async (connection, realm) => {
set((s) => {
s.loadGovernedAccounts = true
s.governedTokenAccounts = []
s.assetAccounts = []
})
const governancesArray = _get().governancesArray
const accounts = governancesArray.length
? await getAccountsForGovernances(connection, realm, governancesArray)
@ -111,27 +129,33 @@ const useGovernanceAssetsStore = create<GovernanceAssetsStore>((set, _get) => ({
x.type === AccountType.NFT ||
x.type === AccountType.SOL
)
.filter(filterOutHiddenAccs)
s.assetAccounts = accounts.filter(filterOutHiddenAccs)
.filter(filterOutHiddenAccounts)
s.assetAccounts = accounts.filter(filterOutHiddenAccounts)
})
},
refetchGovernanceAccounts: async (connection, realm, governancePk) => {
refetchGovernanceAccounts: async (
connection: ConnectionContext,
realm: ProgramAccount<Realm>,
governancePk: PublicKey
) => {
set((s) => {
s.loadGovernedAccounts = false
})
const governancesArray = _get().governancesArray.filter(
(x) => x.pubkey?.toBase58() === governancePk.toBase58()
const governancesArray = _get().governancesArray.filter((x) =>
x.pubkey.equals(governancePk)
)
const previousAccounts = _get().assetAccounts.filter(
(x) => x.governance?.pubkey?.toBase58() !== governancePk.toBase58()
(x) => !x.governance.pubkey.equals(governancePk)
)
const accounts = await getAccountsForGovernances(
connection,
realm,
governancesArray
)
set((s) => {
s.loadGovernedAccounts = false
s.governedTokenAccounts = [...previousAccounts, ...accounts]
@ -141,9 +165,9 @@ const useGovernanceAssetsStore = create<GovernanceAssetsStore>((set, _get) => ({
x.type === AccountType.NFT ||
x.type === AccountType.SOL
)
.filter(filterOutHiddenAccs)
.filter(filterOutHiddenAccounts)
s.assetAccounts = [...previousAccounts, ...accounts].filter(
filterOutHiddenAccs
filterOutHiddenAccounts
)
})
},
@ -154,50 +178,73 @@ const getTokenAccountObj = async (
governance: ProgramAccount<Governance>,
tokenAccount: TokenProgramAccount<AccountInfo>,
mintAccounts: TokenProgramAccount<MintInfo>[]
) => {
): Promise<AccountTypeNFT | AccountTypeToken | null> => {
const isNftAccount =
tokenAccount.account.mint.toBase58() === DEFAULT_NFT_TREASURY_MINT
const mint = mintAccounts.find(
(x) => x.publicKey.toBase58() === tokenAccount.account.mint.toBase58()
)
const mint = mintAccounts.find((x) =>
x.publicKey.equals(tokenAccount.account.mint)
)!
if (isNftAccount) {
return new AccountTypeNFT(tokenAccount, mint!, governance)
return new AccountTypeNFT(tokenAccount, mint, governance)
}
if (
mint?.account.supply &&
mint?.account.supply.cmpn(1) !== 0 &&
mint.account.supply &&
mint.account.supply.cmpn(1) !== 0 &&
mint.publicKey.toBase58() !== DEFAULT_NATIVE_SOL_MINT
) {
return new AccountTypeToken(tokenAccount, mint!, governance)
}
return null
}
const getSolAccountsObj = async (
connection: ConnectionContext,
accounts: AssetAccount[],
solAccountsInfo: SolAccInfo[],
mintAccounts: TokenProgramAccount<MintInfo>[],
mintAccounts: TokenProgramAccount<MintAccount>[],
governances: ProgramAccount<Governance>[]
) => {
const solAccs: AccountTypeSol[] = []
for (const i of solAccountsInfo) {
const mint = mintAccounts.find((x) => x.publicKey.toBase58() === WSOL_MINT)
const governance = governances.find(
(x) => x.pubkey.toBase58() === i.governancePk.toBase58()
)
): Promise<AssetAccount[]> => {
const solAccounts: AccountTypeSol[] = []
const wsolMintAccount = mintAccounts.find(
(x) => x.publicKey.toBase58() === WSOL_MINT
)! // WSOL should be here
for (const solAccountInfo of solAccountsInfo) {
const governance = governances.find((x) =>
x.pubkey.equals(solAccountInfo.governancePk)
)! // Governance should be here
const account = await getSolAccountObj(
governance!,
governance,
connection,
mint!,
wsolMintAccount,
accounts,
i
solAccountInfo
)
if (account) {
solAccs.push(account)
solAccounts.push(account)
}
}
return solAccs as AssetAccount[]
return solAccounts
}
// Return array without duplicates
const uniquePublicKey = (array: PublicKey[]): PublicKey[] => {
return Array.from(
array.reduce((mintsPks, publicKey) => {
// Transform to string for Set to be able to identify duplicates
mintsPks.add(publicKey.toBase58())
return mintsPks
}, new Set<string>())
).map((address) => new PublicKey(address))
}
const getTokenAssetAccounts = async (
@ -210,25 +257,32 @@ const getTokenAssetAccounts = async (
connection: ConnectionContext
) => {
const accounts: AssetAccount[] = []
const mintsPks = [...tokenAccounts.map((x) => x.account.mint)]
//WSOL is used as mint for sol accounts to calculate amounts
if (!mintsPks.find((x) => x.toBase58() === WSOL_MINT)) {
const mintsPks = uniquePublicKey(
tokenAccounts.map((tokenAccount) => tokenAccount.account.mint)
)
// WSOL must be in the mintsPks array
// WSOL is used as mint for sol accounts to calculate amounts
if (!mintsPks.some((x) => x.toBase58() === WSOL_MINT)) {
mintsPks.push(new PublicKey(WSOL_MINT))
}
const mintAccounts = mintsPks.length
? await getMintAccountsInfo(connection, [...mintsPks])
: []
const nativeSolAddresses = await Promise.all(
governances.map((x) => getNativeTreasuryAddress(realm.owner, x!.pubkey))
const [mintAccounts, ...nativeSolAddresses] = await Promise.all([
getMintAccountsInfo(connection, mintsPks),
...governances.map((x) => getNativeTreasuryAddress(realm.owner, x.pubkey)),
])
const govNativeSolAddress = nativeSolAddresses.map((x, index) => ({
governanceAcc: governances[index],
governancePk: governances[index].pubkey,
nativeSolAddress: x,
}))
const solAccountsInfo = await getSolAccountsInfo(
connection,
govNativeSolAddress
)
const govNativeSolAddress = nativeSolAddresses.map((x, index) => {
return {
governanceAcc: governances[index],
governancePk: governances[index].pubkey,
nativeSolAddress: x,
}
})
const solAccs = await getSolAccountsInfo(connection, govNativeSolAddress)
for (const tokenAccount of tokenAccounts) {
let governance = governances.find(
@ -261,24 +315,26 @@ const getTokenAssetAccounts = async (
const mint = mintAccounts.find(
(x) => x.publicKey.toBase58() === tokenAccount.account.mint.toBase58()
)
const account = new AccountTypeAuxiliaryToken(tokenAccount, mint)
if (account) {
accounts.push(account)
if (mint) {
const account = new AccountTypeAuxiliaryToken(tokenAccount, mint)
if (account) {
accounts.push(account)
}
}
}
}
const solAccounts = await getSolAccountsObj(
connection,
accounts,
solAccs,
solAccountsInfo,
mintAccounts,
governances
)
if (solAccounts.length) {
accounts.push(...solAccounts)
}
return accounts
return [...accounts, ...solAccounts]
}
const getMintAccounts = (
@ -305,38 +361,27 @@ const getMintAccounts = (
const getProgramAssetAccounts = (
programGovernances: ProgramAccount<Governance>[]
) => {
const accounts: AccountTypeProgram[] = []
programGovernances.forEach((programGov) => {
const account = new AccountTypeProgram(programGov!)
if (account) {
accounts.push(account)
}
})
return accounts
): AccountTypeProgram[] => {
return programGovernances.map(
(programGov) => new AccountTypeProgram(programGov)
)
}
const getGenericAssetAccounts = (
genericGovernances: ProgramAccount<Governance>[]
) => {
const accounts: AccountTypeGeneric[] = []
genericGovernances.forEach((programGov) => {
const account = new AccountTypeGeneric(programGov)
if (account) {
accounts.push(account)
}
})
return accounts
): AccountTypeGeneric[] => {
return genericGovernances.map(
(programGov) => new AccountTypeGeneric(programGov)
)
}
const getGovernancesByAccountTypes = (
governancesArray: ProgramAccount<Governance>[],
types: GovernanceAccountType[]
) => {
const governancesFiltered = governancesArray.filter((gov) =>
types.some((t) => gov.account?.accountType === t)
): ProgramAccount<Governance>[] => {
return governancesArray.filter((gov) =>
types.some((t) => gov.account.accountType === t)
)
return governancesFiltered
}
const getSolAccountObj = async (
@ -344,87 +389,282 @@ const getSolAccountObj = async (
connection: ConnectionContext,
mint: TokenProgramAccount<MintInfo>,
accounts: AssetAccount[],
solAcc: SolAccInfo
) => {
if (solAcc.acc) {
const tokenAccountsOwnedBySolAccountInfo = await connection.current.getTokenAccountsByOwner(
solAcc.nativeSolAddress,
{
programId: TOKEN_PROGRAM_ID,
}
)
const tokenAccountsOwnedBySolAccounts = tokenAccountsOwnedBySolAccountInfo.value.map(
(x) => {
const publicKey = x.pubkey
const data = Buffer.from(x.account.data)
const account = parseTokenAccountData(publicKey, data)
return { publicKey, account }
}
)
const groups = group(tokenAccountsOwnedBySolAccounts)
const results = await Promise.all(
{ acc, nativeSolAddress }: SolAccInfo
): Promise<AccountTypeSol | null> => {
if (!acc) {
return null
}
const tokenAccountsOwnedBySolAccountInfo = await connection.current.getTokenAccountsByOwner(
nativeSolAddress,
{
programId: TOKEN_PROGRAM_ID,
}
)
const tokenAccountsOwnedBySolAccounts = tokenAccountsOwnedBySolAccountInfo.value.map(
({ pubkey: publicKey, account: { data: encodedData } }) => {
const data = Buffer.from(encodedData)
const account = parseTokenAccountData(publicKey, data)
return { publicKey, account }
}
)
const groups = chunks(tokenAccountsOwnedBySolAccounts, 100)
const mintAccounts = (
await Promise.all(
groups.map((group) => {
if (group.length) {
return getMintAccountsInfo(
connection,
group.map((x) => x.account.mint)
)
} else {
if (!group.length) {
return []
}
return getMintAccountsInfo(
connection,
group.map((x) => x.account.mint)
)
})
)
).flat()
const mintAccounts = results.flat()
for (const acc of tokenAccountsOwnedBySolAccounts) {
const account = await getTokenAccountObj(governance, acc, mintAccounts)
for (const acc of tokenAccountsOwnedBySolAccounts) {
const account = await getTokenAccountObj(governance, acc, mintAccounts)
if (account) {
accounts.push(account)
}
if (account) {
accounts.push(account)
}
const minRentAmount = await connection.current.getMinimumBalanceForRentExemption(
0
)
const solAccount = solAcc.acc as AccountInfoGen<Buffer | ParsedAccountData>
solAccount.lamports =
solAccount.lamports !== 0
? solAccount.lamports - minRentAmount
: solAccount.lamports
return new AccountTypeSol(
mint!,
solAcc.nativeSolAddress,
solAccount,
governance
)
}
const minRentAmount = await connection.current.getMinimumBalanceForRentExemption(
0
)
const solAccount = acc as AccountInfoGen<Buffer | ParsedAccountData>
solAccount.lamports =
solAccount.lamports !== 0
? solAccount.lamports - minRentAmount
: solAccount.lamports
return new AccountTypeSol(mint, nativeSolAddress, solAccount, governance)
}
const getAccountsForGovernances = async (
const filterOutHiddenAccounts = (x: AssetAccount) => {
const pubkey = typeof x.pubkey === 'string' ? x.pubkey : x.pubkey.toBase58()
return (
HIDDEN_TREASURES.findIndex((x) => x === pubkey) === -1 &&
(!x.extensions.token ||
!x.extensions.token?.account.isFrozen ||
x.type !== AccountType.GENERIC)
)
}
// Return array without duplicates
const uniqueGovernedTokenAccounts = (
assetAccounts: AssetAccount[]
): AssetAccount[] => {
const existing = new Set<string>()
const deduped: AssetAccount[] = []
for (const account of assetAccounts) {
if (!existing.has(account.pubkey.toBase58())) {
existing.add(account.pubkey.toBase58())
deduped.push(account)
}
}
return deduped
}
const getMintAccountsInfo = async (
{ endpoint, current: { commitment } }: ConnectionContext,
publicKeys: PublicKey[]
): Promise<TokenProgramAccount<MintAccount>[]> => {
const { data: mintAccountsJson } = await axios.post(
endpoint,
publicKeys.map((pubkey) => {
const id = pubkey.toBase58()
return {
jsonrpc: '2.0',
id,
method: 'getAccountInfo',
params: [
id,
{
commitment,
encoding: 'base64',
},
],
}
})
)
if (!mintAccountsJson) {
throw new Error(
`Cannot load information about mint accounts ${publicKeys.map((x) =>
x.toBase58()
)}`
)
}
return mintAccountsJson.map(
({
result: {
value: {
data: [encodedData],
},
},
id,
}) => {
const publicKey = new PublicKey(id)
const data = Buffer.from(encodedData, 'base64')
const account = parseMintAccountData(data)
return { publicKey, account }
}
)
}
const getTokenAccountsInfo = async (
{ endpoint, current: { commitment } }: ConnectionContext,
publicKeys: PublicKey[]
): Promise<TokenProgramAccount<TokenAccount>[]> => {
const { data: tokenAccountsInfoJson } = await axios.post<
unknown,
{
data: {
result: {
account: {
data: [string, 'base64']
}
pubkey: string
}[]
}[]
}
>(
endpoint,
publicKeys.map((publicKey) => ({
jsonrpc: '2.0',
id: 1,
method: 'getProgramAccounts',
params: [
TOKEN_PROGRAM_ID.toBase58(),
{
commitment,
encoding: 'base64',
filters: [
{
// number of bytes
dataSize: TokenAccountLayout.span,
},
{
memcmp: {
// number of bytes
offset: tokenAccountOwnerOffset,
bytes: publicKey.toBase58(),
},
},
],
},
],
}))
)
if (!tokenAccountsInfoJson) {
throw new Error(
`Cannot load information about token accounts ${publicKeys.map((x) =>
x.toBase58()
)}`
)
}
return tokenAccountsInfoJson.reduce((tokenAccountsInfo, { result }) => {
result.forEach(
({
account: {
data: [encodedData],
},
pubkey,
}) => {
const publicKey = new PublicKey(pubkey)
const data = Buffer.from(encodedData, 'base64')
const account = parseTokenAccountData(publicKey, data)
tokenAccountsInfo.push({ publicKey, account })
}
)
return tokenAccountsInfo
}, [] as TokenProgramAccount<TokenAccount>[])
}
const getSolAccountsInfo = async (
connection: ConnectionContext,
realm: ProgramAccount<Realm>,
governancesArray: ProgramAccount<Governance>[]
publicKeys: { governancePk: PublicKey; nativeSolAddress: PublicKey }[]
): Promise<SolAccInfo[]> => {
const { data: solAccountsJson } = await axios.post<
unknown,
{
data: {
result: {
value: null | {
data: [string, 'base64']
}
}
}[]
}
>(
connection.endpoint,
publicKeys.map((x) => ({
jsonrpc: '2.0',
id: 1,
method: 'getAccountInfo',
params: [
x.nativeSolAddress.toBase58(),
{
commitment: connection.current.commitment,
encoding: 'jsonParsed',
},
],
}))
)
if (!solAccountsJson.length) {
return []
}
return (
solAccountsJson
.flatMap(({ result: { value } }, index: number) => {
return {
acc: value,
...publicKeys[index],
}
})
// Remove null values
.filter(({ acc }) => acc)
)
}
const loadMintGovernanceAccounts = async (
connection: ConnectionContext,
mintGovernances: ProgramAccount<Governance>[]
) => {
const mintGovernances = getGovernancesByAccountTypes(governancesArray, [
GovernanceAccountType.MintGovernanceV1,
GovernanceAccountType.MintGovernanceV2,
])
const programGovernances = getGovernancesByAccountTypes(governancesArray, [
GovernanceAccountType.ProgramGovernanceV1,
GovernanceAccountType.ProgramGovernanceV2,
])
const genericGovernances = getGenericAssetAccounts(governancesArray)
const mintGovernancesMintInfo = await getMultipleAccountInfoChunked(
connection.current,
mintGovernances.map((x) => x.account.governedAccount)
)
const mintAccounts = getMintAccounts(mintGovernances, mintGovernancesMintInfo)
const programAccounts = getProgramAssetAccounts(programGovernances)
const auxiliaryTokenAccounts = AUXILIARY_TOKEN_ACCOUNTS[realm.account.name]
?.length
return getMintAccounts(mintGovernances, mintGovernancesMintInfo)
}
const loadGovernedTokenAccounts = async (
connection: ConnectionContext,
realm: ProgramAccount<Realm>,
governancesArray: ProgramAccount<Governance>[]
): Promise<AssetAccount[]> => {
const auxiliaryTokenAccounts: typeof AUXILIARY_TOKEN_ACCOUNTS[keyof typeof AUXILIARY_TOKEN_ACCOUNTS] = AUXILIARY_TOKEN_ACCOUNTS[
realm.account.name
]?.length
? AUXILIARY_TOKEN_ACCOUNTS[realm.account.name]
: []
@ -434,188 +674,98 @@ const getAccountsForGovernances = async (
)
)
const fetchTokenAccounts = (addresses: string[]) =>
axios.request({
url: connection.endpoint,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify(
addresses.map((address) => ({
jsonrpc: '2.0',
id: 1,
method: 'getProgramAccounts',
params: [
TOKEN_PROGRAM_ID.toBase58(),
{
commitment: connection.current.commitment,
encoding: 'base64',
filters: [
{
dataSize: TokenAccountLayout.span, // number of bytes
},
{
memcmp: {
offset: tokenAccountOwnerOffset, // number of bytes
bytes: address, // base58 encoded string
},
},
],
},
],
}))
),
})
const tokenAccountsOwnedByGovernances = uniquePublicKey([
...nativeAccountAddresses,
...governancesArray.map((g) => g.pubkey),
...auxiliaryTokenAccounts.map((x) => new PublicKey(x.owner)),
])
const ownedByGovernancesTokenAccounts = await Promise.all([
fetchTokenAccounts(nativeAccountAddresses.map((a) => a.toBase58())),
fetchTokenAccounts(governancesArray.map((g) => g.pubkey.toBase58())),
auxiliaryTokenAccounts?.length
? fetchTokenAccounts(auxiliaryTokenAccounts.map((x) => x.owner))
: Promise.resolve({ data: [] }),
]).then(([x, y, z]) => x.data.concat(y.data).concat(z.data))
const tokenAccountsJson = ownedByGovernancesTokenAccounts.map((x) => {
const auxiliaryMatch = auxiliaryTokenAccounts.find(
(auxAcc) => auxAcc.owner === x.id
)
if (auxiliaryMatch) {
x.result = x.result?.filter((x) =>
auxiliaryMatch.accounts.includes(x.pubkey)
const tokenAccountsInfo = (
await Promise.all(
// Load infos in batch, cannot load 9999 accounts within one request
chunks(tokenAccountsOwnedByGovernances, 100).map((group) =>
getTokenAccountsInfo(connection, group)
)
}
return x
})
const tokenAccountsParsed = tokenAccountsJson.length
? [...tokenAccountsJson.flatMap((x) => x.result)].map((x) => {
const publicKey = new PublicKey(x.pubkey)
const data = Buffer.from(x.account.data[0], 'base64')
const account = parseTokenAccountData(publicKey, data)
return { publicKey, account }
})
: []
)
).flat()
const groups = group(tokenAccountsParsed)
const results = await Promise.all(
groups.map((group) => {
return getTokenAssetAccounts(group, governancesArray, realm, connection)
})
const governedTokenAccounts = (
await Promise.all(
// Load infos in batch, cannot load 9999 accounts within one request
chunks(tokenAccountsInfo, 100).map((group) =>
getTokenAssetAccounts(group, governancesArray, realm, connection)
)
)
).flat()
// Remove potential accounts duplicate
return uniqueGovernedTokenAccounts(governedTokenAccounts)
}
const getAccountsForGovernances = async (
connection: ConnectionContext,
realm: ProgramAccount<Realm>,
governancesArray: ProgramAccount<Governance>[]
): Promise<
(AccountTypeMint | AccountTypeProgram | AssetAccount | AccountTypeGeneric)[]
> => {
// 1 - Sort different types of governances
const mintGovernances = getGovernancesByAccountTypes(governancesArray, [
GovernanceAccountType.MintGovernanceV1,
GovernanceAccountType.MintGovernanceV2,
])
const programGovernances = getGovernancesByAccountTypes(governancesArray, [
GovernanceAccountType.ProgramGovernanceV1,
GovernanceAccountType.ProgramGovernanceV2,
])
// 2 - Load accounts related to mint governances
const mintAccounts = await loadMintGovernanceAccounts(
connection,
mintGovernances
)
const allResults = results.flat()
// remove potential duplicates
const existing = new Set<string>()
const deduped: AssetAccount[] = []
// 3 - Load accounts related to program governances
const programAccounts = getProgramAssetAccounts(programGovernances)
for (const account of allResults) {
if (!existing.has(account.pubkey.toBase58())) {
existing.add(account.pubkey.toBase58())
deduped.push(account)
}
}
// 4 - Load token accounts behind any type of governance
const governedTokenAccounts = await loadGovernedTokenAccounts(
connection,
realm,
governancesArray
)
const tokenAssetAccounts = deduped
const governedTokenAccounts = tokenAssetAccounts
// 5 - Call to fetch token prices for every token account's mints
await tokenService.fetchTokenPrices(
governedTokenAccounts
.filter((x) => x.extensions.mint?.publicKey)
.map((x) => x.extensions.mint!.publicKey.toBase58())
governedTokenAccounts.reduce((mints, governedTokenAccount) => {
if (!governedTokenAccount.extensions.mint?.publicKey) {
return mints
}
return [
...mints,
governedTokenAccount.extensions.mint.publicKey.toBase58(),
]
}, [] as string[])
)
return [
const accounts = [
...mintAccounts,
...programAccounts,
...governedTokenAccounts,
...genericGovernances,
]
}
const getMintAccountsInfo = async (
connection: ConnectionContext,
pubkeys: PublicKey[]
) => {
const mintAccountsInfo = await axios.request({
url: connection.endpoint,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify([
...pubkeys.map((x) => {
return {
jsonrpc: '2.0',
id: x.toBase58(),
method: 'getAccountInfo',
params: [
x.toBase58(),
{
commitment: connection.current.commitment,
encoding: 'base64',
},
],
}
}),
]),
})
const mintAccountsJson = mintAccountsInfo.data
const mintAccountsParsed = mintAccountsJson?.map((x) => {
const result = x.result
const publicKey = new PublicKey(x.id)
const data = Buffer.from(result.value.data[0], 'base64')
const account = parseMintAccountData(data)
return { publicKey, account }
})
return mintAccountsParsed
}
const getSolAccountsInfo = async (
connection: ConnectionContext,
pubkeys: { governancePk: PublicKey; nativeSolAddress: PublicKey }[]
) => {
const solAccountsInfo = await axios.request({
url: connection.endpoint,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify([
...pubkeys.map((x) => {
return {
jsonrpc: '2.0',
id: 1,
method: 'getAccountInfo',
params: [
x.nativeSolAddress.toBase58(),
{
commitment: connection.current.commitment,
encoding: 'jsonParsed',
},
],
}
}),
]),
})
const solAccountsJson = solAccountsInfo.data
const solAccountsParsed = solAccountsJson?.length
? solAccountsJson
.flatMap((x, index) => {
return {
acc: x.result.value,
...pubkeys[index],
}
})
.filter((x) => x.acc)
: []
return solAccountsParsed as SolAccInfo[]
}
const filterOutHiddenAccs = (x: AssetAccount) => {
const pubkey = typeof x.pubkey === 'string' ? x.pubkey : x.pubkey.toBase58()
return (
HIDDEN_TREASURES.findIndex((x) => x === pubkey) === -1 &&
(!x.extensions.token ||
!x.extensions.token?.account.isFrozen ||
x.type !== AccountType.GENERIC)
// 6 - Create generic asset accounts for governance's governedAccounts that have not been handled yet
// We do this so theses accounts may be selected
const genericGovernances = getGenericAssetAccounts(
governancesArray.filter(
(governance) =>
!accounts.some((account) =>
account.pubkey.equals(governance.account.governedAccount)
)
)
)
return [...accounts, ...genericGovernances]
}

View File

@ -282,7 +282,7 @@ function ForesightGovernedAccountSelect(props: {
index: number
governance: ProgramAccount<Governance> | null
}) {
const shouldBeGoverned = props.index !== 0 && props.governance
const shouldBeGoverned = !!(props.index !== 0 && props.governance)
return (
<GovernedAccountSelect
label="Program"

View File

@ -184,7 +184,7 @@ export function parseTokenAccountData(
return accountInfo
}
export function parseMintAccountData(data: Buffer) {
export function parseMintAccountData(data: Buffer): MintAccount {
const mintInfo = MintLayout.decode(data)
if (mintInfo.mintAuthorityOption === 0) {
mintInfo.mintAuthority = null