@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,46 @@
|
|
|
1
|
+
interface EventData {
|
|
2
|
+
event: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
dataLayer: Array<EventData> | undefined;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class AnalyticsService {
|
|
12
|
+
private static pushEvent(eventData: EventData): void {
|
|
13
|
+
if (window) {
|
|
14
|
+
const dataLayer = window.dataLayer || [];
|
|
15
|
+
const domain = window.location.hostname;
|
|
16
|
+
|
|
17
|
+
if (dataLayer && domain.includes("squidrouter.com")) {
|
|
18
|
+
dataLayer.push(eventData);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public static submitButtonPushed(): void {
|
|
24
|
+
this.pushEvent({ event: "submitswap_cta_clicked" });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public static givePermissionToUseTokenButton(): void {
|
|
28
|
+
this.pushEvent({ event: "permissiontoken_cta_clicked" });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static settingClicked(): void {
|
|
32
|
+
this.pushEvent({ event: "settings_clicked" });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public static historyClicked(): void {
|
|
36
|
+
this.pushEvent({ event: "history_clicked" });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public static expressClicked(): void {
|
|
40
|
+
this.pushEvent({ event: "expressTx_clicked" });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static regularClicked(): void {
|
|
44
|
+
this.pushEvent({ event: "regularTx_clicked" });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { backendVersion } from "@/core/constants";
|
|
2
|
+
import { getQueryHeaders } from "@/services/internal/apiService";
|
|
3
|
+
import type { Token } from "@0xsquid/sdk/dist/types";
|
|
4
|
+
import axios from "axios";
|
|
5
|
+
|
|
6
|
+
interface CoingeckoPriceQuery {
|
|
7
|
+
apiUrl?: string;
|
|
8
|
+
chainId?: string;
|
|
9
|
+
tokenAddress?: string;
|
|
10
|
+
integratorId?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const fetchPriceForToken = async ({
|
|
14
|
+
apiUrl,
|
|
15
|
+
chainId,
|
|
16
|
+
tokenAddress,
|
|
17
|
+
integratorId,
|
|
18
|
+
}: CoingeckoPriceQuery): Promise<number> => {
|
|
19
|
+
if (chainId && tokenAddress) {
|
|
20
|
+
const params = {
|
|
21
|
+
chainId,
|
|
22
|
+
tokenAddress,
|
|
23
|
+
};
|
|
24
|
+
const url = `${apiUrl}/${backendVersion}/token-price`;
|
|
25
|
+
|
|
26
|
+
const response = await axios.get(url, {
|
|
27
|
+
params,
|
|
28
|
+
headers: getQueryHeaders(integratorId),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const result = (await response.data) as { token: Token };
|
|
32
|
+
return result.token.usdPrice ?? 0;
|
|
33
|
+
}
|
|
34
|
+
return 0;
|
|
35
|
+
};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { ERC20__factory } from "@/contracts/typechain/factories/ERC20__factory";
|
|
2
|
+
import { nativeEvmTokenAddress } from "@/core/constants";
|
|
3
|
+
import { multicallAbi, multicallAddress } from "@/core/multicall3";
|
|
4
|
+
import type { TokenWithBalance } from "@/core/types/tokens";
|
|
5
|
+
import type { ChainData, CosmosChain, Token } from "@0xsquid/sdk/dist/types";
|
|
6
|
+
import { StargateClient } from "@cosmjs/stargate";
|
|
7
|
+
import { fetchBalance } from "@wagmi/core";
|
|
8
|
+
import type { BigNumber } from "ethers";
|
|
9
|
+
import { constants, utils } from "ethers";
|
|
10
|
+
import { readContracts } from "wagmi";
|
|
11
|
+
|
|
12
|
+
type ContractAddress = `0x${string}`;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Use readContracts to fetch all tokens balance in one call
|
|
16
|
+
* native tokens & erc20 tokens will have different abi & contract & function name
|
|
17
|
+
* Because it's not possible to fetch native token balance with balanceOf
|
|
18
|
+
* @param tokens
|
|
19
|
+
* @param userAddress
|
|
20
|
+
* @returns
|
|
21
|
+
*/
|
|
22
|
+
const getTokensSupportingMultiCall = async (
|
|
23
|
+
tokens: Token[],
|
|
24
|
+
userAddress?: ContractAddress
|
|
25
|
+
): Promise<TokenWithBalance[]> => {
|
|
26
|
+
const multicallBalances = (await readContracts({
|
|
27
|
+
contracts: tokens.map((t) => {
|
|
28
|
+
const isTokenNative = t.address.toLowerCase() === nativeEvmTokenAddress;
|
|
29
|
+
return {
|
|
30
|
+
chainId: +t.chainId,
|
|
31
|
+
abi: isTokenNative ? multicallAbi : ERC20__factory.abi,
|
|
32
|
+
address: isTokenNative
|
|
33
|
+
? (multicallAddress as ContractAddress)
|
|
34
|
+
: (t.address as ContractAddress),
|
|
35
|
+
functionName: isTokenNative ? "getEthBalance" : "balanceOf",
|
|
36
|
+
args: [userAddress],
|
|
37
|
+
};
|
|
38
|
+
}),
|
|
39
|
+
allowFailure: true,
|
|
40
|
+
})) as BigNumber[];
|
|
41
|
+
|
|
42
|
+
return tokens.map((t, i) => ({
|
|
43
|
+
...t,
|
|
44
|
+
balance: utils.formatUnits(
|
|
45
|
+
multicallBalances[i] ?? constants.Zero,
|
|
46
|
+
t.decimals
|
|
47
|
+
),
|
|
48
|
+
}));
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type FetchBalanceResult = {
|
|
52
|
+
decimals: number;
|
|
53
|
+
formatted: string;
|
|
54
|
+
symbol: string;
|
|
55
|
+
value: BigNumber;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Some chains don't support multicall, so we need to fetch them with Promise.all & fetchBalance
|
|
60
|
+
* @param tokens
|
|
61
|
+
* @param userAddress
|
|
62
|
+
* @returns
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
const getTokensWithoutMultiCall = async (
|
|
66
|
+
tokens: Token[],
|
|
67
|
+
userAddress: ContractAddress
|
|
68
|
+
): Promise<TokenWithBalance[]> => {
|
|
69
|
+
const balances = (await Promise.all(
|
|
70
|
+
tokens.map(async (t) => {
|
|
71
|
+
let balance: FetchBalanceResult | undefined;
|
|
72
|
+
try {
|
|
73
|
+
if (t.address.toLowerCase() === nativeEvmTokenAddress) {
|
|
74
|
+
balance = await fetchBalance({
|
|
75
|
+
address: userAddress,
|
|
76
|
+
chainId: +t.chainId,
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
balance = await fetchBalance({
|
|
80
|
+
address: userAddress,
|
|
81
|
+
chainId: +t.chainId,
|
|
82
|
+
token: t.address as ContractAddress,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return balance;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
return {
|
|
89
|
+
decimals: t.decimals,
|
|
90
|
+
formatted: "0",
|
|
91
|
+
symbol: t.symbol,
|
|
92
|
+
value: constants.Zero,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
)) as FetchBalanceResult[];
|
|
97
|
+
|
|
98
|
+
return tokens.map((t, i) => ({
|
|
99
|
+
...t,
|
|
100
|
+
balance: utils.formatUnits(balances[i].value, t.decimals),
|
|
101
|
+
}));
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const getAllEvmTokensBalance = async (
|
|
105
|
+
evmTokens: Token[],
|
|
106
|
+
userAddress: string
|
|
107
|
+
): Promise<TokenWithBalance[]> => {
|
|
108
|
+
// Some tokens don't support multicall, so we need to fetch them with Promise.all
|
|
109
|
+
// TODO: Once we support multicall on all chains, we can remove this split
|
|
110
|
+
const chainWithoutMulticall = [314, 3141]; // Filecoin, & Filecoin testnet
|
|
111
|
+
const splittedTokensByMultiCallSupport = evmTokens.reduce(
|
|
112
|
+
(acc, token) => {
|
|
113
|
+
if (chainWithoutMulticall.includes(+token.chainId)) {
|
|
114
|
+
acc[0].push(token);
|
|
115
|
+
} else {
|
|
116
|
+
acc[1].push(token);
|
|
117
|
+
}
|
|
118
|
+
return acc;
|
|
119
|
+
},
|
|
120
|
+
[[], []] as Token[][]
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const tokensNotSupportingMulticall = splittedTokensByMultiCallSupport[0];
|
|
124
|
+
const tokensSupportingMulticall = splittedTokensByMultiCallSupport[1];
|
|
125
|
+
|
|
126
|
+
const tokensMulticall = await getTokensSupportingMultiCall(
|
|
127
|
+
tokensSupportingMulticall,
|
|
128
|
+
userAddress as ContractAddress
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const tokensNotMultiCall = await getTokensWithoutMultiCall(
|
|
132
|
+
tokensNotSupportingMulticall,
|
|
133
|
+
userAddress as ContractAddress
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return [...tokensMulticall, ...tokensNotMultiCall];
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* We'll get the current block of a given RPC to see if we can query it
|
|
141
|
+
* Without getting CORS error or any other error
|
|
142
|
+
* @param rpc
|
|
143
|
+
* @returns
|
|
144
|
+
*/
|
|
145
|
+
const testCosmosRpc = async (rpc: string): Promise<boolean> => {
|
|
146
|
+
let rpcIsValid = false;
|
|
147
|
+
try {
|
|
148
|
+
const client = await StargateClient.connect(rpc);
|
|
149
|
+
const block = await client.getBlock();
|
|
150
|
+
rpcIsValid = !!block.id;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
rpcIsValid = false;
|
|
153
|
+
}
|
|
154
|
+
return rpcIsValid;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const getWorkingCosmosRpcUrl = async (
|
|
158
|
+
chainData: ChainData
|
|
159
|
+
): Promise<string> => {
|
|
160
|
+
const rpcList =
|
|
161
|
+
(chainData as CosmosChain & { rpcList: string[] }).rpcList ?? chainData.rpc
|
|
162
|
+
? [chainData.rpc]
|
|
163
|
+
: [];
|
|
164
|
+
|
|
165
|
+
// if rpcList is empty, use the default rpc
|
|
166
|
+
if (rpcList.length === 0) {
|
|
167
|
+
try {
|
|
168
|
+
const isValid = await testCosmosRpc(chainData.rpc);
|
|
169
|
+
if (isValid) {
|
|
170
|
+
return chainData.rpc;
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.log(
|
|
174
|
+
`Error fetching rpc url: ${chainData.rpc} - Error: ${(error as any)?.toString()}`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// if rpcList is not empty, try to fetch each rpc until we find a valid one
|
|
180
|
+
for (const rpc of rpcList) {
|
|
181
|
+
try {
|
|
182
|
+
// eslint-disable-next-line no-await-in-loop
|
|
183
|
+
const isValid = await testCosmosRpc(rpc);
|
|
184
|
+
if (isValid) {
|
|
185
|
+
return rpc;
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.log(
|
|
189
|
+
`Error fetching rpc url: ${chainData.rpc} - Error: ${(error as any)?.toString()}`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// In the case that all rpcs are invalid, return the default rpc anyway
|
|
195
|
+
return chainData.rpc;
|
|
196
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { TokenWithBalance } from "@/core/types/tokens";
|
|
2
|
+
import type { TypedWindow } from "@/core/types/wallet";
|
|
3
|
+
import type { ChainData, CosmosChain, Token } from "@0xsquid/squid-types";
|
|
4
|
+
import { StargateClient } from "@cosmjs/stargate";
|
|
5
|
+
import type { Keplr } from "@keplr-wallet/types";
|
|
6
|
+
import { formatUnits } from "ethers/lib/utils.js";
|
|
7
|
+
import { SecretNetworkClient } from "secretjs";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Fetch secret network token balance
|
|
11
|
+
* Using the permit signature, see permit function for more details
|
|
12
|
+
* @param secretJS
|
|
13
|
+
* @param contract
|
|
14
|
+
* @param chainId
|
|
15
|
+
* @param walletAddress
|
|
16
|
+
* @param permit
|
|
17
|
+
* @returns
|
|
18
|
+
*/
|
|
19
|
+
export const getTokenBalance = async (
|
|
20
|
+
secretJS: SecretNetworkClient,
|
|
21
|
+
contract: { address: string; codeHash: string },
|
|
22
|
+
permit: any
|
|
23
|
+
) => {
|
|
24
|
+
if (permit) {
|
|
25
|
+
const msg = {
|
|
26
|
+
balance: {},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const result = await secretJS.query.compute.queryContract({
|
|
30
|
+
contract_address: contract.address,
|
|
31
|
+
code_hash: contract.codeHash,
|
|
32
|
+
query: {
|
|
33
|
+
with_permit: {
|
|
34
|
+
query: msg,
|
|
35
|
+
permit,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
return -1;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const getPermit = async (
|
|
45
|
+
chainId: string,
|
|
46
|
+
contracts: any[],
|
|
47
|
+
address: string
|
|
48
|
+
) => {
|
|
49
|
+
const contractsString = contracts.join("_");
|
|
50
|
+
const permKey = `perm_${chainId}_${contractsString}_${address}`;
|
|
51
|
+
let permit: any;
|
|
52
|
+
|
|
53
|
+
const permitStored = window.localStorage.getItem(permKey);
|
|
54
|
+
if (permitStored) permit = JSON.parse(permitStored);
|
|
55
|
+
|
|
56
|
+
// Not able to fetch permit signature from local storage,
|
|
57
|
+
// Ask user to sign message
|
|
58
|
+
if (!permit) {
|
|
59
|
+
try {
|
|
60
|
+
const result = await (window as TypedWindow).keplr.signAmino(
|
|
61
|
+
chainId,
|
|
62
|
+
address,
|
|
63
|
+
{
|
|
64
|
+
chain_id: chainId,
|
|
65
|
+
account_number: "0",
|
|
66
|
+
sequence: "0",
|
|
67
|
+
fee: {
|
|
68
|
+
amount: [{ denom: "uscrt", amount: "0" }],
|
|
69
|
+
gas: "1",
|
|
70
|
+
},
|
|
71
|
+
msgs: [
|
|
72
|
+
{
|
|
73
|
+
type: "query_permit",
|
|
74
|
+
value: {
|
|
75
|
+
permit_name: "secret-bridge-balance",
|
|
76
|
+
allowed_tokens: contracts,
|
|
77
|
+
permissions: ["balance"],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
memo: "",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
preferNoSetFee: true,
|
|
85
|
+
preferNoSetMemo: true,
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
permit = {
|
|
89
|
+
params: {
|
|
90
|
+
permit_name: "secret-bridge-balance",
|
|
91
|
+
allowed_tokens: contracts,
|
|
92
|
+
chain_id: chainId,
|
|
93
|
+
permissions: ["balance"],
|
|
94
|
+
},
|
|
95
|
+
signature: result.signature,
|
|
96
|
+
};
|
|
97
|
+
window.localStorage.setItem(permKey, JSON.stringify(permit));
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.log("--- PERMIT ERROR ---");
|
|
100
|
+
console.log(err);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return permit;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Fetches the secret balance of the user
|
|
108
|
+
* This has a different logic than the other balances because Secret network hides the balance of the user by design
|
|
109
|
+
* So we need to fetch the balance in a different way
|
|
110
|
+
*/
|
|
111
|
+
export const SECRET_CHAIN_ID = "secret-4";
|
|
112
|
+
|
|
113
|
+
export const fetchAllSecretBalances = async (
|
|
114
|
+
chainData: ChainData,
|
|
115
|
+
userAddress: string,
|
|
116
|
+
secretTokens: (Token & { codeHash?: string })[],
|
|
117
|
+
keplr?: Keplr
|
|
118
|
+
): Promise<TokenWithBalance[]> => {
|
|
119
|
+
if (!keplr) return [];
|
|
120
|
+
// Enables app to utilize keplr's secret utilities
|
|
121
|
+
await keplr.enable(SECRET_CHAIN_ID);
|
|
122
|
+
// Create a client that handles the query encryption
|
|
123
|
+
const client = new SecretNetworkClient({
|
|
124
|
+
url: (chainData as CosmosChain).rest,
|
|
125
|
+
chainId: SECRET_CHAIN_ID,
|
|
126
|
+
wallet: keplr.getOfflineSignerOnlyAmino(SECRET_CHAIN_ID),
|
|
127
|
+
encryptionUtils: keplr.getEnigmaUtils(SECRET_CHAIN_ID),
|
|
128
|
+
walletAddress: userAddress,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Get secret tokens
|
|
132
|
+
|
|
133
|
+
const permit = await getPermit(
|
|
134
|
+
SECRET_CHAIN_ID,
|
|
135
|
+
secretTokens.map((st) => st.address),
|
|
136
|
+
userAddress
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// Fetching all balances in parallel
|
|
140
|
+
const privateTokens = await Promise.all(
|
|
141
|
+
secretTokens
|
|
142
|
+
.filter((t) => !!t.codeHash)
|
|
143
|
+
.map(async (token) => {
|
|
144
|
+
const result = (await getTokenBalance(
|
|
145
|
+
client,
|
|
146
|
+
{
|
|
147
|
+
address: token.address,
|
|
148
|
+
codeHash: token.codeHash ?? "",
|
|
149
|
+
},
|
|
150
|
+
permit
|
|
151
|
+
)) as { balance: { amount: string } };
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
...token,
|
|
155
|
+
balance: formatUnits(result.balance.amount, token?.decimals),
|
|
156
|
+
} as TokenWithBalance;
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// Use Stargate getBalance for SCRT
|
|
161
|
+
const stargateClient = await StargateClient.connect(chainData.rpc);
|
|
162
|
+
const nativeSecretToken = secretTokens.find((t) => t.address === "uscrt");
|
|
163
|
+
|
|
164
|
+
const publicTokenBalance = await stargateClient.getBalance(
|
|
165
|
+
userAddress,
|
|
166
|
+
"uscrt"
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const publicTokenWithBalance = {
|
|
170
|
+
...nativeSecretToken,
|
|
171
|
+
balance: formatUnits(
|
|
172
|
+
publicTokenBalance.amount,
|
|
173
|
+
nativeSecretToken?.decimals
|
|
174
|
+
),
|
|
175
|
+
} as TokenWithBalance;
|
|
176
|
+
|
|
177
|
+
return [...privateTokens, publicTokenWithBalance];
|
|
178
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./internal/walletService";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { AppConfig } from "@/core/types/config";
|
|
2
|
+
import type { ChainData, Token } from "@0xsquid/squid-types";
|
|
3
|
+
|
|
4
|
+
export const shareSubgraphId = (token1: Token, token2: Token) => {
|
|
5
|
+
return Boolean(
|
|
6
|
+
token1?.subGraphIds?.some((sgi) => !!token2?.subGraphIds?.includes(sgi)) ||
|
|
7
|
+
token2?.subGraphIds?.some((sgi) => !!token1?.subGraphIds?.includes(sgi))
|
|
8
|
+
);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if `tokenToCheck` shares any subgraph id with the tokens being compared
|
|
13
|
+
* If any of the tokens being compared shares a subgraph id with `tokenToCheck`, it will be sorted first
|
|
14
|
+
* @param a first token being compared
|
|
15
|
+
* @param b second token being compared
|
|
16
|
+
* @param tokenToCheck the token to check subgraph ids against
|
|
17
|
+
*/
|
|
18
|
+
export function sortTokensBySharedSubgraphIds({
|
|
19
|
+
a,
|
|
20
|
+
b,
|
|
21
|
+
tokenToCheck,
|
|
22
|
+
}: {
|
|
23
|
+
a: Token;
|
|
24
|
+
b: Token;
|
|
25
|
+
tokenToCheck: Token;
|
|
26
|
+
}): -1 | 0 | 1 {
|
|
27
|
+
const tokenToCheckSharesIdsWithA = shareSubgraphId(a, tokenToCheck);
|
|
28
|
+
|
|
29
|
+
const tokenToCheckSharesIdsWithB = shareSubgraphId(b, tokenToCheck);
|
|
30
|
+
|
|
31
|
+
if (tokenToCheckSharesIdsWithA && !tokenToCheckSharesIdsWithB) {
|
|
32
|
+
return -1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!tokenToCheckSharesIdsWithA && tokenToCheckSharesIdsWithB) {
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Filter chains based on provided config
|
|
44
|
+
*
|
|
45
|
+
* Filter order:
|
|
46
|
+
* 1. Chains that are not disabled or coming soon
|
|
47
|
+
* 2. Disabled chains
|
|
48
|
+
* 3. Coming soon chains
|
|
49
|
+
*/
|
|
50
|
+
export function filterChains({
|
|
51
|
+
chains,
|
|
52
|
+
config,
|
|
53
|
+
direction,
|
|
54
|
+
}: {
|
|
55
|
+
chains: ChainData[];
|
|
56
|
+
config: Pick<AppConfig, "disabledChains" | "comingSoonChainIds">;
|
|
57
|
+
direction: "from" | "to";
|
|
58
|
+
}): ChainData[] {
|
|
59
|
+
// filter coming soon chains
|
|
60
|
+
const comingSoonChains = chains.filter((c) =>
|
|
61
|
+
config.comingSoonChainIds?.includes(c.chainId)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// map direction to be a key of config.disabledChains object
|
|
65
|
+
const parsedDirection = direction === "from" ? "source" : "destination";
|
|
66
|
+
// get disabled chains for the direction provided
|
|
67
|
+
const disabledChainsOnDirection = config.disabledChains?.[parsedDirection];
|
|
68
|
+
|
|
69
|
+
// filter disabled chains
|
|
70
|
+
const disabledChains = chains.filter(
|
|
71
|
+
(c) =>
|
|
72
|
+
disabledChainsOnDirection?.includes(c.chainId) &&
|
|
73
|
+
!config.comingSoonChainIds?.includes(c.chainId)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// filter chains that are not disabled or coming soon
|
|
77
|
+
const filteredChains = chains.filter(
|
|
78
|
+
(c) =>
|
|
79
|
+
!config.comingSoonChainIds?.includes(c.chainId) &&
|
|
80
|
+
!disabledChainsOnDirection?.includes(c.chainId)
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return [...filteredChains, ...disabledChains, ...comingSoonChains];
|
|
84
|
+
}
|