diff --git a/pages/dao/[symbol]/proposal/components/instructions/UpdateTokenMetadata.tsx b/pages/dao/[symbol]/proposal/components/instructions/UpdateTokenMetadata.tsx index 08f823651..654271688 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UpdateTokenMetadata.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UpdateTokenMetadata.tsx @@ -78,7 +78,7 @@ const UpdateTokenMetadata = ({ }, [form, governedAccount]) useEffect(() => { setGovernedAccount(form?.mintAccount?.governance) - setMintAuthority(form?.mintAccount?.extensions.mint?.account.mintAuthority) + setMintAuthority(form?.mintAccount?.governance.nativeTreasuryAddress) }, [form.mintAccount]) const schema = getUpdateTokenMetadataSchema() diff --git a/stores/useGovernanceAssetsStore.tsx b/stores/useGovernanceAssetsStore.tsx index 24b1fdab7..9dcad1c0c 100644 --- a/stores/useGovernanceAssetsStore.tsx +++ b/stores/useGovernanceAssetsStore.tsx @@ -7,14 +7,14 @@ import { TOKEN_PROGRAM_ID, ProgramAccount, GovernanceAccountType, -} from '@realms-today/spl-governance' +} from '@solana/spl-governance' import { LAMPORTS_PER_SOL, ParsedAccountData, PublicKey, StakeProgram, } from '@solana/web3.js' -import { MintInfo, u64 } from '@solana/spl-token' +import { AccountInfo, MintInfo } from '@solana/spl-token' import { AUXILIARY_TOKEN_ACCOUNTS, DEFAULT_NATIVE_SOL_MINT, @@ -47,12 +47,11 @@ import { GovernanceProgramAccountWithNativeTreasuryAddress, AccountTypeStake, StakeState, - isToken2022, } from '@utils/uiTypes/assets' import group from '@utils/group' import { getFilteredProgramAccounts } from '@utils/helpers' import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes' -import { AccountLayout, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token-new' +import { getMetadataAddress } from '@utils/metaplex' const additionalPossibleMintAccounts = { Mango: [ @@ -82,16 +81,16 @@ interface GovernanceAssetsStore extends State { setGovernancesArray: ( connection: ConnectionContext, realm: ProgramAccount, - governances: ProgramAccount[], + governances: ProgramAccount[] ) => void getGovernedAccounts: ( connection: ConnectionContext, - realm: ProgramAccount, + realm: ProgramAccount ) => Promise refetchGovernanceAccounts: ( connection: ConnectionContext, realm: ProgramAccount, - governancePk: PublicKey, + governancePk: PublicKey ) => Promise } @@ -111,10 +110,10 @@ const useGovernanceAssetsStore = create((set, _get) => ({ setGovernancesArray: ( connection: ConnectionContext, realm: ProgramAccount, - governances: ProgramAccount[], + governances: ProgramAccount[] ) => { const array: ProgramAccount[] = governances.filter( - (gov) => !HIDDEN_GOVERNANCES.has(gov.pubkey.toString()), + (gov) => !HIDDEN_GOVERNANCES.has(gov.pubkey.toString()) ) set((s) => { @@ -136,20 +135,16 @@ const useGovernanceAssetsStore = create((set, _get) => ({ const governancesArray = _get().governancesArray const accounts: AssetAccount[] = [] - const nativeAddresses = [ - ...governancesArray.map((x) => { - const [signatoryRecordAddress] = PublicKey.findProgramAddressSync( - [Buffer.from('native-treasury'), x.pubkey.toBuffer()], - realm.owner, - ) - return signatoryRecordAddress - }), - ] + const nativeAddresses = await Promise.all([ + ...governancesArray.map((x) => + getNativeTreasuryAddress(realm.owner, x.pubkey) + ), + ]) const governancesWithNativeTreasuryAddress = governancesArray.map( (x, index) => ({ ...x, nativeTreasuryAddress: nativeAddresses[index], - }), + }) ) //due to long request for mint accounts that are owned by every governance //we fetch @@ -160,7 +155,6 @@ const useGovernanceAssetsStore = create((set, _get) => ({ const additionalMintAccounts = additionalPossibleMintAccounts[realm.account.name] - if (additionalMintAccounts) { possibleMintAccountPks.push(...additionalMintAccounts) } @@ -168,7 +162,7 @@ const useGovernanceAssetsStore = create((set, _get) => ({ const governedTokenAccounts = await loadGovernedTokenAccounts( connection, realm, - governancesWithNativeTreasuryAddress, + governancesWithNativeTreasuryAddress ) // 2 - Call to fetch token prices for every token account's mints await tokenPriceService.fetchTokenPrices( @@ -181,12 +175,14 @@ const useGovernanceAssetsStore = create((set, _get) => ({ ...mints, governedTokenAccount.extensions.mint.publicKey.toBase58(), ] - }, [] as string[]), + }, [] as string[]) ) accounts.push(...governedTokenAccounts) const stakeAccounts = await loadStakeAccounts( connection, - governedTokenAccounts.filter((x) => x.isSol), + governedTokenAccounts.filter( + (x) => x.isSol + ) ) accounts.push(...stakeAccounts) @@ -197,7 +193,7 @@ const useGovernanceAssetsStore = create((set, _get) => ({ (x) => x.type === AccountType.TOKEN || x.type === AccountType.NFT || - x.type === AccountType.SOL, + x.type === AccountType.SOL ) .filter(filterOutHiddenAccounts) s.assetAccounts = accounts.filter(filterOutHiddenAccounts) @@ -207,7 +203,7 @@ const useGovernanceAssetsStore = create((set, _get) => ({ const mintAccounts = await loadMintGovernanceAccounts( connection, governancesWithNativeTreasuryAddress, - possibleMintAccountPks, + possibleMintAccountPks ) accounts.push(...mintAccounts) set((s) => { @@ -218,7 +214,7 @@ const useGovernanceAssetsStore = create((set, _get) => ({ // 4 - Load accounts related to program governances const programAccounts = await getProgramAssetAccounts( connection, - governancesWithNativeTreasuryAddress, + governancesWithNativeTreasuryAddress ) accounts.push(...programAccounts) set((s) => { @@ -232,9 +228,9 @@ const useGovernanceAssetsStore = create((set, _get) => ({ governancesWithNativeTreasuryAddress.filter( (governance) => !accounts.some((account) => - account.pubkey.equals(governance.account.governedAccount), - ), - ), + account.pubkey.equals(governance.account.governedAccount) + ) + ) ) accounts.push(...genericGovernances) @@ -246,24 +242,24 @@ const useGovernanceAssetsStore = create((set, _get) => ({ refetchGovernanceAccounts: async ( connection: ConnectionContext, realm: ProgramAccount, - governancePk: PublicKey, + governancePk: PublicKey ) => { set((s) => { s.loadGovernedAccounts = false }) const governancesArray = _get().governancesArray.filter((x) => - x.pubkey.equals(governancePk), + x.pubkey.equals(governancePk) ) const previousAccounts = _get().assetAccounts.filter( - (x) => !x.governance.pubkey.equals(governancePk), + (x) => !x.governance.pubkey.equals(governancePk) ) const accounts = await getAccountsForGovernances( connection, realm, - governancesArray, + governancesArray ) set((s) => { @@ -273,35 +269,31 @@ const useGovernanceAssetsStore = create((set, _get) => ({ (x) => x.type === AccountType.TOKEN || x.type === AccountType.NFT || - x.type === AccountType.SOL, + x.type === AccountType.SOL ) .filter(filterOutHiddenAccounts) s.assetAccounts = [...previousAccounts, ...accounts].filter( - filterOutHiddenAccounts, + filterOutHiddenAccounts ) }) }, })) export default useGovernanceAssetsStore -const getTokenAccountObj = ( +const getTokenAccountObj = async ( governance: GovernanceProgramAccountWithNativeTreasuryAddress, - tokenAccount: TokenProgramAccount, - mintAccounts: TokenProgramAccount[], -): AccountTypeNFT | AccountTypeToken | null => { + tokenAccount: TokenProgramAccount, + mintAccounts: TokenProgramAccount[] +): Promise => { const isNftAccount = tokenAccount.account.mint.toBase58() === DEFAULT_NFT_TREASURY_MINT const mint = mintAccounts.find((x) => - x.publicKey.equals(tokenAccount.account.mint), + x.publicKey.equals(tokenAccount.account.mint) )! - if (isNftAccount && !isToken2022(tokenAccount.account)) { - return new AccountTypeNFT( - tokenAccount as TokenProgramAccount, - mint, - governance, - ) + if (isNftAccount) { + return new AccountTypeNFT(tokenAccount, mint, governance) } if ( @@ -309,36 +301,36 @@ const getTokenAccountObj = ( mint.account.supply.cmpn(1) !== 0 && mint.publicKey.toBase58() !== DEFAULT_NATIVE_SOL_MINT ) { - return new AccountTypeToken( - tokenAccount as TokenProgramAccount, - mint!, - governance, - ) + return new AccountTypeToken(tokenAccount, mint!, governance) } return null } const getSolAccountsObj = async ( + connection: ConnectionContext, + accounts: AssetAccount[], solAccountsInfo: SolAccInfo[], mintAccounts: TokenProgramAccount[], - governances: GovernanceProgramAccountWithNativeTreasuryAddress[], + governances: GovernanceProgramAccountWithNativeTreasuryAddress[] ): Promise => { const solAccounts: AccountTypeSol[] = [] const wsolMintAccount = mintAccounts.find( - (x) => x.publicKey.toBase58() === WSOL_MINT, + (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), + x.pubkey.equals(solAccountInfo.governancePk) )! // Governance should be here - const account = getSolAccountObj( + const account = await getSolAccountObj( governance, + connection, wsolMintAccount, - solAccountInfo, + accounts, + solAccountInfo ) if (account) { @@ -357,22 +349,22 @@ const uniquePublicKey = (array: PublicKey[]): PublicKey[] => { mintsPks.add(publicKey.toBase58()) return mintsPks - }, new Set()), + }, new Set()) ).map((address) => new PublicKey(address)) } const getTokenAssetAccounts = async ( tokenAccounts: { publicKey: PublicKey - account: TokenAccount + account: AccountInfo }[], governances: GovernanceProgramAccountWithNativeTreasuryAddress[], - connection: ConnectionContext, + connection: ConnectionContext ) => { const accounts: AssetAccount[] = [] const mintsPks = uniquePublicKey( - tokenAccounts.map((tokenAccount) => tokenAccount.account.mint), + tokenAccounts.map((tokenAccount) => tokenAccount.account.mint) ) // WSOL must be in the mintsPks array @@ -394,41 +386,38 @@ const getTokenAssetAccounts = async ( for (const tokenAccount of tokenAccounts) { let governance = governances.find( - (x) => x.pubkey.toBase58() === tokenAccount.account.owner.toBase58(), + (x) => x.pubkey.toBase58() === tokenAccount.account.owner.toBase58() ) const nativeSolAddress = govNativeSolAddress.find((x) => - x.nativeSolAddress.equals(tokenAccount.account.owner), + x.nativeSolAddress.equals(tokenAccount.account.owner) )?.nativeSolAddress if (!governance && nativeSolAddress) { governance = govNativeSolAddress.find((x) => - x.nativeSolAddress.equals(nativeSolAddress), + x.nativeSolAddress.equals(nativeSolAddress) )?.governanceAcc } if (governance) { - const account = getTokenAccountObj( + const account = await getTokenAccountObj( governance!, tokenAccount, - mintAccounts, + mintAccounts ) if (account) { accounts.push(account) } } else if ( [...Object.values(AUXILIARY_TOKEN_ACCOUNTS).flatMap((x) => x)].find((x) => - x.accounts.includes(tokenAccount.publicKey.toBase58()), + x.accounts.includes(tokenAccount.publicKey.toBase58()) ) ) { const mint = mintAccounts.find( - (x) => x.publicKey.toBase58() === tokenAccount.account.mint.toBase58(), + (x) => x.publicKey.toBase58() === tokenAccount.account.mint.toBase58() ) - if (mint && !isToken2022(tokenAccount.account)) { - const account = new AccountTypeAuxiliaryToken( - tokenAccount as TokenProgramAccount, - mint, - ) + if (mint) { + const account = new AccountTypeAuxiliaryToken(tokenAccount, mint) if (account) { accounts.push(account) @@ -438,9 +427,11 @@ const getTokenAssetAccounts = async ( } const solAccounts = await getSolAccountsObj( + connection, + accounts, solAccountsInfo, mintAccounts, - governances, + governances ) return [...accounts, ...solAccounts] @@ -448,14 +439,14 @@ const getTokenAssetAccounts = async ( const getMintAccounts = ( mintGovernances: GovernanceProgramAccountWithNativeTreasuryAddress[], - mintGovernancesMintInfo: (MintInfo & { publicKey: PublicKey })[], + mintGovernancesMintInfo: (MintInfo & { publicKey: PublicKey })[] ) => { const accounts: AccountTypeMint[] = [] mintGovernancesMintInfo.forEach((mintAccountInfo, index) => { const mintGovernnace = mintGovernances[index] if (!mintAccountInfo) { throw new Error( - `Missing mintAccountInfo for: ${mintGovernnace?.pubkey.toBase58()}`, + `Missing mintAccountInfo for: ${mintGovernnace?.pubkey.toBase58()}` ) } const account = new AccountTypeMint(mintGovernnace!, mintAccountInfo) @@ -468,7 +459,7 @@ const getMintAccounts = ( const getProgramAssetAccounts = async ( connection: ConnectionContext, - governancesArray: GovernanceProgramAccountWithNativeTreasuryAddress[], + governancesArray: GovernanceProgramAccountWithNativeTreasuryAddress[] ): Promise => { const possibleOwnersPk = [ ...governancesArray.map((x) => x.nativeTreasuryAddress), @@ -476,7 +467,7 @@ const getProgramAssetAccounts = async ( .filter( (x) => x.account.accountType === GovernanceAccountType.ProgramGovernanceV1 || - x.account.accountType === GovernanceAccountType.ProgramGovernanceV2, + x.account.accountType === GovernanceAccountType.ProgramGovernanceV2 ) .map((x) => x.pubkey), ] @@ -489,31 +480,73 @@ const getProgramAssetAccounts = async ( governancesArray.find( (x) => x.pubkey.equals(program.owner) || - x.nativeTreasuryAddress.equals(program.owner), + x.nativeTreasuryAddress.equals(program.owner) )!, program.programId, - program.owner, - ), + program.owner + ) ) } const getGenericAssetAccounts = ( - genericGovernances: GovernanceProgramAccountWithNativeTreasuryAddress[], + genericGovernances: GovernanceProgramAccountWithNativeTreasuryAddress[] ): AccountTypeGeneric[] => { return genericGovernances.map( - (programGov) => new AccountTypeGeneric(programGov), + (programGov) => new AccountTypeGeneric(programGov) ) } -const getSolAccountObj = ( +const getSolAccountObj = async ( governance: GovernanceProgramAccountWithNativeTreasuryAddress, + connection: ConnectionContext, mint: TokenProgramAccount, - { acc, nativeSolAddress }: SolAccInfo, -): AccountTypeSol | null => { + accounts: AssetAccount[], + { acc, nativeSolAddress }: SolAccInfo +): Promise => { 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 = group(tokenAccountsOwnedBySolAccounts) + + const mintAccounts = ( + await Promise.all( + groups.map((group) => { + if (!group.length) { + return [] + } + + return getMintAccountsInfo( + connection, + group.map((x) => x.account.mint) + ) + }) + ) + ).flat() + + for (const acc of tokenAccountsOwnedBySolAccounts) { + const account = await getTokenAccountObj(governance, acc, mintAccounts) + + if (account) { + accounts.push(account) + } + } + // const minRentAmount = await connection.current.getMinimumBalanceForRentExemption( // 0 // ) @@ -542,7 +575,7 @@ const filterOutHiddenAccounts = (x: AssetAccount) => { // Return array without duplicates const uniqueGovernedTokenAccounts = ( - assetAccounts: AssetAccount[], + assetAccounts: AssetAccount[] ): AssetAccount[] => { const existing = new Set() const deduped: AssetAccount[] = [] @@ -559,7 +592,7 @@ const uniqueGovernedTokenAccounts = ( const getMintAccountsInfo = async ( { endpoint, current: { commitment } }: ConnectionContext, - publicKeys: PublicKey[], + publicKeys: PublicKey[] ): Promise[]> => { const { data: mintAccountsJson } = await axios.post( endpoint, @@ -578,14 +611,14 @@ const getMintAccountsInfo = async ( }, ], } - }), + }) ) if (!mintAccountsJson) { throw new Error( `Cannot load information about mint accounts ${publicKeys.map((x) => - x.toBase58(), - )}`, + x.toBase58() + )}` ) } @@ -602,26 +635,24 @@ const getMintAccountsInfo = async ( const data = Buffer.from(encodedData, 'base64') const account = parseMintAccountData(data) return { publicKey, account } - }, + } ) } const getTokenAccountsInfo = async ( { endpoint, current: { commitment } }: ConnectionContext, - publicKeys: PublicKey[], - programId: PublicKey, - encoding = 'base64', + publicKeys: PublicKey[] ): Promise[]> => { const { data: tokenAccountsInfoJson } = await axios.post< unknown, { data: { result: { - value: { - account: any - pubkey: string - }[] - } + account: { + data: [string, 'base64'] + } + pubkey: string + }[] }[] } >( @@ -629,75 +660,60 @@ const getTokenAccountsInfo = async ( publicKeys.map((publicKey) => ({ jsonrpc: '2.0', id: 1, - method: 'getTokenAccountsByOwner', + method: 'getProgramAccounts', params: [ - publicKey, - { programId: programId.toBase58() }, + TOKEN_PROGRAM_ID.toBase58(), { commitment, - encoding: encoding, + encoding: 'base64', + filters: [ + { + // number of bytes + dataSize: 165, + }, + { + 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(), - )}`, + x.toBase58() + )}` ) } - if (programId.equals(TOKEN_PROGRAM_ID)) { - return tokenAccountsInfoJson.reduce((tokenAccountsInfo, { result }) => { - result.value.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 tokenAccountsInfoJson.reduce((tokenAccountsInfo, { result }) => { + result.forEach( + ({ + account: { + data: [encodedData], }, - ) - - return tokenAccountsInfo - }, [] as TokenProgramAccount[]) - } else { - return tokenAccountsInfoJson.reduce((tokenAccountsInfo, { result }) => { - result.value.forEach(({ account, pubkey }) => { + pubkey, + }) => { const publicKey = new PublicKey(pubkey) - const parsed = account.data.parsed.info - const tokenAccount: TokenAccount = { - address: new PublicKey(pubkey), - mint: new PublicKey(parsed.mint), - owner: new PublicKey(parsed.owner), - amount: new u64(parsed.tokenAmount.amount), - delegate: null, - delegatedAmount: new u64(0), - isInitialized: true, - isFrozen: false, - isNative: false, - rentExemptReserve: null, - closeAuthority: null, - extensions: parsed.extensions, - isToken2022: true, - } - tokenAccountsInfo.push({ publicKey, account: tokenAccount }) - }) + const data = Buffer.from(encodedData, 'base64') + const account = parseTokenAccountData(publicKey, data) + tokenAccountsInfo.push({ publicKey, account }) + } + ) - return tokenAccountsInfo - }, [] as TokenProgramAccount[]) - } + return tokenAccountsInfo + }, [] as TokenProgramAccount[]) } const getSolAccountsInfo = async ( connection: ConnectionContext, - publicKeys: { governancePk: PublicKey; nativeSolAddress: PublicKey }[], + publicKeys: { governancePk: PublicKey; nativeSolAddress: PublicKey }[] ): Promise => { const { data: solAccountsJson } = await axios.post< unknown, @@ -723,7 +739,7 @@ const getSolAccountsInfo = async ( encoding: 'jsonParsed', }, ], - })), + })) ) if (!solAccountsJson.length) { @@ -746,31 +762,40 @@ const getSolAccountsInfo = async ( const loadMintGovernanceAccounts = async ( connection: ConnectionContext, governances: GovernanceProgramAccountWithNativeTreasuryAddress[], - possibleMintAccountPks: PublicKey[], + possibleMintAccountPks: PublicKey[] ) => { const nativeAccountAddresses = governances.map((x) => x.nativeTreasuryAddress) const possibleMintAccounts = await getMultipleAccountInfoChunked( connection.current, - possibleMintAccountPks, + possibleMintAccountPks ) - const mintGovernances: GovernanceProgramAccountWithNativeTreasuryAddress[] = - [] + const mintGovernances: GovernanceProgramAccountWithNativeTreasuryAddress[] = [] const mintAccounts: (MintInfo & { publicKey: PublicKey })[] = [] for (const index in possibleMintAccounts) { const possibleMintAccount = possibleMintAccounts[index] const pk = possibleMintAccountPks[index] if (possibleMintAccount) { + const metadataAddress = getMetadataAddress(pk) + const metadataAccountInfo = await connection.current.getAccountInfo(metadataAddress) + const metadataUpdateAuthority = metadataAccountInfo?.data ? + new PublicKey(metadataAccountInfo.data.slice(1,33)) : + null + const data = Buffer.from(possibleMintAccount.data) const parsedMintInfo = parseMintAccountData(data) as MintInfo const ownerGovernance = governances.find( (g) => parsedMintInfo?.mintAuthority && - g.pubkey.equals(parsedMintInfo.mintAuthority), + g.pubkey.equals(parsedMintInfo.mintAuthority) || + metadataUpdateAuthority && + g.pubkey.equals(metadataUpdateAuthority) ) const solAccountPk = nativeAccountAddresses.find( (x) => parsedMintInfo?.mintAuthority && - x.equals(parsedMintInfo.mintAuthority), + x.equals(parsedMintInfo.mintAuthority) || + metadataUpdateAuthority && + x.equals(metadataUpdateAuthority) ) if (ownerGovernance || solAccountPk) { mintGovernances.push( @@ -778,7 +803,7 @@ const loadMintGovernanceAccounts = async ( ? governances[ nativeAccountAddresses.findIndex((x) => x.equals(solAccountPk)) ] - : ownerGovernance!, + : ownerGovernance! ) mintAccounts.push({ ...parsedMintInfo, publicKey: pk }) } @@ -790,50 +815,36 @@ const loadMintGovernanceAccounts = async ( const loadGovernedTokenAccounts = async ( connection: ConnectionContext, realm: ProgramAccount, - governancesArray: GovernanceProgramAccountWithNativeTreasuryAddress[], + governancesArray: GovernanceProgramAccountWithNativeTreasuryAddress[] ): Promise => { console.log('loadGovernedTokenAccounts has been called') - const auxiliaryTokenAccounts: (typeof AUXILIARY_TOKEN_ACCOUNTS)[keyof typeof AUXILIARY_TOKEN_ACCOUNTS] = - AUXILIARY_TOKEN_ACCOUNTS[realm.account.name]?.length - ? AUXILIARY_TOKEN_ACCOUNTS[realm.account.name] - : [] + const auxiliaryTokenAccounts: typeof AUXILIARY_TOKEN_ACCOUNTS[keyof typeof AUXILIARY_TOKEN_ACCOUNTS] = AUXILIARY_TOKEN_ACCOUNTS[ + realm.account.name + ]?.length + ? AUXILIARY_TOKEN_ACCOUNTS[realm.account.name] + : [] - const tokenAccountOwners = uniquePublicKey([ + const tokenAccountsOwnedByGovernances = uniquePublicKey([ ...governancesArray.map((x) => x.nativeTreasuryAddress), + ...governancesArray.map((g) => g.pubkey), ...auxiliaryTokenAccounts.map((x) => new PublicKey(x.owner)), ]) const tokenAccountsInfo = ( await Promise.all( // Load infos in batch, cannot load 9999 accounts within one request - group(tokenAccountOwners, 100).map((group) => - getTokenAccountsInfo(connection, group, TOKEN_PROGRAM_ID), - ), - ) - ) - .flat() - .filter((x) => !x.account.amount.isZero()) - - const token2022AccountsInfo = ( - await Promise.all( - // Load infos in batch, cannot load 9999 accounts within one request - group(tokenAccountOwners, 100).map((group) => - getTokenAccountsInfo( - connection, - group, - TOKEN_2022_PROGRAM_ID, - 'jsonParsed', - ), - ), + group(tokenAccountsOwnedByGovernances, 100).map((group) => + getTokenAccountsInfo(connection, group) + ) ) ).flat() const governedTokenAccounts = ( await Promise.all( // Load infos in batch, cannot load 9999 accounts within one request - group([...tokenAccountsInfo, ...token2022AccountsInfo], 100).map( - (group) => getTokenAssetAccounts(group, governancesArray, connection), - ), + group(tokenAccountsInfo).map((group) => + getTokenAssetAccounts(group, governancesArray, connection) + ) ) ).flat() @@ -843,7 +854,7 @@ const loadGovernedTokenAccounts = async ( const loadStakeAccounts = async ( connection: ConnectionContext, - solAccounts: AssetAccount[], + solAccounts: AssetAccount[] ) => { const accountsNotYetStaked = await Promise.all( solAccounts.map((x) => @@ -860,8 +871,8 @@ const loadStakeAccounts = async ( bytes: x.extensions.transferAddress, }, }, - ]), - ), + ]) + ) ) const accountsStaked = await Promise.all( solAccounts.map((x) => @@ -878,14 +889,14 @@ const loadStakeAccounts = async ( bytes: x.extensions.transferAddress, }, }, - ]), - ), + ]) + ) ) const accountsNotYetStakedMapped = accountsNotYetStaked.flatMap((x, idx) => - x.map((stake) => ({ ...stake, governance: solAccounts[idx].governance })), + x.map((stake) => ({ ...stake, governance: solAccounts[idx].governance })) ) const accountsStakedMapped = accountsStaked.flatMap((x, idx) => - x.map((stake) => ({ ...stake, governance: solAccounts[idx].governance })), + x.map((stake) => ({ ...stake, governance: solAccounts[idx].governance })) ) return [ @@ -896,8 +907,8 @@ const loadStakeAccounts = async ( x.publicKey, StakeState.Inactive, null, - x.accountInfo.lamports / LAMPORTS_PER_SOL, - ), + x.accountInfo.lamports / LAMPORTS_PER_SOL + ) ), ...accountsStakedMapped.map( (x) => @@ -906,8 +917,8 @@ const loadStakeAccounts = async ( x.publicKey, StakeState.Active, PublicKey.decode(x.accountInfo.data.slice(124, 124 + 32)), - x.accountInfo.lamports / LAMPORTS_PER_SOL, - ), + x.accountInfo.lamports / LAMPORTS_PER_SOL + ) ), ] } @@ -915,24 +926,20 @@ const loadStakeAccounts = async ( const getAccountsForGovernances = async ( connection: ConnectionContext, realm: ProgramAccount, - governancesArray: ProgramAccount[], + governancesArray: ProgramAccount[] ): Promise< (AccountTypeMint | AccountTypeProgram | AssetAccount | AccountTypeGeneric)[] > => { - const nativeAddresses = [ - ...governancesArray.map((x) => { - const [signatoryRecordAddress] = PublicKey.findProgramAddressSync( - [Buffer.from('native-treasury'), x.pubkey.toBuffer()], - realm.owner, - ) - return signatoryRecordAddress - }), - ] + const nativeAddresses = await Promise.all([ + ...governancesArray.map((x) => + getNativeTreasuryAddress(realm.owner, x.pubkey) + ), + ]) const governancesWithNativeTreasuryAddress = governancesArray.map( (x, index) => ({ ...x, nativeTreasuryAddress: nativeAddresses[index], - }), + }) ) //due to long request for mint accounts that are owned by every governance //we fetch @@ -949,20 +956,23 @@ const getAccountsForGovernances = async ( // 1 - Load accounts related to program governances // 2 - Load token accounts behind any type of governance // 3 - Load accounts related to mint - const [programAccounts, governedTokenAccounts, mintAccounts] = - await Promise.all([ - getProgramAssetAccounts(connection, governancesWithNativeTreasuryAddress), - loadGovernedTokenAccounts( - connection, - realm, - governancesWithNativeTreasuryAddress, - ), - loadMintGovernanceAccounts( - connection, - governancesWithNativeTreasuryAddress, - possibleMintAccountPks, - ), - ]) + const [ + programAccounts, + governedTokenAccounts, + mintAccounts, + ] = await Promise.all([ + getProgramAssetAccounts(connection, governancesWithNativeTreasuryAddress), + loadGovernedTokenAccounts( + connection, + realm, + governancesWithNativeTreasuryAddress + ), + loadMintGovernanceAccounts( + connection, + governancesWithNativeTreasuryAddress, + possibleMintAccountPks + ), + ]) // 4 - Call to fetch token prices for every token account's mints await tokenPriceService.fetchTokenPrices( @@ -975,7 +985,7 @@ const getAccountsForGovernances = async ( ...mints, governedTokenAccount.extensions.mint.publicKey.toBase58(), ] - }, [] as string[]), + }, [] as string[]) ) const accounts = [ @@ -990,9 +1000,9 @@ const getAccountsForGovernances = async ( governancesWithNativeTreasuryAddress.filter( (governance) => !accounts.some((account) => - account.pubkey.equals(governance.account.governedAccount), - ), - ), + account.pubkey.equals(governance.account.governedAccount) + ) + ) ) return [...accounts, ...genericGovernances] @@ -1002,7 +1012,7 @@ const getAccountsForGovernances = async ( // Solution: I cant tell what this is doing. it should probably be murdered. const getProgramAccountInfo = async ( { endpoint, current }: ConnectionContext, - publicKeys: PublicKey[], + publicKeys: PublicKey[] ): Promise<{ owner: PublicKey; programId: PublicKey }[]> => { let result: { owner: PublicKey; programId: PublicKey }[] = [] try { @@ -1044,7 +1054,7 @@ const getProgramAccountInfo = async ( }, }, ], - })), + })) ) if (executableAccountInfoJson && executableAccountInfoJson.length) { const executableDataPks = executableAccountInfoJson.reduce( @@ -1059,7 +1069,7 @@ const getProgramAccountInfo = async ( return executableAccountInfo }, - [] as { owner: PublicKey; executableDataPk: PublicKey }[], + [] as { owner: PublicKey; executableDataPk: PublicKey }[] ) if (executableDataPks.length) { const { data: programAccountInfoJson } = await axios.post< @@ -1100,7 +1110,7 @@ const getProgramAccountInfo = async ( }, }, ], - })), + })) ) if (programAccountInfoJson && programAccountInfoJson.length) { const programDataPks = programAccountInfoJson.reduce( @@ -1112,7 +1122,7 @@ const getProgramAccountInfo = async ( return programAccountInfo }, - [] as { owner: PublicKey; programId: PublicKey }[], + [] as { owner: PublicKey; programId: PublicKey }[] ) result = programDataPks } diff --git a/utils/metaplex.ts b/utils/metaplex.ts index 0ad1667df..2a3699ff7 100644 --- a/utils/metaplex.ts +++ b/utils/metaplex.ts @@ -45,3 +45,13 @@ export const createIx_transferNft = async ( ix.keys[10].pubkey = payer return ix } + +export function getMetadataAddress(pk: PublicKey) { + const metadataProgramId = new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s") + + return PublicKey.findProgramAddressSync([ + Buffer.from("metadata"), + metadataProgramId.toBuffer(), + pk.toBuffer() + ], metadataProgramId)[0] +} \ No newline at end of file