Skip to content

Commit

Permalink
Update transaction handling to use AA hook
Browse files Browse the repository at this point in the history
  • Loading branch information
piekczyk committed Nov 15, 2024
1 parent 6e6cb62 commit a58db69
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const VaultManageViewComponent = ({
viewWalletAddress: string
}) => {
const user = useUser()
const { publicClient, transactionClient, tokenBalance, tokenBalanceLoading } = useClient({
const { tokenBalance, tokenBalanceLoading } = useClient({
vault,
})
const { amountParsed, amountDisplay, manualSetAmount, handleAmountChange, onFocus, onBlur } =
Expand All @@ -70,8 +70,6 @@ export const VaultManageViewComponent = ({
setTransactionType,
} = useTransaction({
vault,
publicClient,
transactionClient,
amountParsed,
manualSetAmount,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,13 @@ export const VaultOpenViewComponent = ({
const { getStorageOnce } = useLocalStorageOnce<string>({
key: `${vault.id}-amount`,
})
const { publicClient, transactionClient, tokenBalance, tokenBalanceLoading } = useClient({
const { tokenBalance, tokenBalanceLoading } = useClient({
vault,
})
const { amountParsed, manualSetAmount, amountDisplay, handleAmountChange, onBlur, onFocus } =
useAmount({ vault })
const { sidebar, txHashes, removeTxHash, vaultChainId, reset } = useTransaction({
vault,
publicClient,
transactionClient,
amountParsed,
manualSetAmount,
})
Expand Down
6 changes: 6 additions & 0 deletions apps/earn-protocol/constants/networks-list.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SDKChainId } from '@summerfi/app-types'
import { keyBy } from 'lodash-es'

import { clientId } from '@/helpers/client-id'
Expand Down Expand Up @@ -98,6 +99,11 @@ export const optimismGoerliRpc = getRpc(NetworkNames.optimismGoerli)
export const baseMainnetRpc = getRpc(NetworkNames.baseMainnet)
export const baseGoerliRpc = getRpc(NetworkNames.baseGoerli)

export const SDKChainIdToRpcGatewayMap = {
[SDKChainId.ARBITRUM]: arbitrumMainnetRpc,
[SDKChainId.BASE]: baseMainnetRpc,
}

const mainnetConfig: NetworkConfig = {
name: NetworkNames.ethereumMainnet,
hexId: NetworkHexIds.MAINNET,
Expand Down
80 changes: 21 additions & 59 deletions apps/earn-protocol/hooks/use-client.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,29 @@
import { useEffect, useMemo, useState } from 'react'
import { useChain, useUser } from '@account-kit/react'
import { type SDKVaultishType } from '@summerfi/app-types'
import { subgraphNetworkToSDKId, ten } from '@summerfi/app-utils'
import { useUser } from '@account-kit/react'
import { type SDKChainId, type SDKVaultishType } from '@summerfi/app-types'
import { ten } from '@summerfi/app-utils'
import BigNumber from 'bignumber.js'
import { createPublicClient, createWalletClient, custom, erc20Abi } from 'viem'
import { createPublicClient, erc20Abi, http } from 'viem'

import { useClientChainId } from '@/hooks/use-client-chain-id'
import { SDKChainIdToRpcGatewayMap } from '@/constants/networks-list'
import { useUpdateAANetwork } from '@/hooks/use-update-aa-network'

export const useClient = ({ vault }: { vault?: SDKVaultishType }) => {
const user = useUser()
const { chain: connectedChain } = useChain()
const [tokenBalance, setTokenBalance] = useState<BigNumber>()
const [tokenBalanceLoading, setTokenBalanceLoading] = useState(true)
const { clientChainId } = useClientChainId()
const transactionClient = useMemo(() => {
// used for the tx itself
if (user) {
// todo: handle other wallets, this is just working with metamask
if (user.type === 'eoa' && window.ethereum) {
const externalProvider = window.ethereum

return createWalletClient({
chain: connectedChain,
transport: custom(externalProvider),
account: user.address,
})
}
}

return null
}, [user, connectedChain])

const publicClient = useMemo(
// used for the tx receipt
() => {
if (user) {
// todo: handle other wallets, this is just working with metamask
if (user.type === 'eoa' && window.ethereum) {
const externalProvider = window.ethereum

return createPublicClient({
chain: connectedChain,
transport: custom(externalProvider),
})
}
}

return null
},
[connectedChain, user],
)

const vaultChainId = vault ? subgraphNetworkToSDKId(vault.protocol.network) : null
const { appChain } = useUpdateAANetwork()

const isProperChainSelected = clientChainId === vaultChainId
// Client for read-only data fetching using our rpcGateway
const publicClient = useMemo(() => {
return createPublicClient({
chain: appChain,
transport: http(
SDKChainIdToRpcGatewayMap[appChain.id as SDKChainId.ARBITRUM | SDKChainId.BASE],
),
})
}, [appChain])

useEffect(() => {
const inputTokenAddress = vault?.inputToken.id
Expand All @@ -63,8 +33,8 @@ export const useClient = ({ vault }: { vault?: SDKVaultishType }) => {
setTokenBalance(undefined)
}

if (user?.address && publicClient) {
if (inputTokenAddress && inputTokenDecimals && !tokenBalance && isProperChainSelected) {
if (user?.address) {
if (inputTokenAddress && inputTokenDecimals && !tokenBalance) {
setTokenBalanceLoading(true)
publicClient
.readContract({
Expand All @@ -84,22 +54,14 @@ export const useClient = ({ vault }: { vault?: SDKVaultishType }) => {
// eslint-disable-next-line no-console
console.error('Error reading token balance', err)
})
} else {
setTokenBalanceLoading(false)
}
setTokenBalanceLoading(false)
}
}, [
publicClient,
tokenBalance,
user,
tokenBalanceLoading,
vault?.inputToken.id,
vault?.inputToken.decimals,
isProperChainSelected,
])
}, [publicClient, tokenBalance, user, vault?.inputToken.id, vault?.inputToken.decimals])

return {
publicClient,
transactionClient,
tokenBalance,
tokenBalanceLoading,
}
Expand Down
92 changes: 57 additions & 35 deletions apps/earn-protocol/hooks/use-transaction.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
'use client'

import { useCallback, useEffect, useMemo, useState } from 'react'
import { useAuthModal, useChain, useUser } from '@account-kit/react'
import {
useAuthModal,
useChain,
useSendUserOperation,
useSmartAccountClient,
useUser,
} from '@account-kit/react'
import {
type EarnTransactionTypes,
type EarnTransactionViewStates,
Expand All @@ -17,15 +23,12 @@ import { useRouter } from 'next/navigation'
import { SDKChainIdToAAChainMap } from '@/account-kit/config'
import { TransactionAction } from '@/constants/transaction-actions'
import { useAppSDK } from '@/hooks/use-app-sdk'
import { type useClient } from '@/hooks/use-client'
import { useClientChainId } from '@/hooks/use-client-chain-id'

type UseTransactionParams = {
vault: SDKVaultishType
amountParsed: BigNumber | undefined
manualSetAmount: (amountParsed: string | undefined) => void
transactionClient: ReturnType<typeof useClient>['transactionClient']
publicClient: ReturnType<typeof useClient>['publicClient']
}

const labelTransactions = (transactions: TransactionInfo[]) => {
Expand All @@ -36,13 +39,7 @@ const labelTransactions = (transactions: TransactionInfo[]) => {
}))
}

export const useTransaction = ({
vault,
manualSetAmount,
transactionClient,
publicClient,
amountParsed,
}: UseTransactionParams) => {
export const useTransaction = ({ vault, manualSetAmount, amountParsed }: UseTransactionParams) => {
const [transactionType, setTransactionType] = useState<TransactionAction>(
TransactionAction.DEPOSIT,
)
Expand All @@ -58,6 +55,8 @@ export const useTransaction = ({
const { setChain, isSettingChain } = useChain()
const { clientChainId } = useClientChainId()

const { client: smartAccountClient } = useSmartAccountClient({ type: 'LightAccount' })

const reset = useCallback(() => {
manualSetAmount(undefined)
setTransactions(undefined)
Expand All @@ -82,44 +81,67 @@ export const useTransaction = ({
return transactions[0]
}, [transactions])

const executeNextTransaction = useCallback(async () => {
setTxStatus('txInProgress')

if (!transactionClient || !publicClient) {
throw new Error('Error executing the transaction, no public or transaction client')
}

if (!nextTransaction) {
throw new Error('No transaction to execute')
}
try {
const transactionHash = await transactionClient.sendTransaction({
to: nextTransaction.transaction.target.value,
data: nextTransaction.transaction.calldata,
})

await publicClient.waitForTransactionReceipt({
hash: transactionHash,
confirmations: 5,
})
// Configure User Operation (transaction) sender, passing client which can be undefined
const { sendUserOperation } = useSendUserOperation({
client: smartAccountClient,
waitForTxn: true,
onSuccess: ({ hash }) => {
// await publicClient.waitForTransactionReceipt({
// hash,
// confirmations: 5,
// })

setTxHashes((prev) => [
...prev,
{
type: nextTransaction.label,
hash: transactionHash,
type: nextTransaction!.label,
hash,
},
])
setTxStatus('txSuccess')
setTransactions(transactions?.slice(1))
} catch (err) {
},
onError: (err) => {
// eslint-disable-next-line no-console
console.error('Error executing the transaction:', err)

if (err instanceof Error) {
setError(err.message)
} else {
setError('Error executing the transaction')
}
},
})

const sendTransaction = ({
target,
data,
value = 0n,
}: {
target: `0x${string}`
data: `0x${string}`
value?: bigint
}) => {
sendUserOperation({
uo: {
target,
data,
value,
},
})
}

const executeNextTransaction = useCallback(async () => {
setTxStatus('txInProgress')

if (!nextTransaction) {
throw new Error('No transaction to execute')
}
}, [nextTransaction, transactionClient, publicClient, transactions])
sendTransaction({
target: nextTransaction.transaction.target.value,
data: nextTransaction.transaction.calldata,
})
}, [nextTransaction, transactions])

const getTransactionsList = useCallback(async () => {
if (amountParsed && user) {
Expand Down
41 changes: 41 additions & 0 deletions apps/earn-protocol/hooks/use-update-aa-network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client'
import { useEffect } from 'react'
import { arbitrum, base } from '@account-kit/infra'
import { useChain } from '@account-kit/react'
import { humanNetworktoSDKNetwork, subgraphNetworkToId } from '@summerfi/app-utils'
import { useParams } from 'next/navigation'

import { NetworkIds } from '@/constants/networks-list'
import { useClientChainId } from '@/hooks/use-client-chain-id'

export const networkIdsToAccountKitChainsMap = {
[NetworkIds.BASEMAINNET]: base,
[NetworkIds.ARBITRUMMAINNET]: arbitrum,
}

// Update account kit network based on app network derived from currently displayed strategy
// To avoid forced EOA popups to change network, trigger it only when EOA wallet network and app network are already aligned
export const useUpdateAANetwork = () => {
const { clientChainId } = useClientChainId()
const params = useParams()
const { network } = params

const { setChain } = useChain()

const sdkNetwork = humanNetworktoSDKNetwork(network as string)
const appChainId = subgraphNetworkToId(sdkNetwork) as
| NetworkIds.BASEMAINNET
| NetworkIds.ARBITRUMMAINNET

const appChain = networkIdsToAccountKitChainsMap[appChainId]

useEffect(() => {
if (clientChainId === appChainId) {
setChain({ chain: appChain })
// eslint-disable-next-line no-console
console.info(`Updated app network to: ${appChain.id}`)
}
}, [appChain, setChain, clientChainId, appChainId])

return { clientChainId, appChainId, appChain }
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const formatDateDifference = ({ from, to }: { from: Date; to: Date }): st
const seconds = Math.floor((to.getTime() - from.getTime()) / 1000)

if (seconds < 60) {
return `${seconds} second${seconds !== 1 ? 's' : ''}`
return `just now`
}

const minutes = Math.floor(seconds / 60)
Expand Down

0 comments on commit a58db69

Please sign in to comment.