From e8b4bd89b40f58137e19f8d8236f1a7c7d9c5461 Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Thu, 2 May 2024 03:01:37 -0400 Subject: [PATCH 1/6] chore(ui): rename algod functions (get -> fetch) --- ui/src/api/algod.ts | 14 +++++++------- ui/src/components/AddStakeModal.tsx | 4 ++-- ui/src/components/ValidatorRewards.tsx | 4 ++-- ui/src/providers/AuthAddressProvider.tsx | 4 ++-- ui/src/utils/contracts.ts | 6 +++--- ui/src/utils/development.ts | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ui/src/api/algod.ts b/ui/src/api/algod.ts index 90b50f7e..1db0dde4 100644 --- a/ui/src/api/algod.ts +++ b/ui/src/api/algod.ts @@ -17,7 +17,7 @@ const algodClient = algokit.getAlgoClient({ token: algodConfig.token, }) -export async function getAccountInformation( +export async function fetchAccountInformation( address: string, exclude: Exclude = 'none', ): Promise { @@ -25,16 +25,16 @@ export async function getAccountInformation( return accountInfo as AccountInformation } -export async function getAccountBalance( +export async function fetchAccountBalance( address: string, availableBalance = false, ): Promise { - const accountInfo = await getAccountInformation(address, 'all') + const accountInfo = await fetchAccountInformation(address, 'all') return availableBalance ? accountInfo.amount - accountInfo['min-balance'] : accountInfo.amount } -export async function getAsset(assetId: number): Promise { +export async function fetchAsset(assetId: number): Promise { const asset = await algodClient.getAssetByID(assetId).do() return asset as Asset } @@ -43,7 +43,7 @@ export async function fetchBalance(address: string | null): Promise getAsset(holding['asset-id'])) + const promises = batch.map((holding) => fetchAsset(holding['asset-id'])) const assets = await Promise.all(promises) const assetCreatorHoldings = assets.map((asset, index) => { return { diff --git a/ui/src/components/AddStakeModal.tsx b/ui/src/components/AddStakeModal.tsx index 6c52b7ed..6de07e05 100644 --- a/ui/src/components/AddStakeModal.tsx +++ b/ui/src/components/AddStakeModal.tsx @@ -9,7 +9,7 @@ import { useForm } from 'react-hook-form' import { toast } from 'sonner' import { useDebouncedCallback } from 'use-debounce' import { z } from 'zod' -import { getAccountInformation } from '@/api/algod' +import { fetchAccountInformation } from '@/api/algod' import { addStake, doesStakerNeedToPayMbr, @@ -81,7 +81,7 @@ export function AddStakeModal({ const accountInfoQuery = useQuery({ queryKey: ['account-info', activeAddress], - queryFn: () => getAccountInformation(activeAddress!), + queryFn: () => fetchAccountInformation(activeAddress!), enabled: !!activeAddress && !!validator, // wait until modal is open }) diff --git a/ui/src/components/ValidatorRewards.tsx b/ui/src/components/ValidatorRewards.tsx index 36e0445d..5b917291 100644 --- a/ui/src/components/ValidatorRewards.tsx +++ b/ui/src/components/ValidatorRewards.tsx @@ -1,6 +1,6 @@ import { Validator } from '@/interfaces/validator' import { useQuery } from '@tanstack/react-query' -import { getAccountBalance } from '@/api/algod' +import { fetchAccountBalance } from '@/api/algod' import { getApplicationAddress } from 'algosdk' import { AlgoDisplayAmount } from '@/components/AlgoDisplayAmount' @@ -14,7 +14,7 @@ async function fetchRewardBalances(validator: Validator) { try { let totalBalances = 0 for (const pool of validator.pools) { - const poolBal = await getAccountBalance(getApplicationAddress(pool.poolAppId), true) + const poolBal = await fetchAccountBalance(getApplicationAddress(pool.poolAppId), true) totalBalances += poolBal } // Truncate to nearest whole ALGO diff --git a/ui/src/providers/AuthAddressProvider.tsx b/ui/src/providers/AuthAddressProvider.tsx index d21e5879..adada5ba 100644 --- a/ui/src/providers/AuthAddressProvider.tsx +++ b/ui/src/providers/AuthAddressProvider.tsx @@ -1,6 +1,6 @@ import { useWallet } from '@txnlab/use-wallet-react' import * as React from 'react' -import { getAccountInformation } from '@/api/algod' +import { fetchAccountInformation } from '@/api/algod' interface IContext { authAddress: string | undefined @@ -26,7 +26,7 @@ export function AuthAddressProvider({ children }: { children: React.ReactNode }) React.useEffect(() => { const fetchAuthAddress = async () => { try { - const accountInfo = await getAccountInformation(activeAddress!, 'all') + const accountInfo = await fetchAccountInformation(activeAddress!, 'all') const authAddr = accountInfo['auth-addr'] setAuthAddress(authAddr) } catch (error) { diff --git a/ui/src/utils/contracts.ts b/ui/src/utils/contracts.ts index 02a006ca..2f9abdb4 100644 --- a/ui/src/utils/contracts.ts +++ b/ui/src/utils/contracts.ts @@ -1,7 +1,7 @@ import { AlgoAmount } from '@algorandfoundation/algokit-utils/types/amount' import { QueryClient } from '@tanstack/react-query' import algosdk from 'algosdk' -import { getAccountInformation } from '@/api/algod' +import { fetchAccountInformation } from '@/api/algod' import { fetchNfd, fetchNfdSearch } from '@/api/nfd' import { GatingType } from '@/constants/gating' import { AssetHolding } from '@/interfaces/algod' @@ -303,7 +303,7 @@ export async function fetchGatingAssets( if (entryGatingType === GatingType.CreatorAccount) { const creatorAddress = entryGatingAddress - const accountInfo = await getAccountInformation(creatorAddress) + const accountInfo = await fetchAccountInformation(creatorAddress) if (accountInfo['created-assets']) { const assetIds = accountInfo['created-assets'].map((asset) => asset.index) @@ -320,7 +320,7 @@ export async function fetchGatingAssets( const nfd = await fetchNfd(nfdAppId, { view: 'tiny' }) const addresses = nfd.caAlgo || [] - const promises = addresses.map((address) => getAccountInformation(address)) + const promises = addresses.map((address) => fetchAccountInformation(address)) const accountsInfo = await Promise.all(promises) const assetIds = accountsInfo .map((accountInfo) => accountInfo['created-assets']) diff --git a/ui/src/utils/development.ts b/ui/src/utils/development.ts index 36b658de..8874d932 100644 --- a/ui/src/utils/development.ts +++ b/ui/src/utils/development.ts @@ -4,7 +4,7 @@ import { QueryClient } from '@tanstack/react-query' import { useRouter } from '@tanstack/react-router' import algosdk from 'algosdk' import { toast } from 'sonner' -import { getAccountInformation, getAsset } from '@/api/algod' +import { fetchAccountInformation, fetchAsset } from '@/api/algod' import { epochBalanceUpdate } from '@/api/contracts' import { StakerPoolData } from '@/interfaces/staking' import { ToStringTypes } from '@/interfaces/utils' @@ -98,7 +98,7 @@ export async function sendRewardTokensToPool( try { const tokenId = validator.config.rewardTokenId - const asset = await getAsset(tokenId) + const asset = await fetchAsset(tokenId) const unitName = asset.params['unit-name'] toast.loading(`Sign to send ${rewardTokenAmount} ${unitName} tokens to pool`, { @@ -123,7 +123,7 @@ export async function sendRewardTokensToPool( atc.addTransaction({ txn: assetTxn, signer }) await atc.execute(algodClient, 4) - const poolAccountInfo = await getAccountInformation(poolAddress) + const poolAccountInfo = await fetchAccountInformation(poolAddress) const assetHolding = poolAccountInfo.assets?.find((a) => a['asset-id'] === tokenId) const balanceStr = formatAssetAmount( From d100e18204d487c4937e7de3ea93f40bc1add96a Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Thu, 2 May 2024 05:43:33 -0400 Subject: [PATCH 2/6] feat(ui): ParamsCache fetches suggestedParams and for app clients --- ui/src/api/clients.ts | 89 +++++++++++++++++++++++++ ui/src/api/contracts.ts | 129 +++++++++++------------------------- ui/src/utils/paramsCache.ts | 47 +++++++++++++ 3 files changed, 176 insertions(+), 89 deletions(-) create mode 100644 ui/src/api/clients.ts create mode 100644 ui/src/utils/paramsCache.ts diff --git a/ui/src/api/clients.ts b/ui/src/api/clients.ts new file mode 100644 index 00000000..72e7408d --- /dev/null +++ b/ui/src/api/clients.ts @@ -0,0 +1,89 @@ +import * as algokit from '@algorandfoundation/algokit-utils' +import { TransactionSignerAccount } from '@algorandfoundation/algokit-utils/types/account' +import algosdk from 'algosdk' +import { FEE_SINK } from '@/constants/accounts' +import { StakingPoolClient } from '@/contracts/StakingPoolClient' +import { ValidatorRegistryClient } from '@/contracts/ValidatorRegistryClient' +import { makeEmptyTransactionSigner } from '@/lib/makeEmptyTransactionSigner' +import { getRetiAppIdFromViteEnvironment } from '@/utils/env' +import { getAlgodConfigFromViteEnvironment } from '@/utils/network/getAlgoClientConfigs' +import { ParamsCache } from '@/utils/paramsCache' + +const algodConfig = getAlgodConfigFromViteEnvironment() +const algodClient = algokit.getAlgoClient({ + server: algodConfig.server, + port: algodConfig.port, + token: algodConfig.token, +}) + +const RETI_APP_ID = getRetiAppIdFromViteEnvironment() + +export async function getValidatorClient( + signer: algosdk.TransactionSigner, + activeAddress: string, +): Promise { + const params = await ParamsCache.getInstance().getSuggestedParams() + + return new ValidatorRegistryClient( + { + sender: { signer, addr: activeAddress }, + resolveBy: 'id', + id: RETI_APP_ID, + params, + }, + algodClient, + ) +} + +export async function getSimulateValidatorClient( + senderAddr: string = FEE_SINK, + authAddr?: string, +): Promise { + const params = await ParamsCache.getInstance().getSuggestedParams() + + return new ValidatorRegistryClient( + { + sender: { addr: senderAddr, signer: makeEmptyTransactionSigner(authAddr) }, + resolveBy: 'id', + id: RETI_APP_ID, + params, + }, + algodClient, + ) +} + +export async function getStakingPoolClient( + poolAppId: number | bigint, + signer: algosdk.TransactionSigner, + activeAddress: string, +): Promise { + const params = await ParamsCache.getInstance().getSuggestedParams() + + return new StakingPoolClient( + { + sender: { signer, addr: activeAddress } as TransactionSignerAccount, + resolveBy: 'id', + id: poolAppId, + params, + }, + algodClient, + ) +} + +export async function getSimulateStakingPoolClient( + poolAppId: number | bigint, + senderAddr: string = FEE_SINK, + authAddr?: string, +): Promise { + const params = await ParamsCache.getInstance().getSuggestedParams() + + return new StakingPoolClient( + { + sender: { addr: senderAddr, signer: makeEmptyTransactionSigner(authAddr) }, + resolveBy: 'id', + id: poolAppId, + params, + }, + algodClient, + ) +} diff --git a/ui/src/api/contracts.ts b/ui/src/api/contracts.ts index 8e9fc62a..b1109372 100644 --- a/ui/src/api/contracts.ts +++ b/ui/src/api/contracts.ts @@ -2,8 +2,14 @@ import * as algokit from '@algorandfoundation/algokit-utils' import { TransactionSignerAccount } from '@algorandfoundation/algokit-utils/types/account' import { AlgoAmount } from '@algorandfoundation/algokit-utils/types/amount' import algosdk from 'algosdk' +import { + getSimulateStakingPoolClient, + getSimulateValidatorClient, + getStakingPoolClient, + getValidatorClient, +} from '@/api/clients' import { fetchNfd } from '@/api/nfd' -import { ALGORAND_ZERO_ADDRESS_STRING, FEE_SINK } from '@/constants/accounts' +import { ALGORAND_ZERO_ADDRESS_STRING } from '@/constants/accounts' import { StakingPoolClient } from '@/contracts/StakingPoolClient' import { ValidatorRegistryClient } from '@/contracts/ValidatorRegistryClient' import { StakedInfo, StakerPoolData, StakerValidatorData } from '@/interfaces/staking' @@ -33,7 +39,6 @@ import { transformValidatorData, } from '@/utils/contracts' import { dayjs } from '@/utils/dayjs' -import { getRetiAppIdFromViteEnvironment } from '@/utils/env' import { getAlgodConfigFromViteEnvironment } from '@/utils/network/getAlgoClientConfigs' import { encodeCallParams } from '@/utils/tests/abi' @@ -44,60 +49,6 @@ const algodClient = algokit.getAlgoClient({ token: algodConfig.token, }) -const RETI_APP_ID = getRetiAppIdFromViteEnvironment() - -export const makeSimulateValidatorClient = (senderAddr: string = FEE_SINK, authAddr?: string) => { - return new ValidatorRegistryClient( - { - sender: { addr: senderAddr, signer: makeEmptyTransactionSigner(authAddr) }, - resolveBy: 'id', - id: RETI_APP_ID, - }, - algodClient, - ) -} - -export const makeValidatorClient = (signer: algosdk.TransactionSigner, activeAddress: string) => { - return new ValidatorRegistryClient( - { - sender: { signer, addr: activeAddress } as TransactionSignerAccount, - resolveBy: 'id', - id: RETI_APP_ID, - }, - algodClient, - ) -} - -export const makeSimulateStakingPoolClient = ( - poolAppId: number | bigint, - senderAddr: string = FEE_SINK, - authAddr?: string, -) => { - return new StakingPoolClient( - { - sender: { addr: senderAddr, signer: makeEmptyTransactionSigner(authAddr) }, - resolveBy: 'id', - id: poolAppId, - }, - algodClient, - ) -} - -export const makeStakingPoolClient = ( - poolAppId: number | bigint, - signer: algosdk.TransactionSigner, - activeAddress: string, -) => { - return new StakingPoolClient( - { - sender: { signer, addr: activeAddress } as TransactionSignerAccount, - resolveBy: 'id', - id: poolAppId, - }, - algodClient, - ) -} - export function callGetNumValidators(validatorClient: ValidatorRegistryClient) { return validatorClient .compose() @@ -130,7 +81,7 @@ export async function fetchValidator( client?: ValidatorRegistryClient, ) { try { - const validatorClient = client || makeSimulateValidatorClient() + const validatorClient = client || (await getSimulateValidatorClient()) const [config, state, validatorPoolData, poolTokenPayoutRatios, nodePoolAssignments] = await Promise.all([ @@ -180,7 +131,7 @@ export async function fetchValidator( export async function fetchValidators(client?: ValidatorRegistryClient) { try { - const validatorClient = client || makeSimulateValidatorClient() + const validatorClient = client || (await getSimulateValidatorClient()) // App call to fetch total number of validators const numValidatorsResponse = await callGetNumValidators(validatorClient) @@ -227,7 +178,7 @@ export async function addValidator( activeAddress: string, authAddr?: string, ) { - const validatorClient = makeValidatorClient(signer, activeAddress) + const validatorClient = await getValidatorClient(signer, activeAddress) const validatorAppRef = await validatorClient.appClient.getAppReference() @@ -284,7 +235,7 @@ export async function addValidator( sunsettingTo: Number(0), } - const simulateValidatorClient = makeSimulateValidatorClient(activeAddress, authAddr) + const simulateValidatorClient = await getSimulateValidatorClient(activeAddress, authAddr) const simulateResult = await simulateValidatorClient .compose() @@ -386,7 +337,7 @@ export async function fetchNodePoolAssignments( validatorId: string | number | bigint, ): Promise { try { - const validatorClient = makeSimulateValidatorClient() + const validatorClient = await getSimulateValidatorClient() const nodePoolAssignmentResponse = await callGetNodePoolAssignments( Number(validatorId), @@ -420,7 +371,7 @@ export function callGetTokenPayoutRatio( export async function fetchTokenPayoutRatio(validatorId: string | number | bigint) { try { - const validatorClient = makeSimulateValidatorClient() + const validatorClient = await getSimulateValidatorClient() const result = await callGetTokenPayoutRatio(Number(validatorId), validatorClient) @@ -440,7 +391,7 @@ export function callGetMbrAmounts(validatorClient: ValidatorRegistryClient) { export async function fetchMbrAmounts(client?: ValidatorRegistryClient): Promise { try { - const validatorClient = client || makeSimulateValidatorClient() + const validatorClient = client || (await getSimulateValidatorClient()) const mbrAmountsResponse = await callGetMbrAmounts(validatorClient) const [validatorMbr, poolMbr, poolInitMbr, stakerMbr] = mbrAmountsResponse.returns![0] @@ -464,7 +415,7 @@ export async function addStakingPool( signer: algosdk.TransactionSigner, activeAddress: string, ): Promise { - const validatorClient = makeValidatorClient(signer, activeAddress) + const validatorClient = await getValidatorClient(signer, activeAddress) const validatorAppRef = await validatorClient.appClient.getAppReference() const suggestedParams = await algodClient.getTransactionParams().do() @@ -526,7 +477,7 @@ export async function initStakingPoolStorage( suggestedParams, }) - const stakingPoolClient = makeStakingPoolClient(poolAppId, signer, activeAddress) + const stakingPoolClient = await getStakingPoolClient(poolAppId, signer, activeAddress) await stakingPoolClient .compose() @@ -549,7 +500,7 @@ export async function doesStakerNeedToPayMbr( authAddr?: string, client?: ValidatorRegistryClient, ): Promise { - const validatorClient = client || makeSimulateValidatorClient(activeAddress, authAddr) + const validatorClient = client || (await getSimulateValidatorClient(activeAddress, authAddr)) const result = await validatorClient .compose() @@ -571,7 +522,7 @@ export async function findPoolForStaker( authAddr?: string, client?: ValidatorRegistryClient, ): Promise { - const validatorClient = client || makeSimulateValidatorClient(activeAddress, authAddr) + const validatorClient = client || (await getSimulateValidatorClient(activeAddress, authAddr)) const result = await validatorClient .compose() @@ -616,7 +567,7 @@ export async function addStake( activeAddress: string, authAddr?: string, ): Promise { - const validatorClient = makeValidatorClient(signer, activeAddress) + const validatorClient = await getValidatorClient(signer, activeAddress) const validatorAppRef = await validatorClient.appClient.getAppReference() const suggestedParams = await algodClient.getTransactionParams().do() @@ -628,7 +579,7 @@ export async function addStake( suggestedParams, }) - const simulateValidatorClient = makeSimulateValidatorClient(activeAddress, authAddr) + const simulateValidatorClient = await getSimulateValidatorClient(activeAddress, authAddr) const simulateResults = await simulateValidatorClient .compose() @@ -695,7 +646,7 @@ export async function isNewStakerToValidator( staker: string, minEntryStake: number | bigint, ) { - const validatorClient = makeSimulateValidatorClient() + const validatorClient = await getSimulateValidatorClient() const result = await callFindPoolForStaker(validatorId, staker, minEntryStake, validatorClient) const [_, isNewStaker] = result.returns![0] @@ -715,7 +666,7 @@ export async function callGetStakedPoolsForAccount( export async function fetchStakedPoolsForAccount(staker: string): Promise { try { - const validatorClient = makeSimulateValidatorClient() + const validatorClient = await getSimulateValidatorClient() const result = await callGetStakedPoolsForAccount(staker, validatorClient) const stakedPools = result.returns![0] @@ -743,7 +694,7 @@ export async function fetchStakerPoolData( staker: string, ): Promise { try { - const stakingPoolClient = makeSimulateStakingPoolClient(poolKey.poolAppId) + const stakingPoolClient = await getSimulateStakingPoolClient(poolKey.poolAppId) const stakingPoolGS = await stakingPoolClient.appClient.getGlobalState() let lastPayoutTime = dayjs() @@ -846,7 +797,7 @@ export async function fetchProtocolConstraints( client?: ValidatorRegistryClient, ): Promise { try { - const validatorClient = client || makeSimulateValidatorClient() + const validatorClient = client || (await getSimulateValidatorClient()) const result = await callGetProtocolConstraints(validatorClient) @@ -890,7 +841,7 @@ export async function removeStake( activeAddress: string, authAddr?: string, ) { - const stakingPoolSimulateClient = makeSimulateStakingPoolClient( + const stakingPoolSimulateClient = await getSimulateStakingPoolClient( poolAppId, activeAddress, authAddr, @@ -916,7 +867,7 @@ export async function removeStake( ), ) - const stakingPoolClient = makeStakingPoolClient(poolAppId, signer, activeAddress) + const stakingPoolClient = await getStakingPoolClient(poolAppId, signer, activeAddress) await stakingPoolClient .compose() @@ -938,7 +889,7 @@ export async function epochBalanceUpdate( authAddr?: string, ): Promise { try { - const stakingPoolSimulateClient = makeSimulateStakingPoolClient( + const stakingPoolSimulateClient = await getSimulateStakingPoolClient( poolAppId, activeAddress, authAddr, @@ -956,7 +907,7 @@ export async function epochBalanceUpdate( 3000 + 1000 * ((simulateResult.simulateResponse.txnGroups[0].appBudgetAdded as number) / 700), ) - const stakingPoolClient = makeStakingPoolClient(poolAppId, signer, activeAddress) + const stakingPoolClient = await getStakingPoolClient(poolAppId, signer, activeAddress) await stakingPoolClient .compose() @@ -985,13 +936,13 @@ export async function fetchPoolInfo( client?: ValidatorRegistryClient, ): Promise { try { - const validatorClient = client || makeSimulateValidatorClient() + const validatorClient = client || (await getSimulateValidatorClient()) const result = await callGetPoolInfo(poolKey, validatorClient) const [poolAppId, totalStakers, totalAlgoStaked] = result.returns![0] - const stakingPoolClient = makeSimulateStakingPoolClient(poolAppId) + const stakingPoolClient = await getSimulateStakingPoolClient(poolAppId) const poolAppRef = await stakingPoolClient.appClient.getAppReference() const poolAddress = poolAppRef.appAddress @@ -1022,7 +973,7 @@ export async function fetchValidatorPools( client?: ValidatorRegistryClient, ): Promise { try { - const validatorClient = client || makeSimulateValidatorClient() + const validatorClient = client || (await getSimulateValidatorClient()) const result = await callGetPools(Number(validatorId), validatorClient) @@ -1031,7 +982,7 @@ export async function fetchValidatorPools( const poolAddresses: string[] = [] for (const poolInfo of poolsInfo) { - const stakingPoolClient = makeSimulateStakingPoolClient(poolInfo[0]) + const stakingPoolClient = await getSimulateStakingPoolClient(poolInfo[0]) const poolAppRef = await stakingPoolClient.appClient.getAppReference() const poolAddress = poolAppRef.appAddress poolAddresses.push(poolAddress) @@ -1058,7 +1009,7 @@ export async function claimTokens( const atc1 = new algosdk.AtomicTransactionComposer() for (const pool of pools) { - const client = makeSimulateStakingPoolClient(pool.poolAppId, activeAddress, authAddr) + const client = await getSimulateStakingPoolClient(pool.poolAppId, activeAddress, authAddr) await client.gas({}, { note: '1', sendParams: { atc: atc1, fee: AlgoAmount.MicroAlgos(0) } }) await client.gas({}, { note: '2', sendParams: { atc: atc1, fee: AlgoAmount.MicroAlgos(0) } }) await client.claimTokens({}, { sendParams: { atc: atc1, fee: AlgoAmount.MicroAlgos(240_000) } }) @@ -1084,7 +1035,7 @@ export async function claimTokens( const atc2 = new algosdk.AtomicTransactionComposer() for (const pool of pools) { - const client = makeStakingPoolClient(pool.poolAppId, signer, activeAddress) + const client = await getStakingPoolClient(pool.poolAppId, signer, activeAddress) await client.gas({}, { note: '1', sendParams: { atc: atc2, fee: AlgoAmount.MicroAlgos(0) } }) await client.gas({}, { note: '2', sendParams: { atc: atc2, fee: AlgoAmount.MicroAlgos(0) } }) await client.claimTokens({}, { sendParams: { atc: atc2, fee: feesAmount } }) @@ -1098,7 +1049,7 @@ export async function claimTokens( export async function fetchStakedInfoForPool(poolAppId: number): Promise { try { - const stakingPoolClient = makeSimulateStakingPoolClient(poolAppId) + const stakingPoolClient = await getSimulateStakingPoolClient(poolAppId) const boxValue = await stakingPoolClient.appClient.getBoxValue('stakers') const stakersInfo = chunkBytes(boxValue) @@ -1118,7 +1069,7 @@ export async function changeValidatorManager( signer: algosdk.TransactionSigner, activeAddress: string, ) { - const validatorClient = makeValidatorClient(signer, activeAddress) + const validatorClient = await getValidatorClient(signer, activeAddress) return validatorClient .compose() @@ -1133,7 +1084,7 @@ export async function changeValidatorSunsetInfo( signer: algosdk.TransactionSigner, activeAddress: string, ) { - const validatorClient = makeValidatorClient(signer, activeAddress) + const validatorClient = await getValidatorClient(signer, activeAddress) return validatorClient .compose() @@ -1148,7 +1099,7 @@ export async function changeValidatorNfd( signer: algosdk.TransactionSigner, activeAddress: string, ) { - const validatorClient = makeValidatorClient(signer, activeAddress) + const validatorClient = await getValidatorClient(signer, activeAddress) return validatorClient .compose() @@ -1162,7 +1113,7 @@ export async function changeValidatorCommissionAddress( signer: algosdk.TransactionSigner, activeAddress: string, ) { - const validatorClient = makeValidatorClient(signer, activeAddress) + const validatorClient = await getValidatorClient(signer, activeAddress) return validatorClient .compose() @@ -1180,7 +1131,7 @@ export async function changeValidatorRewardInfo( signer: algosdk.TransactionSigner, activeAddress: string, ) { - const validatorClient = makeValidatorClient(signer, activeAddress) + const validatorClient = await getValidatorClient(signer, activeAddress) return validatorClient .compose() diff --git a/ui/src/utils/paramsCache.ts b/ui/src/utils/paramsCache.ts new file mode 100644 index 00000000..90c4450b --- /dev/null +++ b/ui/src/utils/paramsCache.ts @@ -0,0 +1,47 @@ +import * as algokit from '@algorandfoundation/algokit-utils' +import algosdk from 'algosdk' +import { getAlgodConfigFromViteEnvironment } from '@/utils/network/getAlgoClientConfigs' + +const algodConfig = getAlgodConfigFromViteEnvironment() + +interface CachedParams { + suggestedParams: algosdk.SuggestedParams + timestamp: number +} + +export class ParamsCache { + private static instance: ParamsCache + private client: algosdk.Algodv2 + private cache: CachedParams | null = null + + private constructor() { + this.client = algokit.getAlgoClient({ + server: algodConfig.server, + port: algodConfig.port, + token: algodConfig.token, + }) + } + + public static getInstance(): ParamsCache { + if (!ParamsCache.instance) { + ParamsCache.instance = new ParamsCache() + } + return ParamsCache.instance + } + + public async getSuggestedParams(): Promise { + const now = Date.now() + const staleTime = 1000 * 60 * 5 // 5 minutes + + if (this.cache && now - this.cache.timestamp < staleTime) { + return this.cache.suggestedParams + } + + const suggestedParams = await this.client.getTransactionParams().do() + this.cache = { + suggestedParams, + timestamp: now, + } + return suggestedParams + } +} From 30e60a2ea7b07fdd470e294f6660c5b8ac0f8bb6 Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Thu, 2 May 2024 05:45:54 -0400 Subject: [PATCH 3/6] feat(ui): suggested params query for use in components --- ui/src/api/algod.ts | 5 +++++ ui/src/api/queries.ts | 8 +++++++- ui/src/routes/__root.tsx | 9 +++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/ui/src/api/algod.ts b/ui/src/api/algod.ts index 1db0dde4..98f57130 100644 --- a/ui/src/api/algod.ts +++ b/ui/src/api/algod.ts @@ -101,3 +101,8 @@ export async function fetchAssetCreatorHoldings( return allAssetCreatorHoldings } + +export async function fetchSuggestedParams() { + const suggestedParams = await algodClient.getTransactionParams().do() + return suggestedParams +} diff --git a/ui/src/api/queries.ts b/ui/src/api/queries.ts index d510b396..349fbaa6 100644 --- a/ui/src/api/queries.ts +++ b/ui/src/api/queries.ts @@ -1,5 +1,5 @@ import { queryOptions } from '@tanstack/react-query' -import { fetchAssetHoldings, fetchBalance } from '@/api/algod' +import { fetchAssetHoldings, fetchBalance, fetchSuggestedParams } from '@/api/algod' import { fetchMbrAmounts, fetchNodePoolAssignments, @@ -95,3 +95,9 @@ export const stakesQueryOptions = (staker: string | null) => retry: false, refetchInterval: 1000 * 60, // every minute }) + +export const suggestedParamsQueryOptions = queryOptions({ + queryKey: ['suggested-params'], + queryFn: () => fetchSuggestedParams(), + staleTime: 1000 * 60 * 5, // every 5 mins +}) diff --git a/ui/src/routes/__root.tsx b/ui/src/routes/__root.tsx index 1a819f52..7af3675c 100644 --- a/ui/src/routes/__root.tsx +++ b/ui/src/routes/__root.tsx @@ -2,12 +2,21 @@ import { QueryClient } from '@tanstack/react-query' import { ReactQueryDevtools } from '@tanstack/react-query-devtools' import { createRootRouteWithContext, Outlet } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/router-devtools' +import { suggestedParamsQueryOptions } from '@/api/queries' import { Layout } from '@/components/Layout' export const Route = createRootRouteWithContext<{ queryClient: QueryClient walletManager: { activeAddress: string | null } }>()({ + beforeLoad: () => { + return { + suggestedParamsQueryOptions, + } + }, + loader: ({ context: { queryClient, suggestedParamsQueryOptions } }) => { + queryClient.ensureQueryData(suggestedParamsQueryOptions) + }, component: () => ( <> From 962408c0dd4fe679d627255dfe511da5fae1b544 Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Fri, 3 May 2024 02:59:13 -0400 Subject: [PATCH 4/6] refactor(ui): manage ParamsCache singleton internally --- ui/src/api/clients.ts | 8 ++++---- ui/src/utils/paramsCache.ts | 11 ++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/ui/src/api/clients.ts b/ui/src/api/clients.ts index 72e7408d..d7e21f3f 100644 --- a/ui/src/api/clients.ts +++ b/ui/src/api/clients.ts @@ -22,7 +22,7 @@ export async function getValidatorClient( signer: algosdk.TransactionSigner, activeAddress: string, ): Promise { - const params = await ParamsCache.getInstance().getSuggestedParams() + const params = await ParamsCache.getSuggestedParams() return new ValidatorRegistryClient( { @@ -39,7 +39,7 @@ export async function getSimulateValidatorClient( senderAddr: string = FEE_SINK, authAddr?: string, ): Promise { - const params = await ParamsCache.getInstance().getSuggestedParams() + const params = await ParamsCache.getSuggestedParams() return new ValidatorRegistryClient( { @@ -57,7 +57,7 @@ export async function getStakingPoolClient( signer: algosdk.TransactionSigner, activeAddress: string, ): Promise { - const params = await ParamsCache.getInstance().getSuggestedParams() + const params = await ParamsCache.getSuggestedParams() return new StakingPoolClient( { @@ -75,7 +75,7 @@ export async function getSimulateStakingPoolClient( senderAddr: string = FEE_SINK, authAddr?: string, ): Promise { - const params = await ParamsCache.getInstance().getSuggestedParams() + const params = await ParamsCache.getSuggestedParams() return new StakingPoolClient( { diff --git a/ui/src/utils/paramsCache.ts b/ui/src/utils/paramsCache.ts index 90c4450b..c892fa20 100644 --- a/ui/src/utils/paramsCache.ts +++ b/ui/src/utils/paramsCache.ts @@ -10,7 +10,7 @@ interface CachedParams { } export class ParamsCache { - private static instance: ParamsCache + private static readonly instance: ParamsCache = new ParamsCache() private client: algosdk.Algodv2 private cache: CachedParams | null = null @@ -22,14 +22,11 @@ export class ParamsCache { }) } - public static getInstance(): ParamsCache { - if (!ParamsCache.instance) { - ParamsCache.instance = new ParamsCache() - } - return ParamsCache.instance + public static async getSuggestedParams(): Promise { + return this.instance.fetchAndCacheParams() } - public async getSuggestedParams(): Promise { + private async fetchAndCacheParams(): Promise { const now = Date.now() const staleTime = 1000 * 60 * 5 // 5 minutes From 53f1639190f44c4bf79b8e6d8b5d1164849c9e94 Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Fri, 3 May 2024 03:07:21 -0400 Subject: [PATCH 5/6] feat(ui): use ParamsCache for composed transactions --- ui/src/api/contracts.ts | 9 +++++---- ui/src/utils/development.ts | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ui/src/api/contracts.ts b/ui/src/api/contracts.ts index 383207d6..037e26b0 100644 --- a/ui/src/api/contracts.ts +++ b/ui/src/api/contracts.ts @@ -40,6 +40,7 @@ import { } from '@/utils/contracts' import { dayjs } from '@/utils/dayjs' import { getAlgodConfigFromViteEnvironment } from '@/utils/network/getAlgoClientConfigs' +import { ParamsCache } from '@/utils/paramsCache' import { encodeCallParams } from '@/utils/tests/abi' const algodConfig = getAlgodConfigFromViteEnvironment() @@ -197,7 +198,7 @@ export async function addValidator( .simulate({ allowEmptySignatures: true, allowUnnamedResources: true }) ).returns![0] - const suggestedParams = await algodClient.getTransactionParams().do() + const suggestedParams = await ParamsCache.getSuggestedParams() suggestedParams.flatFee = true suggestedParams.fee = AlgoAmount.Algos(10.001).microAlgos @@ -375,7 +376,7 @@ export async function addStakingPool( const validatorClient = await getValidatorClient(signer, activeAddress) const validatorAppRef = await validatorClient.appClient.getAppReference() - const suggestedParams = await algodClient.getTransactionParams().do() + const suggestedParams = await ParamsCache.getSuggestedParams() const payValidatorAddPoolMbr = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ from: activeAddress, @@ -423,7 +424,7 @@ export async function initStakingPoolStorage( signer: algosdk.TransactionSigner, activeAddress: string, ): Promise { - const suggestedParams = await algodClient.getTransactionParams().do() + const suggestedParams = await ParamsCache.getSuggestedParams() const mbrAmount = optInRewardToken ? poolInitMbr + AlgoAmount.Algos(0.1).microAlgos : poolInitMbr @@ -527,7 +528,7 @@ export async function addStake( const validatorClient = await getValidatorClient(signer, activeAddress) const validatorAppRef = await validatorClient.appClient.getAppReference() - const suggestedParams = await algodClient.getTransactionParams().do() + const suggestedParams = await ParamsCache.getSuggestedParams() const stakeTransferPayment = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ from: activeAddress, diff --git a/ui/src/utils/development.ts b/ui/src/utils/development.ts index 70551001..f3ced876 100644 --- a/ui/src/utils/development.ts +++ b/ui/src/utils/development.ts @@ -12,6 +12,7 @@ import { Validator, ValidatorConfig } from '@/interfaces/validator' import { convertToStringTypes } from '@/utils/convert' import { convertToBaseUnits, formatAssetAmount } from '@/utils/format' import { getAlgodConfigFromViteEnvironment } from '@/utils/network/getAlgoClientConfigs' +import { ParamsCache } from '@/utils/paramsCache' const algodConfig = getAlgodConfigFromViteEnvironment() const algodClient = algokit.getAlgoClient({ @@ -47,7 +48,7 @@ export async function simulateEpoch( ) const atc = new algosdk.AtomicTransactionComposer() - const suggestedParams = await algodClient.getTransactionParams().do() + const suggestedParams = await ParamsCache.getSuggestedParams() for (const pool of pools) { const poolKey = pool.poolKey @@ -110,7 +111,7 @@ export async function sendRewardTokensToPool( const poolAddress = algosdk.getApplicationAddress(poolAppId) const atc = new algosdk.AtomicTransactionComposer() - const suggestedParams = await algodClient.getTransactionParams().do() + const suggestedParams = await ParamsCache.getSuggestedParams() const assetTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({ from: activeAddress, From 4c396b42ece72194db2f736c57e28656d018e41f Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Fri, 3 May 2024 03:12:16 -0400 Subject: [PATCH 6/6] chore(ui): revert commit 30e60a2 There's no need for the Tanstack Query suggested params query; ParamsCache alone will suffice. --- ui/src/api/algod.ts | 5 ----- ui/src/api/queries.ts | 13 +------------ ui/src/routes/__root.tsx | 17 ++--------------- 3 files changed, 3 insertions(+), 32 deletions(-) diff --git a/ui/src/api/algod.ts b/ui/src/api/algod.ts index fe360e97..313d17d9 100644 --- a/ui/src/api/algod.ts +++ b/ui/src/api/algod.ts @@ -105,11 +105,6 @@ export async function fetchAssetCreatorHoldings( return allAssetCreatorHoldings } -export async function fetchSuggestedParams() { - const suggestedParams = await algodClient.getTransactionParams().do() - return suggestedParams -} - /** * Fetches the average time between blocks over a specified number of rounds. * @param {number} numRounds - The number of rounds to fetch. diff --git a/ui/src/api/queries.ts b/ui/src/api/queries.ts index d4e630ae..53667b04 100644 --- a/ui/src/api/queries.ts +++ b/ui/src/api/queries.ts @@ -1,10 +1,5 @@ import { queryOptions } from '@tanstack/react-query' -import { - fetchAssetHoldings, - fetchAverageBlockTime, - fetchBalance, - fetchSuggestedParams, -} from '@/api/algod' +import { fetchAssetHoldings, fetchAverageBlockTime, fetchBalance } from '@/api/algod' import { fetchMbrAmounts, fetchNodePoolAssignments, @@ -101,12 +96,6 @@ export const stakesQueryOptions = (staker: string | null) => refetchInterval: 1000 * 60, // every minute }) -export const suggestedParamsQueryOptions = queryOptions({ - queryKey: ['suggested-params'], - queryFn: () => fetchSuggestedParams(), - staleTime: 1000 * 60 * 5, // every 5 mins -}) - export const blockTimeQueryOptions = queryOptions({ queryKey: ['block-time'], queryFn: () => fetchAverageBlockTime(), diff --git a/ui/src/routes/__root.tsx b/ui/src/routes/__root.tsx index f91dcab1..aebb3677 100644 --- a/ui/src/routes/__root.tsx +++ b/ui/src/routes/__root.tsx @@ -2,11 +2,7 @@ import { QueryClient } from '@tanstack/react-query' import { ReactQueryDevtools } from '@tanstack/react-query-devtools' import { createRootRouteWithContext, Outlet } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/router-devtools' -import { - blockTimeQueryOptions, - constraintsQueryOptions, - suggestedParamsQueryOptions, -} from '@/api/queries' +import { blockTimeQueryOptions, constraintsQueryOptions } from '@/api/queries' import { Layout } from '@/components/Layout' export const Route = createRootRouteWithContext<{ @@ -17,20 +13,11 @@ export const Route = createRootRouteWithContext<{ return { blockTimeQueryOptions, constraintsQueryOptions, - suggestedParamsQueryOptions, } }, - loader: ({ - context: { - queryClient, - blockTimeQueryOptions, - constraintsQueryOptions, - suggestedParamsQueryOptions, - }, - }) => { + loader: ({ context: { queryClient, blockTimeQueryOptions, constraintsQueryOptions } }) => { queryClient.ensureQueryData(blockTimeQueryOptions) queryClient.ensureQueryData(constraintsQueryOptions) - queryClient.ensureQueryData(suggestedParamsQueryOptions) }, component: () => ( <>