From ee8698f07c08824e3900a84a9378f7c10fad2a30 Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Thu, 5 Dec 2024 01:02:37 -0500 Subject: [PATCH] refactor(ui): remove rekeyed account simulate workaround Remove custom `makeEmptyTransactionSigner` implementation and `AuthAddressProvider` since algosdk now correctly handles simulate calls from rekeyed accounts. This was originally added as a workaround in #89 but is no longer needed after algorand/go-algorand#5942. --- ui/src/api/clients.ts | 9 ++-- ui/src/api/contracts.ts | 45 ++++++-------------- ui/src/components/AddStakeModal.tsx | 14 +++---- ui/src/components/AddValidatorForm.tsx | 4 -- ui/src/components/ClaimTokens.tsx | 4 +- ui/src/components/StakingTable.tsx | 3 -- ui/src/components/UnstakeModal.tsx | 3 -- ui/src/components/ValidatorTable.tsx | 3 -- ui/src/lib/makeEmptyTransactionSigner.ts | 29 ------------- ui/src/main.tsx | 7 +--- ui/src/providers/AuthAddressProvider.tsx | 53 ------------------------ ui/src/utils/development.tsx | 6 +-- 12 files changed, 25 insertions(+), 155 deletions(-) delete mode 100644 ui/src/lib/makeEmptyTransactionSigner.ts delete mode 100644 ui/src/providers/AuthAddressProvider.tsx diff --git a/ui/src/api/clients.ts b/ui/src/api/clients.ts index 568e0607..bc6e4113 100644 --- a/ui/src/api/clients.ts +++ b/ui/src/api/clients.ts @@ -1,8 +1,7 @@ -import algosdk from 'algosdk' +import algosdk, { makeEmptyTransactionSigner } from 'algosdk' import { FEE_SINK } from '@/constants/accounts' import { StakingPoolClient, StakingPoolFactory } 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 { AlgorandClient } from '@algorandfoundation/algokit-utils' @@ -29,11 +28,10 @@ export async function getValidatorClient( export async function getSimulateValidatorClient( senderAddr: string = FEE_SINK, - authAddr?: string, ): Promise { return algorandClient.client.getTypedAppClientById(ValidatorRegistryClient, { defaultSender: senderAddr, - defaultSigner: makeEmptyTransactionSigner(authAddr), + defaultSigner: makeEmptyTransactionSigner(), appId: RETI_APP_ID, }) } @@ -53,11 +51,10 @@ export async function getStakingPoolClient( export async function getSimulateStakingPoolClient( poolAppId: bigint, senderAddr: string = FEE_SINK, - authAddr?: string, ): Promise { return algorandClient.client.getTypedAppClientById(StakingPoolClient, { defaultSender: senderAddr, - defaultSigner: makeEmptyTransactionSigner(authAddr), + defaultSigner: makeEmptyTransactionSigner(), appId: poolAppId, }) } diff --git a/ui/src/api/contracts.ts b/ui/src/api/contracts.ts index 237e1431..2b53b540 100644 --- a/ui/src/api/contracts.ts +++ b/ui/src/api/contracts.ts @@ -35,7 +35,6 @@ import { Validator, ValidatorConfigInput, } from '@/interfaces/validator' -import { makeEmptyTransactionSigner } from '@/lib/makeEmptyTransactionSigner' import { BalanceChecker } from '@/utils/balanceChecker' import { calculateValidatorPoolMetrics } from '@/utils/contracts' import { ParamsCache } from '@/utils/paramsCache' @@ -249,14 +248,13 @@ export async function addValidator( nfdAppId: bigint, signer: algosdk.TransactionSigner, activeAddress: string, - authAddr?: string, ) { const validatorClient = await getValidatorClient(signer, activeAddress) const { addValidatorMbr } = ( await validatorClient.send.getMbrAmounts({ args: {}, - signer: makeEmptyTransactionSigner(authAddr), + signer: algosdk.makeEmptyTransactionSigner(), }) ).return! @@ -435,7 +433,7 @@ export async function doesStakerNeedToPayMbr( authAddr?: string, client?: ValidatorRegistryClient, ): Promise { - const validatorClient = client || (await getSimulateValidatorClient(activeAddress, authAddr)) + const validatorClient = client || (await getSimulateValidatorClient(activeAddress)) const result = await validatorClient.send.doesStakerNeedToPayMbr({ args: { staker: activeAddress }, @@ -454,7 +452,7 @@ export async function findPoolForStaker( authAddr?: string, client?: ValidatorRegistryClient, ): Promise { - const validatorClient = client || (await getSimulateValidatorClient(activeAddress, authAddr)) + const validatorClient = client || (await getSimulateValidatorClient(activeAddress)) const result = await validatorClient .newGroup() @@ -495,7 +493,6 @@ export async function addStake( rewardTokenId: bigint, signer: algosdk.TransactionSigner, activeAddress: string, - authAddr?: string, ): Promise { const validatorClient = await getValidatorClient(signer, activeAddress) const suggestedParams = await ParamsCache.getSuggestedParams() @@ -507,7 +504,7 @@ export async function addStake( const needsOptInTxn = rewardTokenId > 0 && !(await isOptedInToAsset(activeAddress, rewardTokenId)) - const simulateValidatorClient = await getSimulateValidatorClient(activeAddress, authAddr) + const simulateValidatorClient = await getSimulateValidatorClient(activeAddress) const simulateComposer = simulateValidatorClient .newGroup() @@ -534,7 +531,7 @@ export async function addStake( suggestedParams, }) - simulateComposer.addTransaction(rewardTokenOptInTxn, makeEmptyTransactionSigner(authAddr)) + simulateComposer.addTransaction(rewardTokenOptInTxn, algosdk.makeEmptyTransactionSigner()) } const simulateResults = await simulateComposer.simulate({ @@ -762,15 +759,10 @@ export async function removeStake( rewardTokenId: bigint, signer: algosdk.TransactionSigner, activeAddress: string, - authAddr?: string, ) { const suggestedParams = await ParamsCache.getSuggestedParams() - const stakingPoolSimulateClient = await getSimulateStakingPoolClient( - poolAppId, - activeAddress, - authAddr, - ) + const stakingPoolSimulateClient = await getSimulateStakingPoolClient(poolAppId, activeAddress) const needsOptInTxn = rewardTokenId > 0 && !(await isOptedInToAsset(activeAddress, rewardTokenId)) @@ -792,7 +784,7 @@ export async function removeStake( suggestedParams, }) - simulateComposer.addTransaction(rewardTokenOptInTxn, makeEmptyTransactionSigner(authAddr)) + simulateComposer.addTransaction(rewardTokenOptInTxn, algosdk.makeEmptyTransactionSigner()) } const simulateResult = await simulateComposer.simulate({ @@ -845,14 +837,9 @@ export async function epochBalanceUpdate( poolAppId: bigint, signer: algosdk.TransactionSigner, activeAddress: string, - authAddr?: string, ): Promise { try { - const stakingPoolSimulateClient = await getSimulateStakingPoolClient( - poolAppId, - activeAddress, - authAddr, - ) + const stakingPoolSimulateClient = await getSimulateStakingPoolClient(poolAppId, activeAddress) const simulateResult = await stakingPoolSimulateClient .newGroup() @@ -860,24 +847,18 @@ export async function epochBalanceUpdate( args: [], note: '1', staticFee: AlgoAmount.MicroAlgos(0), - signer: makeEmptyTransactionSigner( - 'A7NMWS3NT3IUDMLVO26ULGXGIIOUQ3ND2TXSER6EBGRZNOBOUIQXHIBGDE', - ), + signer: algosdk.makeEmptyTransactionSigner(), }) .gas({ args: [], note: '2', staticFee: AlgoAmount.MicroAlgos(0), - signer: makeEmptyTransactionSigner( - 'A7NMWS3NT3IUDMLVO26ULGXGIIOUQ3ND2TXSER6EBGRZNOBOUIQXHIBGDE', - ), + signer: algosdk.makeEmptyTransactionSigner(), }) .epochBalanceUpdate({ args: {}, staticFee: AlgoAmount.MicroAlgos(240_000), - signer: makeEmptyTransactionSigner( - 'A7NMWS3NT3IUDMLVO26ULGXGIIOUQ3ND2TXSER6EBGRZNOBOUIQXHIBGDE', - ), + signer: algosdk.makeEmptyTransactionSigner(), }) .simulate({ allowEmptySignatures: true, allowUnnamedResources: true }) @@ -988,18 +969,16 @@ export async function claimTokens( pools: PoolInfo[], signer: algosdk.TransactionSigner, activeAddress: string, - authAddr?: string, ) { const [algorand, stakingFactory] = getStakingPoolFactory() const feeComposer = algorand.newGroup() - const simSigner = makeEmptyTransactionSigner(authAddr) for (const pool of pools) { const client = stakingFactory.getAppClientById({ appId: pool.poolAppId, defaultSender: activeAddress, - defaultSigner: simSigner, + defaultSigner: algosdk.makeEmptyTransactionSigner(), }) feeComposer .addAppCallMethodCall( diff --git a/ui/src/components/AddStakeModal.tsx b/ui/src/components/AddStakeModal.tsx index cc624ad4..692e2b2f 100644 --- a/ui/src/components/AddStakeModal.tsx +++ b/ui/src/components/AddStakeModal.tsx @@ -43,7 +43,6 @@ import { Input } from '@/components/ui/input' import { GatingType } from '@/constants/gating' import { StakerPoolData, StakerValidatorData } from '@/interfaces/staking' import { Validator } from '@/interfaces/validator' -import { useAuthAddress } from '@/providers/AuthAddressProvider' import { InsufficientBalanceError } from '@/utils/balanceChecker' import { calculateMaxAvailableToStake, @@ -82,7 +81,6 @@ export function AddStakeModal({ const queryClient = useQueryClient() const router = useRouter() const { transactionSigner, activeAddress } = useWallet() - const { authAddress, isReady } = useAuthAddress() const accountInfoQuery = useQuery({ queryKey: ['account-info', activeAddress], @@ -119,8 +117,8 @@ export function AddStakeModal({ // @todo: make this a custom hook, call from higher up and pass down as prop const mbrRequiredQuery = useQuery({ queryKey: ['mbr-required', activeAddress], - queryFn: () => doesStakerNeedToPayMbr(activeAddress!, authAddress), - enabled: !!activeAddress && isReady, + queryFn: () => doesStakerNeedToPayMbr(activeAddress!), + enabled: !!activeAddress, }) const mbrRequired = mbrRequiredQuery.data || false const mbrAmount = mbrRequired ? addStakerMbr : 0n @@ -227,7 +225,6 @@ export function AddStakeModal({ Number(validator.id), BigInt(amountToStake), activeAddress, - authAddress, ) setTargetPoolId(Number(poolKey.poolId)) // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -235,16 +232,16 @@ export function AddStakeModal({ console.error(`Error fetching target pool: ${error.message}`) } }, - [activeAddress, authAddress, minimumStake, validator], + [activeAddress, minimumStake, validator], ) React.useEffect(() => { - if (validator?.id && isReady) { + if (validator?.id) { fetchTargetPoolId() } else { setTargetPoolId(null) } - }, [fetchTargetPoolId, isReady, validator?.id]) + }, [fetchTargetPoolId, validator?.id]) const debouncedFetchTargetPoolId = useDebouncedCallback(async (value) => { const isValid = await form.trigger('amountToStake') @@ -309,7 +306,6 @@ export function AddStakeModal({ validator!.config.rewardTokenId, transactionSigner, activeAddress, - authAddress, ) toast.success( diff --git a/ui/src/components/AddValidatorForm.tsx b/ui/src/components/AddValidatorForm.tsx index fa8835cc..98120ee0 100644 --- a/ui/src/components/AddValidatorForm.tsx +++ b/ui/src/components/AddValidatorForm.tsx @@ -38,7 +38,6 @@ import { import { Separator } from '@/components/ui/separator' import { GatingType } from '@/constants/gating' import { useBlockTime } from '@/hooks/useBlockTime' -import { useAuthAddress } from '@/providers/AuthAddressProvider' import { InsufficientBalanceError } from '@/utils/balanceChecker' import { getEpochLengthBlocks, @@ -74,10 +73,8 @@ export function AddValidatorForm({ constraints }: AddValidatorFormProps) { const [isSigning, setIsSigning] = React.useState(false) const { transactionSigner, activeAddress } = useWallet() - const { authAddress } = useAuthAddress() const queryClient = useQueryClient() - const navigate = useNavigate({ from: '/add' }) const formSchema = z @@ -322,7 +319,6 @@ export function AddValidatorForm({ constraints }: AddValidatorFormProps) { nfdForInfoAppId, transactionSigner, activeAddress, - authAddress, ) toast.success( diff --git a/ui/src/components/ClaimTokens.tsx b/ui/src/components/ClaimTokens.tsx index 7efe2f64..ded3d6ba 100644 --- a/ui/src/components/ClaimTokens.tsx +++ b/ui/src/components/ClaimTokens.tsx @@ -6,7 +6,6 @@ import { toast } from 'sonner' import { claimTokens } from '@/api/contracts' import { DropdownMenuItem } from '@/components/ui/dropdown-menu' import { Validator } from '@/interfaces/validator' -import { useAuthAddress } from '@/providers/AuthAddressProvider' import { formatAssetAmount } from '@/utils/format' interface ClaimTokensProps { @@ -16,7 +15,6 @@ interface ClaimTokensProps { export function ClaimTokens({ validator, rewardTokenBalance }: ClaimTokensProps) { const { transactionSigner, activeAddress } = useWallet() - const { authAddress } = useAuthAddress() const queryClient = useQueryClient() const toastIdRef = React.useRef(`toast-${Date.now()}-${Math.random()}`) @@ -40,7 +38,7 @@ export function ClaimTokens({ validator, rewardTokenBalance }: ClaimTokensProps) toast.loading('Sign transactions to claim reward tokens...', { id: toastId }) - await claimTokens(validator.pools, transactionSigner, activeAddress, authAddress) + await claimTokens(validator.pools, transactionSigner, activeAddress) toast.success(
diff --git a/ui/src/components/StakingTable.tsx b/ui/src/components/StakingTable.tsx index 3f6fa5a1..cdd055c6 100644 --- a/ui/src/components/StakingTable.tsx +++ b/ui/src/components/StakingTable.tsx @@ -40,7 +40,6 @@ import { import { UnstakeModal } from '@/components/UnstakeModal' import { StakerValidatorData } from '@/interfaces/staking' import { Validator } from '@/interfaces/validator' -import { useAuthAddress } from '@/providers/AuthAddressProvider' import { calculateRewardEligibility, canManageValidator, @@ -78,7 +77,6 @@ export function StakingTable({ const [unstakeValidator, setUnstakeValidator] = React.useState(null) const { transactionSigner, activeAddress } = useWallet() - const { authAddress } = useAuthAddress() const router = useRouter() const queryClient = useQueryClient() @@ -276,7 +274,6 @@ export function StakingTable({ 100, transactionSigner, activeAddress, - authAddress, queryClient, router, ) diff --git a/ui/src/components/UnstakeModal.tsx b/ui/src/components/UnstakeModal.tsx index 40975b8f..1ebdfe02 100644 --- a/ui/src/components/UnstakeModal.tsx +++ b/ui/src/components/UnstakeModal.tsx @@ -37,7 +37,6 @@ import { } from '@/components/ui/select' import { StakerPoolData, StakerValidatorData } from '@/interfaces/staking' import { Validator } from '@/interfaces/validator' -import { useAuthAddress } from '@/providers/AuthAddressProvider' import { InsufficientBalanceError } from '@/utils/balanceChecker' import { setValidatorQueriesData } from '@/utils/contracts' import { formatAlgoAmount } from '@/utils/format' @@ -73,7 +72,6 @@ export function UnstakeModal({ validator, setValidator, stakesByValidator }: Uns const queryClient = useQueryClient() const router = useRouter() const { transactionSigner, activeAddress } = useWallet() - const { authAddress } = useAuthAddress() const formSchema = z.object({ amountToUnstake: z @@ -203,7 +201,6 @@ export function UnstakeModal({ validator, setValidator, stakesByValidator }: Uns validator!.config.rewardTokenId, transactionSigner, activeAddress, - authAddress, ) toast.success( diff --git a/ui/src/components/ValidatorTable.tsx b/ui/src/components/ValidatorTable.tsx index 9ecf28a2..029e291a 100644 --- a/ui/src/components/ValidatorTable.tsx +++ b/ui/src/components/ValidatorTable.tsx @@ -52,7 +52,6 @@ import { ValidatorRewards } from '@/components/ValidatorRewards' import { useLocalStorage } from '@/hooks/useLocalStorage' import { StakerValidatorData } from '@/interfaces/staking' import { Validator } from '@/interfaces/validator' -import { useAuthAddress } from '@/providers/AuthAddressProvider' import { calculateMaxStake, calculateSaturationPercentage, @@ -88,7 +87,6 @@ export function ValidatorTable({ const [addPoolValidator, setAddPoolValidator] = React.useState(null) const { transactionSigner, activeAddress } = useWallet() - const { authAddress } = useAuthAddress() const router = useRouter() const queryClient = useQueryClient() @@ -419,7 +417,6 @@ export function ValidatorTable({ 100, transactionSigner, activeAddress!, - authAddress, queryClient, router, ) diff --git a/ui/src/lib/makeEmptyTransactionSigner.ts b/ui/src/lib/makeEmptyTransactionSigner.ts deleted file mode 100644 index b14e03f0..00000000 --- a/ui/src/lib/makeEmptyTransactionSigner.ts +++ /dev/null @@ -1,29 +0,0 @@ -import algosdk from 'algosdk' - -/** - * This is a "polyfill" for algosdk's `makeEmptyTransactionSigner` function that supports simulate - * calls from rekeyed accounts. - * @see https://github.com/algorand/go-algorand/issues/5914 - * - * @param {string} authAddr - Optional authorized address (spending key) for a rekeyed account. - * @returns A function that can be used with simulate w/ the "allow empty signatures" option. - */ -export const makeEmptyTransactionSigner = (authAddr?: string): algosdk.TransactionSigner => { - return async (txns: algosdk.Transaction[], indexesToSign: number[]): Promise => { - const emptySigTxns: Uint8Array[] = [] - - indexesToSign.forEach((i) => { - const txn = txns[i] - const encodedStxn: Map = new Map([['txn', txn.toEncodingData()]]) - - // If authAddr is provided, use its decoded publicKey as the signer - if (authAddr) { - encodedStxn.set('sgnr', algosdk.decodeAddress(authAddr).publicKey) - } - - emptySigTxns.push(algosdk.msgpackRawEncode(encodedStxn)) - }) - - return emptySigTxns - } -} diff --git a/ui/src/main.tsx b/ui/src/main.tsx index 791d15a2..af2c17a1 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -14,7 +14,6 @@ import { HelmetProvider } from 'react-helmet-async' import ErrorBoundary from '@/components/ErrorBoundary' import { Toaster } from '@/components/ui/sonner' import { WalletShortcutHandler } from '@/components/WalletShortcutHandler' -import { AuthAddressProvider } from '@/providers/AuthAddressProvider' import { ThemeProvider } from '@/providers/ThemeProvider' import { routeTree } from '@/routeTree.gen' import '@/styles/main.css' @@ -105,10 +104,8 @@ function AppProviders() { - - - - + + diff --git a/ui/src/providers/AuthAddressProvider.tsx b/ui/src/providers/AuthAddressProvider.tsx deleted file mode 100644 index d1182aec..00000000 --- a/ui/src/providers/AuthAddressProvider.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { useWallet } from '@txnlab/use-wallet-react' -import * as React from 'react' -import { fetchAccountInformation } from '@/api/algod' - -interface IContext { - authAddress: string | undefined - isReady: boolean -} - -const AuthAddressContext = React.createContext({} as IContext) - -export const useAuthAddress = (): IContext => { - const context = React.useContext(AuthAddressContext) - if (!context) { - throw new Error('useAuthAddress must be used within a AuthAddressProvider') - } - return context -} - -export function AuthAddressProvider({ children }: { children: React.ReactNode }): JSX.Element { - const [authAddress, setAuthAddress] = React.useState(undefined) - const [isReady, setIsReady] = React.useState(false) - - const { activeAddress } = useWallet() - - React.useEffect(() => { - const fetchAuthAddress = async () => { - try { - const accountInfo = await fetchAccountInformation(activeAddress!, 'all') - const authAddr = accountInfo.authAddr - setAuthAddress(authAddr?.toString()) - } catch (error) { - console.error(`Error fetching active wallet's authorized address:`, error) - setAuthAddress(undefined) - } finally { - setIsReady(true) - } - } - - if (activeAddress) { - setIsReady(false) - fetchAuthAddress() - } else { - setAuthAddress(undefined) - } - }, [activeAddress]) - - return ( - - {children} - - ) -} diff --git a/ui/src/utils/development.tsx b/ui/src/utils/development.tsx index 99487c59..b39d58a8 100644 --- a/ui/src/utils/development.tsx +++ b/ui/src/utils/development.tsx @@ -97,7 +97,6 @@ export async function triggerPoolPayouts( pools: StakerPoolData[], signer: algosdk.TransactionSigner, activeAddress: string, - authAddr: string | undefined, ) { if (process.env.NODE_ENV !== 'development') { throw new Error('Triggering pool payouts is only available in development mode') @@ -118,7 +117,7 @@ export async function triggerPoolPayouts( const isLastPool = i === pools.length - 1 - const promiseFunction = epochBalanceUpdate(poolAppId, signer, activeAddress, authAddr) + const promiseFunction = epochBalanceUpdate(poolAppId, signer, activeAddress) const [nextItemPromise, resolveNextItem] = createNextItemPromise() @@ -156,7 +155,6 @@ export async function simulateEpoch( rewardAmount: number, signer: algosdk.TransactionSigner, activeAddress: string, - authAddr: string | undefined, queryClient: QueryClient, router: ReturnType, ) { @@ -227,7 +225,7 @@ export async function simulateEpoch( await wait(3000) // Trigger payouts by calling epochBalanceUpdate, starting with first pool (will iterate through all pools) - await triggerPoolPayouts(pools, signer, activeAddress, authAddr) + await triggerPoolPayouts(pools, signer, activeAddress) queryClient.invalidateQueries({ queryKey: ['stakes', { staker: activeAddress }] }) router.invalidate()