@0xsquid/react-hooks 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +7 -0
- package/.eslintignore +1 -0
- package/.eslintrc.json +44 -0
- package/dist/index.js +6 -0
- package/jest.config.ts +28 -0
- package/package.json +124 -0
- package/scripts/copyAssets.js +27 -0
- package/scripts/removeBuildTestFolder.js +28 -0
- package/scripts/replacePaths.js +37 -0
- package/src/assets/images/icons/wallets/binance.svg +4 -0
- package/src/assets/images/icons/wallets/bitget.svg +43 -0
- package/src/assets/images/icons/wallets/bitkeep.svg +9 -0
- package/src/assets/images/icons/wallets/blockchaindotcom.svg +11 -0
- package/src/assets/images/icons/wallets/brave.svg +18 -0
- package/src/assets/images/icons/wallets/c98.svg +3 -0
- package/src/assets/images/icons/wallets/coinbase.svg +5 -0
- package/src/assets/images/icons/wallets/cosmostation.svg +9 -0
- package/src/assets/images/icons/wallets/defiConnect.svg +25 -0
- package/src/assets/images/icons/wallets/exodus.svg +33 -0
- package/src/assets/images/icons/wallets/fetchai.svg +1 -0
- package/src/assets/images/icons/wallets/index.ts +43 -0
- package/src/assets/images/icons/wallets/keplr.svg +9 -0
- package/src/assets/images/icons/wallets/leap.svg +35 -0
- package/src/assets/images/icons/wallets/metamask.svg +48 -0
- package/src/assets/images/icons/wallets/okx.svg +1 -0
- package/src/assets/images/icons/wallets/rabby.svg +1 -0
- package/src/assets/images/icons/wallets/trustwallet.svg +11 -0
- package/src/assets/images/icons/wallets/walletConnect.svg +11 -0
- package/src/assets/images/icons/wallets/wallet_icon.svg +1 -0
- package/src/assets/images/icons/wallets/xdefi.svg +9 -0
- package/src/assets/images/icons/wallets/zerion.svg +11 -0
- package/src/connectors/ExodusConnector.ts +6 -0
- package/src/connectors/LedgerLiveConnector.ts +226 -0
- package/src/contracts/typechain/ERC20.d.ts +441 -0
- package/src/contracts/typechain/factories/ERC20__factory.d.ts +56 -0
- package/src/contracts/typechain/factories/ERC20__factory.ts +340 -0
- package/src/core/constants.ts +549 -0
- package/src/core/externalLinks.ts +26 -0
- package/src/core/index.ts +1 -0
- package/src/core/multicall3.ts +22 -0
- package/src/core/numbers.ts +44 -0
- package/src/core/providers/CosmosProvider.tsx +55 -0
- package/src/core/queries/queries-keys.ts +156 -0
- package/src/core/queries/react-query-config.ts +13 -0
- package/src/core/types/components.ts +16 -0
- package/src/core/types/config.ts +52 -0
- package/src/core/types/dex.ts +31 -0
- package/src/core/types/error.ts +52 -0
- package/src/core/types/event.ts +16 -0
- package/src/core/types/route.ts +21 -0
- package/src/core/types/swap.ts +8 -0
- package/src/core/types/tokens.ts +30 -0
- package/src/core/types/transaction.ts +84 -0
- package/src/core/types/types.ts +37 -0
- package/src/core/types/wallet.ts +83 -0
- package/src/hooks/chains/useSquidChains.ts +66 -0
- package/src/hooks/cosmos/useCosmos.ts +279 -0
- package/src/hooks/cosmos/useCosmosForChain.ts +33 -0
- package/src/hooks/index.ts +32 -0
- package/src/hooks/navigation/useKeyboardNavigation.ts +88 -0
- package/src/hooks/store/useSquidStore.ts +80 -0
- package/src/hooks/swap/useSwap.ts +359 -0
- package/src/hooks/tokens/useAllTokensWithBalance.ts +157 -0
- package/src/hooks/tokens/useBalance.ts +180 -0
- package/src/hooks/tokens/useMultiChainBalance.ts +59 -0
- package/src/hooks/tokens/usePrices.ts +52 -0
- package/src/hooks/tokens/useSingleTokenPrice.ts +45 -0
- package/src/hooks/tokens/useSquidTokens.ts +48 -0
- package/src/hooks/tokens/useTokensWithBalance.ts +169 -0
- package/src/hooks/transaction/useEstimate.ts +397 -0
- package/src/hooks/transaction/useEstimatePriceImpact.ts +39 -0
- package/src/hooks/transaction/useExecuteTransaction.ts +440 -0
- package/src/hooks/transaction/useExpress.ts +46 -0
- package/src/hooks/transaction/useGetRoute.ts +145 -0
- package/src/hooks/transaction/useSingleTransaction.ts +155 -0
- package/src/hooks/transaction/useTransaction.ts +734 -0
- package/src/hooks/user/useUserParams.ts +63 -0
- package/src/hooks/wallet/useAddToken.ts +53 -0
- package/src/hooks/wallet/useAutoConnect.ts +56 -0
- package/src/hooks/wallet/useGnosisContext.ts +84 -0
- package/src/hooks/wallet/useIntegratorContext.ts +44 -0
- package/src/hooks/wallet/useMultiChainWallet.ts +146 -0
- package/src/hooks/wallet/useWallet.ts +86 -0
- package/src/hooks/webapp-router/useSquidRouter.ts +42 -0
- package/src/index.ts +5 -0
- package/src/provider/index.tsx +105 -0
- package/src/react-app-env.d.ts +2 -0
- package/src/services/external/analyticsService.ts +46 -0
- package/src/services/external/coingeckoService.ts +35 -0
- package/src/services/external/rpcService.ts +196 -0
- package/src/services/external/secretService.ts +178 -0
- package/src/services/index.ts +1 -0
- package/src/services/internal/apiService.ts +6 -0
- package/src/services/internal/assetsService.ts +84 -0
- package/src/services/internal/configService.ts +261 -0
- package/src/services/internal/errorService.ts +124 -0
- package/src/services/internal/eventService.ts +93 -0
- package/src/services/internal/priceService.ts +10 -0
- package/src/services/internal/transactionService.ts +255 -0
- package/src/services/internal/transactionStatusService.ts +293 -0
- package/src/services/internal/walletService.ts +158 -0
- package/src/tests/assetsService.test.ts +176 -0
- package/src/tests/configService.test.ts +486 -0
- package/src/tests/fetchSquidData.ts +43 -0
- package/src/tests/jest-svg-transform.ts +19 -0
- package/src/tests/sample.json +0 -0
- package/src/tests/walletService.test.ts +178 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TransactionHistoryStore,
|
|
3
|
+
TransactionParams,
|
|
4
|
+
TransactionStatus,
|
|
5
|
+
} from "@/core/types/transaction";
|
|
6
|
+
import { TransactionType } from "@/core/types/transaction";
|
|
7
|
+
import { usePersistStore } from "@/hooks/store/useSquidStore";
|
|
8
|
+
import type { ChainData } from "@0xsquid/sdk/dist/types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Helper to find the desired transaction history in array
|
|
12
|
+
* @param transactionID
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export const findHistoryItem = (
|
|
16
|
+
transactionID: string | undefined
|
|
17
|
+
): TransactionHistoryStore | undefined => {
|
|
18
|
+
// TODO: This works but could be improved, persistStore shouldnt be used directly
|
|
19
|
+
// Or move this inside a callback hook
|
|
20
|
+
return usePersistStore
|
|
21
|
+
.getState()
|
|
22
|
+
.transactionsHistory?.find((th) => th.transactionId === transactionID);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Will return the same object but with the desired transaction history
|
|
27
|
+
* Status changed
|
|
28
|
+
* @param transactionID
|
|
29
|
+
* @param status
|
|
30
|
+
* @param transactionsHistory
|
|
31
|
+
* @param statusResponse
|
|
32
|
+
* @returns
|
|
33
|
+
*/
|
|
34
|
+
export const updateTransactionHistoryStatus = (
|
|
35
|
+
transactionID: string | undefined,
|
|
36
|
+
status: TransactionStatus,
|
|
37
|
+
transactionsHistory: TransactionHistoryStore[] | undefined,
|
|
38
|
+
statusResponse: any | undefined
|
|
39
|
+
): TransactionHistoryStore[] => {
|
|
40
|
+
return (
|
|
41
|
+
transactionsHistory?.map((th) => {
|
|
42
|
+
// Use the status response from the query if available, if not, use the one from the store
|
|
43
|
+
statusResponse = statusResponse ?? th.statusResponse;
|
|
44
|
+
|
|
45
|
+
// If the transaction is already in the store and had a different status, update status
|
|
46
|
+
if (th.transactionId === transactionID && th.status !== status) {
|
|
47
|
+
return {
|
|
48
|
+
...th,
|
|
49
|
+
status,
|
|
50
|
+
statusResponse,
|
|
51
|
+
axelarUrl: statusResponse?.axelarTransactionUrl,
|
|
52
|
+
sourceTxExplorerUrl: statusResponse?.fromChain?.transactionUrl,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
...th,
|
|
57
|
+
axelarUrl: statusResponse?.axelarTransactionUrl,
|
|
58
|
+
sourceTxExplorerUrl: statusResponse?.fromChain?.transactionUrl,
|
|
59
|
+
};
|
|
60
|
+
}) ?? []
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const replaceTransactionAtNonce = (
|
|
65
|
+
nonce: number,
|
|
66
|
+
fromAddress: string,
|
|
67
|
+
transactionsHistory: TransactionHistoryStore[] | undefined,
|
|
68
|
+
TransactionHistoryStore: TransactionHistoryStore
|
|
69
|
+
): TransactionHistoryStore[] => {
|
|
70
|
+
const newTransactionsHistory: TransactionHistoryStore[] =
|
|
71
|
+
transactionsHistory ?? [];
|
|
72
|
+
// Find the transaction with the same nonce and from address
|
|
73
|
+
const transactionIndex = transactionsHistory?.findIndex(
|
|
74
|
+
(th) => th.nonce === nonce && th.fromAddress === fromAddress
|
|
75
|
+
);
|
|
76
|
+
// If found, replace it with the new transaction
|
|
77
|
+
if (transactionIndex !== undefined && transactionIndex !== -1) {
|
|
78
|
+
newTransactionsHistory[transactionIndex] = TransactionHistoryStore;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return newTransactionsHistory;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const formatTransactionHistoryDate = (
|
|
85
|
+
transaction: TransactionHistoryStore | undefined
|
|
86
|
+
): { month: string; day: string } | undefined => {
|
|
87
|
+
if (!transaction?.timestamp) return undefined;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const date = new Date(Number(transaction.timestamp));
|
|
91
|
+
|
|
92
|
+
// Format date to: MMM DD. Examples:
|
|
93
|
+
// Jan 01
|
|
94
|
+
// May 12
|
|
95
|
+
const month = new Intl.DateTimeFormat("en-US", { month: "short" }).format(
|
|
96
|
+
date
|
|
97
|
+
);
|
|
98
|
+
const day = date.toLocaleString("en-US", { day: "2-digit" });
|
|
99
|
+
|
|
100
|
+
return { month, day };
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error("Error formatting date:", error);
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const getAxelarExplorerTxUrl = (
|
|
108
|
+
urlPrefix: string | undefined,
|
|
109
|
+
routeType: string | undefined,
|
|
110
|
+
txID: string
|
|
111
|
+
): string | undefined => {
|
|
112
|
+
if (!urlPrefix) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const txType = (routeType as TransactionType) ?? TransactionType.BRIDGE;
|
|
116
|
+
if (
|
|
117
|
+
txType === TransactionType.CALL_BRIDGE ||
|
|
118
|
+
txType === TransactionType.BRIDGE
|
|
119
|
+
) {
|
|
120
|
+
return `${urlPrefix}transfer/${txID}`;
|
|
121
|
+
}
|
|
122
|
+
return `${urlPrefix}gmp/${txID}`;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const getSourceExplorerTxUrl = (
|
|
126
|
+
chain: ChainData | undefined,
|
|
127
|
+
txID: string
|
|
128
|
+
): string | undefined => {
|
|
129
|
+
if (!chain || !chain.blockExplorerUrls[0]) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let txSuffix: string;
|
|
134
|
+
|
|
135
|
+
switch (chain.chainId) {
|
|
136
|
+
case "agoric-3":
|
|
137
|
+
txSuffix = "/transactions/";
|
|
138
|
+
break;
|
|
139
|
+
default:
|
|
140
|
+
txSuffix = "/tx/";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (chain.blockExplorerUrls[0].endsWith("/")) {
|
|
144
|
+
txSuffix = txSuffix.slice(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return `${chain.blockExplorerUrls[0]}${txSuffix}${txID}`;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const getMainExplorerUrl = (transaction?: TransactionParams) => {
|
|
151
|
+
// The most accurate one is coming from squid /status api
|
|
152
|
+
if (transaction?.statusResponse?.axelarTransactionUrl) {
|
|
153
|
+
return transaction?.statusResponse.axelarTransactionUrl;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// If not, we can try to get it from the source chain
|
|
157
|
+
if (transaction?.sourceTxExplorerUrl) {
|
|
158
|
+
return transaction?.sourceTxExplorerUrl;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// If not, we can try to guess it from the transaction type
|
|
162
|
+
if (transaction && transaction?.transactionId) {
|
|
163
|
+
return getAxelarExplorerTxUrl(
|
|
164
|
+
transaction.statusResponse?.axelarTransactionUrl,
|
|
165
|
+
transaction.routeType,
|
|
166
|
+
transaction.transactionId
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return undefined;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* A CCTP Transaction is one that is not using Axelar, but using Circle Mint & Burn mechanism
|
|
175
|
+
* Squid API is using a different status endpoint to track status of CCTP transactions
|
|
176
|
+
* @param transaction
|
|
177
|
+
* @returns
|
|
178
|
+
*/
|
|
179
|
+
export const isCCTPBridge = (transaction: TransactionParams | undefined) => {
|
|
180
|
+
return (
|
|
181
|
+
transaction?.fromChain?.chainId.includes("noble") ||
|
|
182
|
+
transaction?.toChain?.chainId.includes("noble")
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export const formatDistance = (
|
|
187
|
+
date: number | Date,
|
|
188
|
+
baseDate: number | Date,
|
|
189
|
+
options?: {
|
|
190
|
+
includeSeconds?: boolean | undefined;
|
|
191
|
+
addSuffix?: boolean | undefined;
|
|
192
|
+
locale?: Locale | undefined;
|
|
193
|
+
}
|
|
194
|
+
): string => {
|
|
195
|
+
const {
|
|
196
|
+
includeSeconds = false,
|
|
197
|
+
addSuffix = false,
|
|
198
|
+
locale = { locale: "en-US" },
|
|
199
|
+
} = options || {};
|
|
200
|
+
|
|
201
|
+
const elapsedMilliseconds = Math.abs(
|
|
202
|
+
new Date(date).getTime() - new Date(baseDate).getTime()
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const seconds = Math.round(elapsedMilliseconds / 1000);
|
|
206
|
+
const minutes = Math.round(seconds / 60);
|
|
207
|
+
const hours = Math.round(minutes / 60);
|
|
208
|
+
const days = Math.round(hours / 24);
|
|
209
|
+
const months = Math.round(days / 30.44);
|
|
210
|
+
const years = Math.round(days / 365.25);
|
|
211
|
+
|
|
212
|
+
const rtf = new Intl.RelativeTimeFormat(locale.locale, { numeric: "auto" });
|
|
213
|
+
|
|
214
|
+
let formatted = "";
|
|
215
|
+
|
|
216
|
+
if (includeSeconds && seconds < 45) {
|
|
217
|
+
const unit = addSuffix ? "second" : "seconds";
|
|
218
|
+
formatted = rtf.format(-seconds, unit);
|
|
219
|
+
} else if (minutes < 60) {
|
|
220
|
+
formatted = rtf.format(-minutes, "minutes");
|
|
221
|
+
} else if (hours < 24) {
|
|
222
|
+
formatted = rtf.format(-hours, "hours");
|
|
223
|
+
} else if (days < 30) {
|
|
224
|
+
formatted = rtf.format(-days, "days");
|
|
225
|
+
} else if (months < 12) {
|
|
226
|
+
formatted = rtf.format(-months, "months");
|
|
227
|
+
} else {
|
|
228
|
+
formatted = rtf.format(-years, "years");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// remove "ago" from the string
|
|
232
|
+
// before: "2 minutes ago"
|
|
233
|
+
// after: "2 minutes"
|
|
234
|
+
return addSuffix ? formatted : formatted.replace(/\b(?:ago)\b/, "").trim();
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export const formatSeconds = (
|
|
238
|
+
seconds: number,
|
|
239
|
+
secondsTemplate: string = " seconds",
|
|
240
|
+
minutesTemplate: string = " minutes"
|
|
241
|
+
) => {
|
|
242
|
+
let duration = "";
|
|
243
|
+
if (seconds < 60) {
|
|
244
|
+
duration = `${seconds.toString()}${secondsTemplate}`;
|
|
245
|
+
} else {
|
|
246
|
+
duration = formatDistance(0, seconds * 1000, { includeSeconds: true });
|
|
247
|
+
}
|
|
248
|
+
return duration
|
|
249
|
+
.replace(" minutes", minutesTemplate)
|
|
250
|
+
.replace(" minute", minutesTemplate);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
interface Locale {
|
|
254
|
+
locale?: string;
|
|
255
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { SquidStatusErrorType } from "@/core/types/error";
|
|
2
|
+
import type {
|
|
3
|
+
StepStatusGetterProps,
|
|
4
|
+
TransactionParams,
|
|
5
|
+
} from "@/core/types/transaction";
|
|
6
|
+
import {
|
|
7
|
+
AxelarStatusResponseType,
|
|
8
|
+
TransactionStatus,
|
|
9
|
+
TransactionType,
|
|
10
|
+
} from "@/core/types/transaction";
|
|
11
|
+
import type { ChainData, StatusResponse, Token } from "@0xsquid/sdk/dist/types";
|
|
12
|
+
import type { UseQueryResult } from "@tanstack/react-query";
|
|
13
|
+
import { isStatusError } from "./errorService";
|
|
14
|
+
import { getMainExplorerUrl } from "./transactionService";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the steps for a transaction
|
|
18
|
+
* First step and second step are always the same
|
|
19
|
+
* @param transaction
|
|
20
|
+
* @param statusResponse
|
|
21
|
+
* @returns {TransactionStepStatus[]}
|
|
22
|
+
*/
|
|
23
|
+
export const getStepStatuses = ({
|
|
24
|
+
transaction,
|
|
25
|
+
statusResponse,
|
|
26
|
+
onlyFullStatusStep,
|
|
27
|
+
}: StepStatusGetterProps): TransactionStatus[] => {
|
|
28
|
+
let firstStepStatus: TransactionStatus;
|
|
29
|
+
|
|
30
|
+
// "warning" state is a custom one indicating that
|
|
31
|
+
// the user is taking too much time to validate the transaction
|
|
32
|
+
// And can be set to loading
|
|
33
|
+
switch (transaction?.sourceStatus) {
|
|
34
|
+
case "error":
|
|
35
|
+
case "success":
|
|
36
|
+
case "ongoing":
|
|
37
|
+
firstStepStatus = transaction.sourceStatus;
|
|
38
|
+
break;
|
|
39
|
+
case "warning":
|
|
40
|
+
firstStepStatus = TransactionStatus.ONGOING;
|
|
41
|
+
break;
|
|
42
|
+
default:
|
|
43
|
+
firstStepStatus = TransactionStatus.PENDING;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let middleStepStatus: TransactionStatus =
|
|
47
|
+
!onlyFullStatusStep && firstStepStatus !== "success"
|
|
48
|
+
? TransactionStatus.PENDING
|
|
49
|
+
: middleStepChecker(statusResponse);
|
|
50
|
+
|
|
51
|
+
const lastStepStatus = getLastStepStatus(firstStepStatus, middleStepStatus);
|
|
52
|
+
|
|
53
|
+
// Once we have the last step status,
|
|
54
|
+
// we have to override the middle step for some states
|
|
55
|
+
middleStepStatus =
|
|
56
|
+
middleStepStatus === "needs_gas" || middleStepStatus === "partial_success"
|
|
57
|
+
? TransactionStatus.SUCCESS
|
|
58
|
+
: middleStepStatus;
|
|
59
|
+
|
|
60
|
+
return onlyFullStatusStep
|
|
61
|
+
? [middleStepStatus]
|
|
62
|
+
: [firstStepStatus, middleStepStatus, lastStepStatus];
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const getLastStepStatus = (
|
|
66
|
+
first: TransactionStatus,
|
|
67
|
+
middle: TransactionStatus
|
|
68
|
+
): TransactionStatus => {
|
|
69
|
+
if (
|
|
70
|
+
first === TransactionStatus.PENDING ||
|
|
71
|
+
first === "ongoing" ||
|
|
72
|
+
first === "error"
|
|
73
|
+
) {
|
|
74
|
+
return TransactionStatus.PENDING;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
switch (middle) {
|
|
78
|
+
case "initialLoading":
|
|
79
|
+
case "ongoing":
|
|
80
|
+
return TransactionStatus.PENDING;
|
|
81
|
+
default:
|
|
82
|
+
return middle;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const getHalfSuccessState = (
|
|
87
|
+
status?: TransactionStatus
|
|
88
|
+
): TransactionStatus | undefined => {
|
|
89
|
+
switch (status) {
|
|
90
|
+
case "success":
|
|
91
|
+
return TransactionStatus.SUCCESS;
|
|
92
|
+
case "partial_success": // Received axlUSDC
|
|
93
|
+
return TransactionStatus.PARTIAL_SUCCESS;
|
|
94
|
+
case "needs_gas":
|
|
95
|
+
return TransactionStatus.NEEDS_GAS;
|
|
96
|
+
default:
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const middleStepChecker = (
|
|
102
|
+
statusResponse?: UseQueryResult<any, unknown>
|
|
103
|
+
): TransactionStatus => {
|
|
104
|
+
const squidStatus = statusResponse?.data;
|
|
105
|
+
const successState = getHalfSuccessState(
|
|
106
|
+
squidStatus?.squidTransactionStatus as TransactionStatus | undefined
|
|
107
|
+
);
|
|
108
|
+
if (successState) {
|
|
109
|
+
return successState;
|
|
110
|
+
}
|
|
111
|
+
if (squidStatus?.status === AxelarStatusResponseType.ERROR) {
|
|
112
|
+
const { error } = squidStatus;
|
|
113
|
+
if (isStatusError(error)) {
|
|
114
|
+
if (error.errorType === SquidStatusErrorType.NotFoundError) {
|
|
115
|
+
return TransactionStatus.ERROR;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return TransactionStatus.ERROR;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (statusResponse?.isInitialLoading) {
|
|
122
|
+
return TransactionStatus.INITIAL_LOADING;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return TransactionStatus.ONGOING;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const getStepsInfos = ({
|
|
129
|
+
fromChain,
|
|
130
|
+
toChain,
|
|
131
|
+
fromToken,
|
|
132
|
+
toToken,
|
|
133
|
+
amount,
|
|
134
|
+
txType,
|
|
135
|
+
transaction,
|
|
136
|
+
statusResponse,
|
|
137
|
+
}: {
|
|
138
|
+
txType: TransactionType;
|
|
139
|
+
amount: string;
|
|
140
|
+
fromChain?: ChainData;
|
|
141
|
+
toChain?: ChainData;
|
|
142
|
+
fromToken?: Token;
|
|
143
|
+
toToken?: Token;
|
|
144
|
+
transaction?: TransactionParams;
|
|
145
|
+
statusResponse?: UseQueryResult<StatusResponse, unknown>;
|
|
146
|
+
}): {
|
|
147
|
+
label: string;
|
|
148
|
+
status: TransactionStatus;
|
|
149
|
+
subTitle?: string;
|
|
150
|
+
link?: string;
|
|
151
|
+
}[] => {
|
|
152
|
+
const [firstStepStatus, middleStepStatus, lastStepStatus] = getStepStatuses({
|
|
153
|
+
transaction,
|
|
154
|
+
statusResponse,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const payLabel = `Pay ${amount} ${fromToken?.symbol} on ${fromChain?.networkName}`;
|
|
158
|
+
const swapForUSDCLabel = `Swap ${fromToken?.symbol} for axlUSDC`;
|
|
159
|
+
const sendUSDCLabel = `Send axlUSDC to ${toChain?.networkName}`;
|
|
160
|
+
const swapUSDCLabel = `Swap axlUSDC for ${toToken?.symbol}`;
|
|
161
|
+
const receiveLabel = `Receive ${toToken?.symbol} on ${toChain?.networkName}`;
|
|
162
|
+
const axelarUrl = getMainExplorerUrl(transaction);
|
|
163
|
+
const sourceExplorerUrl = transaction?.sourceTxExplorerUrl;
|
|
164
|
+
const destinationExplorerUrl = statusResponse?.data?.toChain?.transactionUrl;
|
|
165
|
+
|
|
166
|
+
switch (txType) {
|
|
167
|
+
case TransactionType.CALL_BRIDGE_CALL:
|
|
168
|
+
return [
|
|
169
|
+
{
|
|
170
|
+
label: payLabel,
|
|
171
|
+
status: firstStepStatus,
|
|
172
|
+
link: sourceExplorerUrl,
|
|
173
|
+
subTitle: "View on explorer",
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
label: swapForUSDCLabel,
|
|
177
|
+
status:
|
|
178
|
+
firstStepStatus !== "success"
|
|
179
|
+
? TransactionStatus.PENDING
|
|
180
|
+
: firstStepStatus,
|
|
181
|
+
link: axelarUrl,
|
|
182
|
+
subTitle: "View on Axelarscan",
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
label: sendUSDCLabel,
|
|
186
|
+
status: middleStepStatus,
|
|
187
|
+
link: axelarUrl,
|
|
188
|
+
subTitle: "View on Axelarscan",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
label: swapUSDCLabel,
|
|
192
|
+
status:
|
|
193
|
+
middleStepStatus !== "success"
|
|
194
|
+
? TransactionStatus.PENDING
|
|
195
|
+
: middleStepStatus,
|
|
196
|
+
link: axelarUrl,
|
|
197
|
+
subTitle: "View on Axelarscan",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
label: receiveLabel,
|
|
201
|
+
status: lastStepStatus,
|
|
202
|
+
link: destinationExplorerUrl,
|
|
203
|
+
subTitle: "View on explorer",
|
|
204
|
+
},
|
|
205
|
+
];
|
|
206
|
+
case TransactionType.CALL_BRIDGE:
|
|
207
|
+
return [
|
|
208
|
+
{
|
|
209
|
+
label: payLabel,
|
|
210
|
+
status: firstStepStatus,
|
|
211
|
+
link: sourceExplorerUrl,
|
|
212
|
+
subTitle: "View on explorer",
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
label: swapForUSDCLabel,
|
|
216
|
+
status:
|
|
217
|
+
firstStepStatus !== "success"
|
|
218
|
+
? TransactionStatus.PENDING
|
|
219
|
+
: firstStepStatus,
|
|
220
|
+
link: axelarUrl,
|
|
221
|
+
subTitle: "View on Axelarscan",
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
label: sendUSDCLabel,
|
|
225
|
+
status: middleStepStatus,
|
|
226
|
+
link: axelarUrl,
|
|
227
|
+
subTitle: "View on Axelarscan",
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
label: receiveLabel,
|
|
231
|
+
status: lastStepStatus,
|
|
232
|
+
link: destinationExplorerUrl,
|
|
233
|
+
subTitle: "View on explorer",
|
|
234
|
+
},
|
|
235
|
+
];
|
|
236
|
+
case TransactionType.BRIDGE:
|
|
237
|
+
return [
|
|
238
|
+
{
|
|
239
|
+
label: payLabel,
|
|
240
|
+
status: firstStepStatus,
|
|
241
|
+
link: sourceExplorerUrl,
|
|
242
|
+
subTitle: "View on explorer",
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
label: sendUSDCLabel,
|
|
246
|
+
status:
|
|
247
|
+
firstStepStatus !== "success"
|
|
248
|
+
? TransactionStatus.PENDING
|
|
249
|
+
: firstStepStatus,
|
|
250
|
+
link: axelarUrl,
|
|
251
|
+
subTitle: "View on Axelarscan",
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
label: receiveLabel,
|
|
255
|
+
status: lastStepStatus,
|
|
256
|
+
link: destinationExplorerUrl,
|
|
257
|
+
subTitle: "View on explorer",
|
|
258
|
+
},
|
|
259
|
+
];
|
|
260
|
+
case TransactionType.BRIDGE_CALL:
|
|
261
|
+
return [
|
|
262
|
+
{
|
|
263
|
+
label: payLabel,
|
|
264
|
+
status: firstStepStatus,
|
|
265
|
+
link: sourceExplorerUrl,
|
|
266
|
+
subTitle: "View on explorer",
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
label: sendUSDCLabel,
|
|
270
|
+
status:
|
|
271
|
+
firstStepStatus !== "success"
|
|
272
|
+
? TransactionStatus.PENDING
|
|
273
|
+
: firstStepStatus,
|
|
274
|
+
link: axelarUrl,
|
|
275
|
+
subTitle: "View on Axelarscan",
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
label: swapUSDCLabel,
|
|
279
|
+
status: middleStepStatus,
|
|
280
|
+
link: axelarUrl,
|
|
281
|
+
subTitle: "View on Axelarscan",
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
label: receiveLabel,
|
|
285
|
+
status: lastStepStatus,
|
|
286
|
+
link: destinationExplorerUrl,
|
|
287
|
+
subTitle: "View on explorer",
|
|
288
|
+
},
|
|
289
|
+
];
|
|
290
|
+
default:
|
|
291
|
+
return [];
|
|
292
|
+
}
|
|
293
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { walletStoreLinks, wallets } from "@/core/constants";
|
|
2
|
+
import type { ConnectorID, TypedWindow, Wallet } from "@/core/types/wallet";
|
|
3
|
+
import type { ChainData, CosmosChain } from "@0xsquid/sdk/dist/types";
|
|
4
|
+
import { ChainType } from "@0xsquid/sdk/dist/types";
|
|
5
|
+
import { fromBech32 } from "@cosmjs/encoding";
|
|
6
|
+
import type { ChainInfo } from "@keplr-wallet/types";
|
|
7
|
+
import { ethers } from "ethers";
|
|
8
|
+
import getProperty from "lodash/get";
|
|
9
|
+
import type { Chain } from "wagmi";
|
|
10
|
+
|
|
11
|
+
export const formatWalletAddress = (
|
|
12
|
+
walletAddress: string | undefined,
|
|
13
|
+
trimLength: number = 5
|
|
14
|
+
) => {
|
|
15
|
+
return walletAddress
|
|
16
|
+
? walletAddress?.length > trimLength
|
|
17
|
+
? `${walletAddress.slice(0, trimLength)}...${walletAddress.slice(
|
|
18
|
+
walletAddress.length - (trimLength - 2), // -2 here because the start of wallet is 0x
|
|
19
|
+
walletAddress.length
|
|
20
|
+
)}`.toLowerCase()
|
|
21
|
+
: walletAddress
|
|
22
|
+
: "";
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const isCosmosAddressValid = (chainPrefix: string, address: string) => {
|
|
26
|
+
try {
|
|
27
|
+
if (!address.toLowerCase().startsWith(chainPrefix)) {
|
|
28
|
+
throw new Error("Invalid address for this chain");
|
|
29
|
+
}
|
|
30
|
+
fromBech32(address);
|
|
31
|
+
return true;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const isWalletAddressValid = (
|
|
38
|
+
chainData?: ChainData,
|
|
39
|
+
address?: string
|
|
40
|
+
) => {
|
|
41
|
+
if (address) {
|
|
42
|
+
if (chainData?.chainType === ChainType.EVM) {
|
|
43
|
+
return ethers.utils.isAddress(address);
|
|
44
|
+
}
|
|
45
|
+
if (chainData?.chainType === ChainType.COSMOS) {
|
|
46
|
+
return isCosmosAddressValid(
|
|
47
|
+
(chainData as CosmosChain).bech32Config.bech32PrefixAccAddr,
|
|
48
|
+
address
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const getWalletByConnectorID = (
|
|
56
|
+
connectorID: ConnectorID
|
|
57
|
+
): Wallet | undefined => {
|
|
58
|
+
return wallets.find((w) => w.connectorId === connectorID);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const getCosmosChainInfosObject = (chain: ChainData): ChainInfo => {
|
|
62
|
+
const cosmosChain = chain as CosmosChain;
|
|
63
|
+
return {
|
|
64
|
+
...cosmosChain,
|
|
65
|
+
chainName: cosmosChain.networkName,
|
|
66
|
+
chainId: cosmosChain.chainId.toString(),
|
|
67
|
+
rpc: cosmosChain.rpc.split("?chain")[0],
|
|
68
|
+
feeCurrencies: cosmosChain.feeCurrencies.map((c) => ({
|
|
69
|
+
...c,
|
|
70
|
+
gasPriceStep: cosmosChain.gasPriceStep,
|
|
71
|
+
})),
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const isWalletExtensionInstalled = (wallet: Wallet) => {
|
|
76
|
+
if (
|
|
77
|
+
(wallet.connectorId === "metaMask" && !window.ethereum) ||
|
|
78
|
+
(wallet.connectorId === "keplr" && !(window as TypedWindow).keplr) ||
|
|
79
|
+
(wallet.connectorId === "leap" && !(window as TypedWindow).leap) ||
|
|
80
|
+
// Cosmostation has two different connectors for different extensions
|
|
81
|
+
((wallet.connectorId === "cosmostationCosmos" ||
|
|
82
|
+
wallet.connectorId === "cosmostation") &&
|
|
83
|
+
!(window as TypedWindow).cosmostation) ||
|
|
84
|
+
(wallet.connectorId === "xdefi" && !(window as TypedWindow).xfi) ||
|
|
85
|
+
(wallet.connectorId === "bitget" && !(window as TypedWindow).bitkeep) ||
|
|
86
|
+
(wallet.connectorId === "exodus" && !(window as TypedWindow).exodus)
|
|
87
|
+
) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const redirectExtensionStoreIfNotInstalled = (wallet: Wallet) => {
|
|
94
|
+
const { userAgent } = navigator;
|
|
95
|
+
let link;
|
|
96
|
+
|
|
97
|
+
if (!isWalletExtensionInstalled(wallet)) {
|
|
98
|
+
if (userAgent.indexOf("Firefox") > -1) {
|
|
99
|
+
link = walletStoreLinks[wallet.connectorId].firefox;
|
|
100
|
+
} else if (userAgent.indexOf("Chrome") > -1) {
|
|
101
|
+
link = walletStoreLinks[wallet.connectorId].chrome;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (link && link !== "") {
|
|
105
|
+
(window as any)?.open(link, "_blank").focus();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get the value of an object property using a string path
|
|
112
|
+
* E.G. window["cosmostation.providers.keplr"]
|
|
113
|
+
* @param obj
|
|
114
|
+
* @param path
|
|
115
|
+
* @returns
|
|
116
|
+
*/
|
|
117
|
+
export const getDescendantProp = (obj: any, path: string) => {
|
|
118
|
+
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const formatChainsForWagmi = (chains: ChainData[]): Chain[] => {
|
|
122
|
+
return chains
|
|
123
|
+
?.filter((c) => c.chainType === ChainType.EVM)
|
|
124
|
+
.map(
|
|
125
|
+
(c) =>
|
|
126
|
+
({
|
|
127
|
+
id: +c.chainId,
|
|
128
|
+
name: c.networkName,
|
|
129
|
+
network: c.networkName,
|
|
130
|
+
contracts: {
|
|
131
|
+
multicall3: {
|
|
132
|
+
address: "0xcA11bde05977b3631167028862bE2a173976CA11",
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
nativeCurrency: c.nativeCurrency,
|
|
136
|
+
rpcUrls: { public: { http: [c.rpc] }, default: { http: [c.rpc] } },
|
|
137
|
+
}) as Chain
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Dynamically accesses a nested property of an object based on a dot-separated string path.
|
|
143
|
+
* Returns the value of the property if found, otherwise undefined.
|
|
144
|
+
* e.g. "xfi.keplr" will return window.xfi.keplr
|
|
145
|
+
*/
|
|
146
|
+
export const accessProperty = (
|
|
147
|
+
object: Record<string, any>,
|
|
148
|
+
path: string
|
|
149
|
+
): any => {
|
|
150
|
+
const value = getProperty(object, path);
|
|
151
|
+
if (value === undefined) {
|
|
152
|
+
console.error(`Property "${path}" not found while reading object`, object);
|
|
153
|
+
}
|
|
154
|
+
return value;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const metamaskIcon = getWalletByConnectorID("metaMask")?.icon;
|
|
158
|
+
export const EVMnetworkNotSupportedErrorCode = 4902;
|