1
- import { useMemo , useState } from "react" ;
2
- import {
3
- Address ,
4
- AddressWithAssetAndAmount ,
5
- ChainRegistryEntry ,
6
- GasConfig ,
7
- IbcTransferStage ,
8
- TransferStep ,
9
- TransferTransactionData ,
10
- } from "types" ;
11
-
12
- type useIbcTransactionProps = {
13
- sourceAddress ?: string ;
14
- registry ?: ChainRegistryEntry ;
15
- sourceChannel ?: string ;
16
- shielded ?: boolean ;
17
- destinationChannel ?: Address ;
18
- selectedAsset ?: AddressWithAssetAndAmount ;
19
- } ;
20
-
1
+ import { SigningStargateClient } from "@cosmjs/stargate" ;
21
2
import { QueryStatus } from "@tanstack/query-core" ;
3
+ import { useQuery , UseQueryResult } from "@tanstack/react-query" ;
22
4
import { TokenCurrency } from "App/Common/TokenCurrency" ;
23
5
import {
24
6
broadcastIbcTransactionAtom ,
@@ -32,13 +14,35 @@ import {
32
14
dispatchToastNotificationAtom ,
33
15
} from "atoms/notifications" ;
34
16
import BigNumber from "bignumber.js" ;
35
- import { getIbcGasConfig } from "integrations/utils" ;
36
17
import invariant from "invariant" ;
37
18
import { useAtomValue , useSetAtom } from "jotai" ;
38
- import { createTransferDataFromIbc } from "lib/transactions" ;
19
+ import {
20
+ createIbcTransferMessage ,
21
+ createTransferDataFromIbc ,
22
+ } from "lib/transactions" ;
23
+ import { useState } from "react" ;
24
+ import {
25
+ Address ,
26
+ AddressWithAssetAndAmount ,
27
+ ChainRegistryEntry ,
28
+ GasConfig ,
29
+ IbcTransferStage ,
30
+ TransferStep ,
31
+ TransferTransactionData ,
32
+ } from "types" ;
39
33
import { toBaseAmount } from "utils" ;
40
34
import { sanitizeAddress } from "utils/address" ;
41
35
import { getKeplrWallet , sanitizeChannel } from "utils/ibc" ;
36
+ import { useSimulateIbcTransferFee } from "./useSimulateIbcTransferFee" ;
37
+
38
+ type useIbcTransactionProps = {
39
+ sourceAddress ?: string ;
40
+ registry ?: ChainRegistryEntry ;
41
+ sourceChannel ?: string ;
42
+ shielded ?: boolean ;
43
+ destinationChannel ?: Address ;
44
+ selectedAsset ?: AddressWithAssetAndAmount ;
45
+ } ;
42
46
43
47
type useIbcTransactionOutput = {
44
48
transferToNamada : (
@@ -48,7 +52,7 @@ type useIbcTransactionOutput = {
48
52
onUpdateStatus ?: ( status : string ) => void
49
53
) => Promise < TransferTransactionData > ;
50
54
transferStatus : "idle" | QueryStatus ;
51
- gasConfig : GasConfig | undefined ;
55
+ gasConfig : UseQueryResult < GasConfig > ;
52
56
} ;
53
57
54
58
export const useIbcTransaction = ( {
@@ -62,6 +66,37 @@ export const useIbcTransaction = ({
62
66
const broadcastIbcTx = useAtomValue ( broadcastIbcTransactionAtom ) ;
63
67
const dispatchNotification = useSetAtom ( dispatchToastNotificationAtom ) ;
64
68
const [ txHash , setTxHash ] = useState < string | undefined > ( ) ;
69
+ const [ rpcUrl , setRpcUrl ] = useState < string | undefined > ( ) ;
70
+ const [ stargateClient , setStargateClient ] = useState <
71
+ SigningStargateClient | undefined
72
+ > ( ) ;
73
+
74
+ // Avoid the same client being created twice for the same chain and provide
75
+ // a way to refetch the query in case it throws an error trying to connect to the RPC
76
+ useQuery ( {
77
+ queryKey : [ "store-stargate-client" , registry ?. chain . chain_id ] ,
78
+ enabled : Boolean ( registry ?. chain ) ,
79
+ queryFn : async ( ) => {
80
+ invariant ( registry ?. chain , "Error: Invalid chain" ) ;
81
+ setRpcUrl ( undefined ) ;
82
+ setStargateClient ( undefined ) ;
83
+ return await queryAndStoreRpc ( registry . chain , async ( rpc : string ) => {
84
+ const client = await createStargateClient ( rpc , registry . chain ) ;
85
+ setStargateClient ( client ) ;
86
+ setRpcUrl ( rpc ) ;
87
+ return client ;
88
+ } ) ;
89
+ } ,
90
+ } ) ;
91
+
92
+ const gasConfigQuery = useSimulateIbcTransferFee ( {
93
+ stargateClient,
94
+ registry,
95
+ selectedAsset,
96
+ isShieldedTransfer : shielded ,
97
+ sourceAddress,
98
+ channel : sourceChannel ,
99
+ } ) ;
65
100
66
101
const dispatchPendingTxNotification = ( tx : TransferTransactionData ) : void => {
67
102
invariant ( tx . hash , "Error: Transaction hash not provided" ) ;
@@ -90,13 +125,6 @@ export const useIbcTransaction = ({
90
125
} ) ;
91
126
} ;
92
127
93
- const gasConfig = useMemo ( ( ) => {
94
- if ( typeof registry !== "undefined" ) {
95
- return getIbcGasConfig ( registry ) ;
96
- }
97
- return undefined ;
98
- } , [ registry ] ) ;
99
-
100
128
const getIbcTransferStage = ( shielded : boolean ) : IbcTransferStage => {
101
129
return shielded ?
102
130
{ type : "IbcToShielded" , currentStep : TransferStep . IbcToShielded }
@@ -116,12 +144,14 @@ export const useIbcTransaction = ({
116
144
invariant ( selectedAsset , "Error: No asset is selected" ) ;
117
145
invariant ( registry , "Error: Invalid chain" ) ;
118
146
invariant ( sourceChannel , "Error: Invalid IBC source channel" ) ;
119
- invariant ( gasConfig , "Error: No transaction fee is set" ) ;
147
+ invariant ( stargateClient , "Error: Stargate client not initialized" ) ;
148
+ invariant ( rpcUrl , "Error: RPC URL not initialized" ) ;
120
149
invariant (
121
150
! shielded || destinationChannel ,
122
151
"Error: Destination channel not provided"
123
152
) ;
124
153
154
+ // Set Keplr option to allow Namadillo to set the transaction fee
125
155
const baseKeplr = getKeplrWallet ( ) ;
126
156
const savedKeplrOptions = baseKeplr . defaultOptions ;
127
157
baseKeplr . defaultOptions = {
@@ -132,6 +162,8 @@ export const useIbcTransaction = ({
132
162
133
163
try {
134
164
const baseAmount = toBaseAmount ( selectedAsset . asset , displayAmount ) ;
165
+
166
+ // This step might require a bit of time
135
167
const { memo : maspCompatibleMemo , receiver : maspCompatibleReceiver } =
136
168
await ( async ( ) => {
137
169
onUpdateStatus ?.( "Generating MASP parameters..." ) ;
@@ -145,48 +177,49 @@ export const useIbcTransaction = ({
145
177
: { memo, receiver : destinationAddress } ;
146
178
} ) ( ) ;
147
179
148
- // Set Keplr option to allow Namadillo to set the transaction fee
149
180
const chainId = registry . chain . chain_id ;
150
-
151
- return await queryAndStoreRpc ( registry . chain , async ( rpc : string ) => {
152
- onUpdateStatus ?.( "Waiting for signature..." ) ;
153
- const client = await createStargateClient ( rpc , registry . chain ) ;
154
- const ibcTransferParams = {
155
- signer : baseKeplr . getOfflineSigner ( chainId ) ,
156
- chainId,
157
- sourceAddress : sanitizeAddress ( sourceAddress ) ,
158
- destinationAddress : sanitizeAddress ( maspCompatibleReceiver ) ,
159
- amount : displayAmount ,
160
- asset : selectedAsset ,
161
- gasConfig,
162
- sourceChannelId : sanitizeChannel ( sourceChannel ! ) ,
163
- destinationChannelId : sanitizeChannel ( destinationChannel ! ) || "" ,
164
- isShielded : ! ! shielded ,
165
- } ;
166
-
167
- const signedMessage = await getSignedMessage (
168
- client ,
169
- ibcTransferParams ,
170
- maspCompatibleMemo
171
- ) ;
172
-
173
- onUpdateStatus ?.( "Broadcasting transaction..." ) ;
174
- const txResponse = await broadcastIbcTx . mutateAsync ( {
175
- client,
176
- tx : signedMessage ,
177
- } ) ;
178
-
179
- const tx = createTransferDataFromIbc (
180
- txResponse ,
181
- rpc ,
182
- selectedAsset . asset ,
183
- chainId ,
184
- getIbcTransferStage ( ! ! shielded )
185
- ) ;
186
- dispatchPendingTxNotification ( tx ) ;
187
- setTxHash ( tx . hash ) ;
188
- return tx ;
181
+ const transferMsg = createIbcTransferMessage (
182
+ sanitizeChannel ( sourceChannel ! ) ,
183
+ sanitizeAddress ( sourceAddress ) ,
184
+ sanitizeAddress ( maspCompatibleReceiver ) ,
185
+ baseAmount ,
186
+ selectedAsset . originalAddress ,
187
+ maspCompatibleMemo
188
+ ) ;
189
+
190
+ // In case the first estimate has failed for some reason, we try to refetch
191
+ const gasConfig = await ( async ( ) => {
192
+ if ( ! gasConfigQuery . data ) {
193
+ onUpdateStatus ?.( "Estimating required gas..." ) ;
194
+ return ( await gasConfigQuery . refetch ( ) ) . data ;
195
+ }
196
+ return gasConfigQuery . data ;
197
+ } ) ( ) ;
198
+ invariant ( gasConfig , "Error: Failed to estimate gas usage" ) ;
199
+
200
+ onUpdateStatus ?.( "Waiting for signature..." ) ;
201
+ const signedMessage = await getSignedMessage (
202
+ stargateClient ,
203
+ transferMsg ,
204
+ gasConfig
205
+ ) ;
206
+
207
+ onUpdateStatus ?.( "Broadcasting transaction..." ) ;
208
+ const txResponse = await broadcastIbcTx . mutateAsync ( {
209
+ client : stargateClient ,
210
+ tx : signedMessage ,
189
211
} ) ;
212
+
213
+ const tx = createTransferDataFromIbc (
214
+ txResponse ,
215
+ rpcUrl || "" ,
216
+ selectedAsset . asset ,
217
+ chainId ,
218
+ getIbcTransferStage ( ! ! shielded )
219
+ ) ;
220
+ dispatchPendingTxNotification ( tx ) ;
221
+ setTxHash ( tx . hash ) ;
222
+ return tx ;
190
223
} catch ( err ) {
191
224
dispatchErrorTxNotification ( err ) ;
192
225
throw err ;
@@ -197,7 +230,7 @@ export const useIbcTransaction = ({
197
230
198
231
return {
199
232
transferToNamada,
200
- gasConfig,
233
+ gasConfig : gasConfigQuery ,
201
234
transferStatus : broadcastIbcTx . status ,
202
235
} ;
203
236
} ;
0 commit comments