@0xsquid/react-hooks 8.11.1-beta-danal.2 → 8.12.1-beta-arc.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.
Files changed (69) hide show
  1. package/dist/core/canton/cantonConnector.d.ts +5 -2
  2. package/dist/core/canton/errors.d.ts +4 -0
  3. package/dist/core/canton/loopCantonConnector.d.ts +38 -0
  4. package/dist/core/client/index.d.ts +0 -1
  5. package/dist/core/constants.d.ts +1 -2
  6. package/dist/core/types/canton.d.ts +37 -3
  7. package/dist/core/types/index.d.ts +1 -1
  8. package/dist/core/types/tokens.d.ts +1 -8
  9. package/dist/core/types/wallet.d.ts +1 -8
  10. package/dist/hooks/chains/useSquidChains.d.ts +0 -1
  11. package/dist/hooks/store/useDepositAddressStore.d.ts +0 -6
  12. package/dist/hooks/swap/useDepositAddress.d.ts +0 -2
  13. package/dist/hooks/tokens/useSquidTokens.d.ts +0 -1
  14. package/dist/hooks/transaction/send/useEstimateSendTransactionGas.d.ts +3 -0
  15. package/dist/hooks/wallet/useMultiChainWallet.d.ts +0 -38
  16. package/dist/hooks/wallet/useWallet.d.ts +0 -42
  17. package/dist/{index-nTqoaD0B.js → index-BBJD55XX.js} +1110 -593
  18. package/dist/index-BBJD55XX.js.map +1 -0
  19. package/dist/{index-ByakOYXP.js → index-CLzjMzS_.js} +1109 -590
  20. package/dist/index-CLzjMzS_.js.map +1 -0
  21. package/dist/{index.es-DtFDz6qc.js → index.es-BVCh4tlV.js} +2 -2
  22. package/dist/{index.es-DtFDz6qc.js.map → index.es-BVCh4tlV.js.map} +1 -1
  23. package/dist/{index.es--AR2n7a0.js → index.es-BiK_wjSb.js} +2 -2
  24. package/dist/{index.es--AR2n7a0.js.map → index.es-BiK_wjSb.js.map} +1 -1
  25. package/dist/index.esm.js +1 -1
  26. package/dist/index.js +3 -5
  27. package/dist/index.js.map +1 -1
  28. package/dist/{secretService-29Ak2qp5.js → secretService-BZ06HTOJ.js} +2 -2
  29. package/dist/{secretService-29Ak2qp5.js.map → secretService-BZ06HTOJ.js.map} +1 -1
  30. package/dist/{secretService-cgr3P1zw.js → secretService-DkE9yz1o.js} +2 -2
  31. package/dist/{secretService-cgr3P1zw.js.map → secretService-DkE9yz1o.js.map} +1 -1
  32. package/dist/server.esm.js +1 -6
  33. package/dist/server.esm.js.map +1 -1
  34. package/dist/server.js +1 -6
  35. package/dist/server.js.map +1 -1
  36. package/dist/services/external/rpcService.d.ts +2 -4
  37. package/dist/services/index.d.ts +0 -1
  38. package/dist/services/internal/arcService.d.ts +7 -0
  39. package/dist/services/internal/assetsService.d.ts +9 -3
  40. package/dist/services/internal/cantonService.d.ts +2 -0
  41. package/dist/services/internal/cantonTransferService.d.ts +3 -3
  42. package/dist/services/internal/configService.d.ts +14 -3
  43. package/dist/services/internal/estimateService.d.ts +13 -3
  44. package/dist/services/internal/searchService.d.ts +1 -2
  45. package/dist/services/internal/tempoService.d.ts +2 -0
  46. package/dist/services/internal/transactionService.d.ts +0 -8
  47. package/dist/services/internal/walletService.d.ts +1 -1
  48. package/dist/{stellarService.client-B__dTUBZ.js → stellarService.client-DtA9zkRt.js} +2 -2
  49. package/dist/{stellarService.client-B__dTUBZ.js.map → stellarService.client-DtA9zkRt.js.map} +1 -1
  50. package/dist/{stellarService.client-D-zkMI5s.js → stellarService.client-fnyXRadt.js} +2 -2
  51. package/dist/{stellarService.client-D-zkMI5s.js.map → stellarService.client-fnyXRadt.js.map} +1 -1
  52. package/dist/tests/arcConfigService.test.d.ts +1 -0
  53. package/dist/tests/arcGasAccounting.test.d.ts +1 -0
  54. package/dist/tests/arcWalletService.test.d.ts +1 -0
  55. package/dist/tests/fixtures/arc.d.ts +8 -0
  56. package/dist/tests/loopCantonConnector.test.d.ts +1 -0
  57. package/dist/tests/loopSdkContract.test.d.ts +1 -0
  58. package/dist/tests/useArcApproval.test.d.ts +1 -0
  59. package/dist/tests/useArcSendEstimate.test.d.ts +1 -0
  60. package/dist/tests/useAutoConnectCanton.test.d.ts +1 -0
  61. package/dist/tests/useCantonRestoration.test.d.ts +1 -0
  62. package/dist/tests/useNativeTokenForChain.test.d.ts +1 -0
  63. package/dist/tests/useSwapArcSelection.test.d.ts +1 -0
  64. package/package.json +3 -2
  65. package/dist/core/types/danal.d.ts +0 -1
  66. package/dist/index-ByakOYXP.js.map +0 -1
  67. package/dist/index-nTqoaD0B.js.map +0 -1
  68. package/dist/services/internal/danalService.d.ts +0 -8
  69. /package/dist/tests/{danalService.test.d.ts → arcAssetsService.test.d.ts} +0 -0
@@ -80,7 +80,6 @@ const suiZeroAddress = "0x0000000000000000000000000000000000000000";
80
80
  const xrplZeroAddress = "rrrrrrrrrrrrrrrrrrrrrhoLvTp";
81
81
  const stellarZeroAddress = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF";
82
82
  const cantonZeroAddress = "none::12200000000000000000000000000000000000000000000000000000000000000000";
83
- const danalZeroAddress = "PCI00000000000000000000000000000000000000000000000000";
84
83
  const chainTypeToZeroAddressMap = {
85
84
  [squidTypes.ChainType.EVM]: viem.zeroAddress,
86
85
  [squidTypes.ChainType.COSMOS]: cosmosZeroAddress,
@@ -90,7 +89,6 @@ const chainTypeToZeroAddressMap = {
90
89
  [squidTypes.ChainType.XRPL]: xrplZeroAddress,
91
90
  [squidTypes.ChainType.STELLAR]: stellarZeroAddress,
92
91
  [squidTypes.ChainType.CANTON]: cantonZeroAddress,
93
- [squidTypes.ChainType.DANAL]: danalZeroAddress,
94
92
  };
95
93
  const nativeEvmTokenAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
96
94
  const nativeCosmosTokenAddress = "uosmo";
@@ -100,7 +98,6 @@ const nativeSuiTokenAddress = "0x00000000000000000000000000000000000000000000000
100
98
  const nativeXrplTokenAddress = "xrp";
101
99
  const nativeStellarTokenAddress = "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA";
102
100
  const nativeCantonTokenAddress = "DSO::1220b1431ef217342db44d516bb9befde802be7d8899637d290895fa58880f19accc";
103
- const nativeDanalTokenAddress = "pci";
104
101
  // by setting slippage to undefined, it's set to "auto"
105
102
  const defaultSlippage = undefined;
106
103
  const destinationAddressResetValue = "null";
@@ -156,6 +153,7 @@ const CHAIN_IDS = {
156
153
  MANTRA: "5888",
157
154
  CITREA: "4114",
158
155
  TEMPO: "4217",
156
+ ARC: "5042",
159
157
  // others
160
158
  BITCOIN: "bitcoin",
161
159
  SOLANA: "solana-mainnet-beta",
@@ -166,7 +164,6 @@ const CHAIN_IDS = {
166
164
  STELLAR: "stellar-mainnet",
167
165
  STELLAR_TESTNET: "stellar-testnet",
168
166
  CANTON: "canton",
169
- DANAL_TESTNET: "danal-testnet",
170
167
  };
171
168
  const chainTypeToDefaultChainIdMap = {
172
169
  [squidTypes.ChainType.EVM]: CHAIN_IDS.ETHEREUM,
@@ -177,7 +174,6 @@ const chainTypeToDefaultChainIdMap = {
177
174
  [squidTypes.ChainType.XRPL]: CHAIN_IDS.XRPL,
178
175
  [squidTypes.ChainType.STELLAR]: CHAIN_IDS.STELLAR,
179
176
  [squidTypes.ChainType.CANTON]: CHAIN_IDS.CANTON,
180
- [squidTypes.ChainType.DANAL]: CHAIN_IDS.DANAL_TESTNET,
181
177
  };
182
178
  const chainTypeToNativeTokenAddressMap = {
183
179
  [squidTypes.ChainType.EVM]: nativeEvmTokenAddress,
@@ -188,7 +184,6 @@ const chainTypeToNativeTokenAddressMap = {
188
184
  [squidTypes.ChainType.XRPL]: nativeXrplTokenAddress,
189
185
  [squidTypes.ChainType.STELLAR]: nativeStellarTokenAddress,
190
186
  [squidTypes.ChainType.CANTON]: nativeCantonTokenAddress,
191
- [squidTypes.ChainType.DANAL]: nativeDanalTokenAddress,
192
187
  };
193
188
  const defaultConfigValues = {
194
189
  integratorId: INTEGRATOR_ID,
@@ -20970,6 +20965,27 @@ const isBitcoinAddressValid = (address) => {
20970
20965
  }
20971
20966
  };
20972
20967
 
20968
+ const arcUsdcTokenAddress = "0x3600000000000000000000000000000000000000";
20969
+ const isArcChain = (chainId) => chainId === CHAIN_IDS.ARC;
20970
+ const isArcUsdcToken = (token) => isArcChain(token?.chainId) &&
20971
+ token?.address.toLowerCase() === arcUsdcTokenAddress;
20972
+ const isArcNativeToken = (token) => isArcChain(token?.chainId) &&
20973
+ token?.address.toLowerCase() === nativeEvmTokenAddress;
20974
+ const normalizeArcTokenAddress = (chainId, address) => isArcChain(chainId) && address?.toLowerCase() === nativeEvmTokenAddress
20975
+ ? arcUsdcTokenAddress
20976
+ : address;
20977
+ const deduplicateArcBalances = (tokens) => {
20978
+ const canonicalChains = new Set(tokens
20979
+ .filter((token) => isArcChain(token.chainId) &&
20980
+ token.address?.toLowerCase() === arcUsdcTokenAddress)
20981
+ .map((token) => token.chainId));
20982
+ return tokens.filter((token) => !(canonicalChains.has(token.chainId) &&
20983
+ token.address?.toLowerCase() === nativeEvmTokenAddress));
20984
+ };
20985
+
20986
+ function getCantonCommandId(memo) {
20987
+ return `canton-${memo}`;
20988
+ }
20973
20989
  /**
20974
20990
  * Format a Canton connector into a Squid {@link CantonWallet}. Shared by the
20975
20991
  * announce-discovery and injected-provider paths.
@@ -21120,51 +21136,16 @@ function cantonInstrumentKey(instrument) {
21120
21136
  * is already a Decimal, not base units).
21121
21137
  */
21122
21138
  function groupCantonHoldingBalances(acsResponse) {
21139
+ return sumCantonHoldingBalances(parseCantonHoldings(acsResponse).filter((holding) => !holding.locked));
21140
+ }
21141
+ function sumCantonHoldingBalances(holdings) {
21123
21142
  const totals = {};
21124
- for (const holding of parseCantonHoldings(acsResponse)) {
21125
- // Locked holdings are not spendable, skip them
21126
- if (holding.locked)
21127
- continue;
21143
+ for (const holding of holdings) {
21128
21144
  const key = cantonInstrumentKey(holding.instrument);
21129
21145
  const total = totals[key] ?? new BigNumber(0);
21130
21146
  totals[key] = total.plus(new BigNumber(holding.amount));
21131
21147
  }
21132
- return Object.fromEntries(Object.entries(totals).map(([key, total]) => [key, total.toString(10)]));
21133
- }
21134
-
21135
- // Matches the backend validation: "PCI" + 50 uppercase-hex chars, case-sensitive.
21136
- const DANAL_ADDRESS_REGEX = /^PCI[0-9A-F]{50}$/;
21137
- function isDanalAddressValid(address) {
21138
- if (!address)
21139
- return false;
21140
- return DANAL_ADDRESS_REGEX.test(address);
21141
- }
21142
- // Fabric transaction id: 64 hex chars, no 0x prefix
21143
- const DANAL_TX_HASH_REGEX = /^[0-9a-fA-F]{64}$/;
21144
- function isDanalTxHashValid(hash) {
21145
- if (!hash)
21146
- return false;
21147
- return DANAL_TX_HASH_REGEX.test(hash);
21148
- }
21149
- function isDanalTransferRequestBody(value) {
21150
- if (typeof value !== "object" || value === null)
21151
- return false;
21152
- const { receiver, amount, memo } = value;
21153
- return (typeof receiver === "string" &&
21154
- receiver.length > 0 &&
21155
- typeof amount === "string" &&
21156
- amount.length > 0 &&
21157
- typeof memo === "string" &&
21158
- memo.length > 0);
21159
- }
21160
- function parseDanalTransferBody(data) {
21161
- try {
21162
- const parsed = JSON.parse(data);
21163
- return isDanalTransferRequestBody(parsed) ? parsed : null;
21164
- }
21165
- catch {
21166
- return null;
21167
- }
21148
+ return Object.fromEntries(Object.entries(totals).map(([key, total]) => [key, total.toFixed()]));
21168
21149
  }
21169
21150
 
21170
21151
  const solanaTokenProgramIdByAddress = {
@@ -21503,15 +21484,6 @@ function isOnChainTxData(squidData) {
21503
21484
  function isDepositAddressDirectTransferRoute(squidData) {
21504
21485
  return squidData.type === squidTypes.SquidDataType.DepositAddressDirectTransfer;
21505
21486
  }
21506
- /**
21507
- * Danal keeps SquidDataType.DepositAddressWithMemo, which XRPL/Stellar use for
21508
- * wallet-signed transactions — so the deposit treatment is gated on the chain,
21509
- * not just the type
21510
- */
21511
- function isDanalDepositRoute(route) {
21512
- return (route.params.fromChain === CHAIN_IDS.DANAL_TESTNET &&
21513
- route.transactionRequest?.type === squidTypes.SquidDataType.DepositAddressWithMemo);
21514
- }
21515
21487
  function getHistoryTransactionId(tx) {
21516
21488
  switch (tx.txType) {
21517
21489
  case exports.HistoryTxType.SWAP:
@@ -22036,11 +22008,6 @@ const chainTypeToTrimLength = {
22036
22008
  start: 3,
22037
22009
  end: 3,
22038
22010
  },
22039
- // PCI0180...E77E
22040
- [squidTypes.ChainType.DANAL]: {
22041
- start: 7,
22042
- end: 4,
22043
- },
22044
22011
  };
22045
22012
  const truncateWithEllipsis = (value, startLength, endLength) => value.length <= startLength + endLength
22046
22013
  ? value
@@ -22089,8 +22056,6 @@ const isWalletAddressValid = (chainData, address) => {
22089
22056
  return isStellarAddressValid(address);
22090
22057
  case squidTypes.ChainType.CANTON:
22091
22058
  return isCantonAddressValid(address);
22092
- case squidTypes.ChainType.DANAL:
22093
- return isDanalAddressValid(address);
22094
22059
  }
22095
22060
  };
22096
22061
  const redirectToExtensionsStore = (wallet) => {
@@ -22254,7 +22219,7 @@ function sortAddressBook(a, b, selectedAddress) {
22254
22219
  return 0;
22255
22220
  }
22256
22221
  const calculateTotalUsdBalanceUSD = (tokens) => {
22257
- const totalUsd = tokens.reduce((total, token) => {
22222
+ const totalUsd = deduplicateArcBalances(tokens).reduce((total, token) => {
22258
22223
  const balance = parseFloat(token.balance);
22259
22224
  const usdPrice = token.usdPrice ?? 0;
22260
22225
  const balanceUsd = balance * usdPrice;
@@ -22375,8 +22340,6 @@ const connectByChainType = async (chainType, wallet, defaultChain, params) => {
22375
22340
  connector,
22376
22341
  },
22377
22342
  });
22378
- case squidTypes.ChainType.DANAL:
22379
- return null;
22380
22343
  }
22381
22344
  };
22382
22345
  const getChainTypesToConnect = (selectedChainTypes, defaultChain, supportedChains) => {
@@ -22501,6 +22464,8 @@ const cancelConnectWallet = (wallet) => {
22501
22464
  return;
22502
22465
  }
22503
22466
  switch (wallet.type) {
22467
+ case squidTypes.ChainType.CANTON:
22468
+ return wallet.connector.cancelConnect?.();
22504
22469
  case squidTypes.ChainType.XRPL:
22505
22470
  return wallet.connector.cancelConnect?.();
22506
22471
  default:
@@ -23066,22 +23031,36 @@ const getFirstAvailableChainId = (config, direction, chains = []) => {
23066
23031
  // if no initial chain ID is found in config, return the first available chain ID
23067
23032
  return availableChains.length > 0 ? availableChains[0].chainId : undefined;
23068
23033
  };
23069
- const fetchHighestBalanceToken = (fetchCachedBalance, chainId) => {
23034
+ const fetchHighestBalanceToken = (fetchCachedBalance, chainId, selectableTokens, direction) => {
23070
23035
  let address = undefined;
23071
23036
  if (fetchCachedBalance) {
23072
23037
  const queryCache = fetchCachedBalance?.queryClient?.getQueryCache();
23038
+ const directionCache = isArcChain(chainId) && direction
23039
+ ? queryCache?.find(keys().allTokensBalance(fetchCachedBalance.address, squidTypes.ChainType.EVM, direction))
23040
+ : undefined;
23073
23041
  // get cached balance from key
23074
- const cache = queryCache?.find(keys().allTokensBalance(fetchCachedBalance?.address, !isNaN(Number(chainId)) ? squidTypes.ChainType.EVM : squidTypes.ChainType.COSMOS));
23075
- // Parse result to TokenWithBalance[]
23076
- const cacheResult = cache?.state.data ?? [];
23042
+ const cache = directionCache?.state.data !== undefined
23043
+ ? directionCache
23044
+ : queryCache?.find(keys().allTokensBalance(fetchCachedBalance?.address, !isNaN(Number(chainId)) ? squidTypes.ChainType.EVM : squidTypes.ChainType.COSMOS));
23045
+ const cacheData = cache?.state.data;
23046
+ const cacheResult = Array.isArray(cacheData)
23047
+ ? cacheData
23048
+ : isArcChain(chainId)
23049
+ ? (cacheData?.tokens ?? [])
23050
+ : [];
23077
23051
  if (cacheResult.length > 0) {
23078
- const tokensWithBalance = cacheResult.filter((t) => t.chainId === chainId && Number(t.balance) > 0);
23052
+ const tokensWithBalance = deduplicateArcBalances(cacheResult).filter((t) => t.chainId === chainId &&
23053
+ Number(t.balance) > 0 &&
23054
+ (!selectableTokens ||
23055
+ selectableTokens.some((token) => token.chainId === t.chainId &&
23056
+ token.address.toLowerCase() ===
23057
+ normalizeArcTokenAddress(t.chainId, t.address)?.toLowerCase())));
23079
23058
  if (tokensWithBalance.length > 0) {
23080
23059
  // Return the token with the highest balance
23081
23060
  // TODO: At the moment we're not taking into account usd price
23082
23061
  const tokenWithHighestBalance = tokensWithBalance.reduce((prev, current) => Number(prev.balance) > Number(current.balance) ? prev : current);
23083
23062
  if (tokenWithHighestBalance) {
23084
- address = tokenWithHighestBalance.address;
23063
+ address = normalizeArcTokenAddress(tokenWithHighestBalance.chainId, tokenWithHighestBalance.address);
23085
23064
  }
23086
23065
  }
23087
23066
  }
@@ -23092,6 +23071,21 @@ const findNativeTokenForChainType = (tokens) => {
23092
23071
  return tokens.find((t) => t.address.toLowerCase() ===
23093
23072
  chainTypeToNativeTokenAddressMap[t.type].toLowerCase());
23094
23073
  };
23074
+ const getTokenAddressForSelection = ({ tokens, config, chainId, address, direction, excludeToken, }) => {
23075
+ if (!isArcChain(chainId))
23076
+ return address;
23077
+ const normalizedAddress = normalizeArcTokenAddress(chainId, address);
23078
+ const excludedAddress = normalizeArcTokenAddress(excludeToken?.chainId, excludeToken?.address);
23079
+ if (!chainId ||
23080
+ !normalizedAddress ||
23081
+ (excludeToken?.chainId === chainId &&
23082
+ normalizedAddress.toLowerCase() === excludedAddress?.toLowerCase()) ||
23083
+ !getSupportedChainIdsForDirection([chainId], config, direction).includes(chainId)) {
23084
+ return undefined;
23085
+ }
23086
+ return filterViewableTokens(tokens, config, direction).find((token) => token.chainId === chainId &&
23087
+ token.address.toLowerCase() === normalizedAddress.toLowerCase())?.address;
23088
+ };
23095
23089
  /**
23096
23090
  * Returns the default token address for a specific chain and direction based on config.
23097
23091
  * If no default tokens are set in config, returns the native token for the specified chain
@@ -23109,6 +23103,25 @@ const getInitialOrDefaultTokenAddressForChain = ({ tokens, config, chainId, dire
23109
23103
  const supportedChainIds = getSupportedChainIdsForDirection([chainId], config, direction);
23110
23104
  if (!supportedChainIds.includes(chainId))
23111
23105
  return;
23106
+ if (isArcChain(chainId)) {
23107
+ const selectAddress = (address) => getTokenAddressForSelection({
23108
+ tokens,
23109
+ config,
23110
+ chainId,
23111
+ address,
23112
+ direction,
23113
+ excludeToken,
23114
+ });
23115
+ const selectableTokens = tokens.filter((token) => token.chainId === chainId &&
23116
+ !isArcNativeToken(token) &&
23117
+ selectAddress(token.address) !== undefined);
23118
+ return (selectAddress(config.defaultTokensPerChain?.find((token) => token.chainId === chainId)
23119
+ ?.address) ??
23120
+ selectAddress(getInitialTokenAddressForChain({ tokens, config, chainId, direction })) ??
23121
+ selectAddress(fetchHighestBalanceToken(fetchCachedBalance, chainId, selectableTokens, direction)) ??
23122
+ selectAddress(arcUsdcTokenAddress) ??
23123
+ selectableTokens[0]?.address);
23124
+ }
23112
23125
  const configDefaultTokenForChain = config.defaultTokensPerChain?.find((token) => token.chainId == chainId);
23113
23126
  // get default token from config
23114
23127
  if (configDefaultTokenForChain &&
@@ -23152,6 +23165,23 @@ const getInitialTokenAddressForChain = ({ chainId, config, direction, tokens, un
23152
23165
  const initialAsset = config.initialAssets?.[direction];
23153
23166
  if (!initialAsset)
23154
23167
  return undefined;
23168
+ if (isArcChain(chainId)) {
23169
+ const address = isNamedTokenConfig(initialAsset)
23170
+ ? tokens.find((token) => token.chainId === chainId &&
23171
+ !isArcNativeToken(token) &&
23172
+ token.symbol.toLowerCase() === initialAsset.symbol?.toLowerCase())?.address
23173
+ : initialAsset.chainId === chainId
23174
+ ? initialAsset.address
23175
+ : undefined;
23176
+ return getTokenAddressForSelection({
23177
+ tokens,
23178
+ config,
23179
+ chainId,
23180
+ address,
23181
+ direction,
23182
+ excludeToken: { chainId, address: unavailableTokenAddress },
23183
+ });
23184
+ }
23155
23185
  if (isNamedTokenConfig(initialAsset)) {
23156
23186
  if (!initialAsset.symbol)
23157
23187
  return undefined;
@@ -23361,12 +23391,44 @@ const handleDestinationAddressOnSwapChange = ({ fromChainId, fromTokenAddress, t
23361
23391
  });
23362
23392
  let destinationTokenAddress = toChainId && !toTokenAddress
23363
23393
  ? defaultToAddress
23364
- : toTokenAddress ?? swapRoute?.toTokenAddress;
23394
+ : (toTokenAddress ?? swapRoute?.toTokenAddress);
23365
23395
  const lowerCaseDestinationTokenAddress = destinationTokenAddress?.toLowerCase();
23366
23396
  if (destinationTokensFiltered.find((t) => t.address === lowerCaseDestinationTokenAddress) === undefined) {
23367
23397
  destinationTokenAddress = defaultToAddress;
23368
23398
  }
23369
- return lowerCaseDestinationTokenAddress;
23399
+ return isArcChain(toChainId ?? swapRoute?.toChainId)
23400
+ ? destinationTokenAddress?.toLowerCase()
23401
+ : lowerCaseDestinationTokenAddress;
23402
+ };
23403
+ const normalizeSwapParamsForSelection = ({ swapParams, tokens, chains, config, }) => {
23404
+ const fromTokenAddress = getTokenAddressForSelection({
23405
+ tokens,
23406
+ config,
23407
+ chainId: swapParams.fromChainId,
23408
+ address: swapParams.fromTokenAddress,
23409
+ direction: "from",
23410
+ });
23411
+ const fromToken = tokens.find((token) => token.chainId === swapParams.fromChainId &&
23412
+ token.address.toLowerCase() === fromTokenAddress?.toLowerCase());
23413
+ const toTokenAddress = getTokenAddressForSelection({
23414
+ tokens: isArcChain(swapParams.toChainId)
23415
+ ? filterTokensForDestination({
23416
+ tokens,
23417
+ selectedDestinationChain: chains.find((chain) => chain.chainId === swapParams.toChainId),
23418
+ selectedSourceToken: fromToken,
23419
+ removeSourceToken: true,
23420
+ })
23421
+ : tokens,
23422
+ config,
23423
+ chainId: swapParams.toChainId,
23424
+ address: swapParams.toTokenAddress,
23425
+ direction: "to",
23426
+ excludeToken: {
23427
+ chainId: swapParams.fromChainId,
23428
+ address: fromTokenAddress,
23429
+ },
23430
+ });
23431
+ return { ...swapParams, fromTokenAddress, toTokenAddress };
23370
23432
  };
23371
23433
  /**
23372
23434
  * When user changes something from the SwapView
@@ -23378,10 +23440,19 @@ const handleDestinationAddressOnSwapChange = ({ fromChainId, fromTokenAddress, t
23378
23440
  * If only destination token is changed but source is not, and they were the same, then source token updates to something else
23379
23441
  *
23380
23442
  */
23381
- const getNewSwapParamsFromInput = ({ inputParams, initialSwapRoute, tokens, chains, config, queryClient, connectedSourceAddress, }) => {
23382
- const { fromChainId, fromTokenAddress, toChainId, toTokenAddress, destinationAddress, fallbackAddress, depositRefundAddress, } = inputParams;
23443
+ const getNewSwapParamsFromInput = ({ inputParams, initialSwapRoute: storedSwapRoute, tokens, chains, config, queryClient, connectedSourceAddress, }) => {
23444
+ const initialSwapRoute = storedSwapRoute
23445
+ ? {
23446
+ ...storedSwapRoute,
23447
+ fromTokenAddress: normalizeArcTokenAddress(storedSwapRoute.fromChainId, storedSwapRoute.fromTokenAddress),
23448
+ toTokenAddress: normalizeArcTokenAddress(storedSwapRoute.toChainId, storedSwapRoute.toTokenAddress),
23449
+ }
23450
+ : undefined;
23451
+ const { fromChainId, toChainId, destinationAddress, fallbackAddress, depositRefundAddress, } = inputParams;
23383
23452
  const srcChainId = fromChainId ?? initialSwapRoute?.fromChainId;
23384
23453
  const destinationChainId = toChainId ?? initialSwapRoute?.toChainId;
23454
+ const fromTokenAddress = normalizeArcTokenAddress(srcChainId, inputParams.fromTokenAddress);
23455
+ const toTokenAddress = normalizeArcTokenAddress(destinationChainId, inputParams.toTokenAddress);
23385
23456
  const defaultFromAddress = getInitialOrDefaultTokenAddressForChain({
23386
23457
  tokens,
23387
23458
  config,
@@ -23413,7 +23484,7 @@ const getNewSwapParamsFromInput = ({ inputParams, initialSwapRoute, tokens, chai
23413
23484
  toTokenAddress === initialSwapRoute?.fromTokenAddress;
23414
23485
  const sourceTokenAddress = sameNewDestAndInitialSource || onlySourceChainIdChanged
23415
23486
  ? defaultFromAddress
23416
- : fromTokenAddress ?? initialSwapRoute?.fromTokenAddress;
23487
+ : (fromTokenAddress ?? initialSwapRoute?.fromTokenAddress);
23417
23488
  let destinationTokenAddress = undefined;
23418
23489
  if (!sameNewSourceAndInitialDest) {
23419
23490
  if (onlySourceChainIdChanged) {
@@ -23457,15 +23528,20 @@ const getNewSwapParamsFromInput = ({ inputParams, initialSwapRoute, tokens, chai
23457
23528
  const newDepositRefundAddress = sourceChainChanged
23458
23529
  ? undefined
23459
23530
  : depositRefundAddress || initialSwapRoute?.depositRefundAddress;
23460
- return {
23461
- fromChainId: srcChainId,
23462
- fromTokenAddress: sourceTokenAddress,
23463
- toChainId: destinationChainId,
23464
- toTokenAddress: destinationTokenAddress,
23465
- destinationAddress: newDestinationAddress,
23466
- fallbackAddress: newFallbackAddress,
23467
- depositRefundAddress: newDepositRefundAddress,
23468
- };
23531
+ return normalizeSwapParamsForSelection({
23532
+ swapParams: {
23533
+ fromChainId: srcChainId,
23534
+ fromTokenAddress: sourceTokenAddress,
23535
+ toChainId: destinationChainId,
23536
+ toTokenAddress: destinationTokenAddress,
23537
+ destinationAddress: newDestinationAddress,
23538
+ fallbackAddress: newFallbackAddress,
23539
+ depositRefundAddress: newDepositRefundAddress,
23540
+ },
23541
+ tokens,
23542
+ chains,
23543
+ config,
23544
+ });
23469
23545
  };
23470
23546
  /* Sorts tokens based on the following order:
23471
23547
  * 1. Native token on chain ID "1" (Ethereum) comes first.
@@ -23523,8 +23599,10 @@ const sortAllTokens = (tokenA, tokenB) => {
23523
23599
  return 0;
23524
23600
  };
23525
23601
  const findToken = (tokens, chainId, address) => tokens.find((t) => t.chainId === chainId && t.address === address);
23526
- const findNativeToken = (tokens, chain) => tokens.find((t) => t.symbol.toUpperCase() === chain.nativeCurrency.symbol.toUpperCase() &&
23527
- t.chainId == chain.chainId);
23602
+ const findNativeToken = (tokens, chain) => tokens.find((t) => t.chainId === chain.chainId &&
23603
+ (isArcChain(chain.chainId)
23604
+ ? isArcNativeToken(t)
23605
+ : t.symbol.toUpperCase() === chain.nativeCurrency.symbol.toUpperCase()));
23528
23606
  const normalizeIbcAddress = (address) => {
23529
23607
  if (!address.toLowerCase().startsWith("ibc/")) {
23530
23608
  return address;
@@ -23594,9 +23672,7 @@ function selectGroupDefaultInfo(tokens) {
23594
23672
  const groupTokensBySymbol = (tokens, chainType) => {
23595
23673
  const groups = {};
23596
23674
  // Filter tokens by chain type if specified
23597
- const filteredTokens = chainType
23598
- ? tokens.filter((token) => token.type === chainType)
23599
- : tokens;
23675
+ const filteredTokens = deduplicateArcBalances(chainType ? tokens.filter((token) => token.type === chainType) : tokens);
23600
23676
  // First pass: group by coingeckoId or symbol
23601
23677
  filteredTokens.forEach((token) => {
23602
23678
  const groupKey = token.coingeckoId || token.symbol.split(".")[0];
@@ -23672,7 +23748,7 @@ const groupTokensBySymbol = (tokens, chainType) => {
23672
23748
  */
23673
23749
  const groupTokensByChainId = (tokens, chains) => {
23674
23750
  const groups = {};
23675
- tokens.forEach((token) => {
23751
+ deduplicateArcBalances(tokens).forEach((token) => {
23676
23752
  const chainData = chains.find((chain) => chain.chainId === token.chainId);
23677
23753
  const groupKey = token.chainId;
23678
23754
  if (!groups[groupKey]) {
@@ -23720,6 +23796,8 @@ const filterViewableTokens = (tokens, config, direction) => {
23720
23796
  { chainId: CHAIN_IDS.DCHAIN, address: nativeEvmTokenAddress },
23721
23797
  ];
23722
23798
  return (tokens.filter((token) => {
23799
+ if (isArcNativeToken(token))
23800
+ return false;
23723
23801
  if (direction) {
23724
23802
  const parsedDirection = getParsedDirection(direction);
23725
23803
  const availableTokensForDirection = config.availableTokens?.[parsedDirection];
@@ -23735,7 +23813,7 @@ const filterViewableTokens = (tokens, config, direction) => {
23735
23813
  };
23736
23814
  const getSecretNetworkBalances = async (chainData, cosmosAddress, squidTokens, keplrTypeWallet) => {
23737
23815
  const squidSecretTokens = squidTokens.filter((t) => t.chainId === CHAIN_IDS.SECRET);
23738
- const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-29Ak2qp5.js'); });
23816
+ const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-BZ06HTOJ.js'); });
23739
23817
  return fetchAllSecretBalances(chainData, cosmosAddress, squidSecretTokens, keplrTypeWallet);
23740
23818
  };
23741
23819
  function getTokenAssetsKey(token) {
@@ -25316,8 +25394,7 @@ const searchTokens = (query, tokens, chains) => {
25316
25394
  .filter((chain) => chain.nativeCurrency.symbol.toLowerCase() === query.toLowerCase() ||
25317
25395
  chain.nativeCurrency.name.toLowerCase().includes(query.toLowerCase()) ||
25318
25396
  chain.networkName.toLowerCase().includes(query.toLowerCase()) ||
25319
- (chain.axelarChainName?.toLowerCase().includes(query.toLowerCase()) ??
25320
- false))
25397
+ chain.axelarChainName.toLowerCase().includes(query.toLowerCase()))
25321
25398
  .map((chain) => tokens.find((token) => token.chainId === chain.chainId &&
25322
25399
  token.address.toLowerCase() === nativeEvmTokenAddress))
25323
25400
  .filter((token) => token !== undefined);
@@ -25730,7 +25807,7 @@ const useSquidTokens = (direction) => {
25730
25807
  config.availableChains,
25731
25808
  direction,
25732
25809
  ]);
25733
- const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, danalTokens, } = React.useMemo(() => {
25810
+ const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = React.useMemo(() => {
25734
25811
  return tokens?.reduce((acc, token) => {
25735
25812
  switch (token.type) {
25736
25813
  case squidTypes.ChainType.EVM:
@@ -25757,9 +25834,6 @@ const useSquidTokens = (direction) => {
25757
25834
  case squidTypes.ChainType.CANTON:
25758
25835
  acc.cantonTokens.push(token);
25759
25836
  break;
25760
- case squidTypes.ChainType.DANAL:
25761
- acc.danalTokens.push(token);
25762
- break;
25763
25837
  }
25764
25838
  return acc;
25765
25839
  }, {
@@ -25771,7 +25845,6 @@ const useSquidTokens = (direction) => {
25771
25845
  xrplTokens: [],
25772
25846
  stellarTokens: [],
25773
25847
  cantonTokens: [],
25774
- danalTokens: [],
25775
25848
  });
25776
25849
  }, [tokens]);
25777
25850
  const findToken = React.useCallback((address, chainId) => {
@@ -25790,7 +25863,6 @@ const useSquidTokens = (direction) => {
25790
25863
  xrplTokens,
25791
25864
  stellarTokens,
25792
25865
  cantonTokens,
25793
- danalTokens,
25794
25866
  };
25795
25867
  };
25796
25868
 
@@ -26327,7 +26399,6 @@ const chainTypeToRefetchInterval = {
26327
26399
  [squidTypes.ChainType.XRPL]: 1_000,
26328
26400
  [squidTypes.ChainType.STELLAR]: 1_000,
26329
26401
  [squidTypes.ChainType.CANTON]: 1_000,
26330
- [squidTypes.ChainType.DANAL]: 1_000,
26331
26402
  };
26332
26403
  /**
26333
26404
  * Returns the status refetch interval of a Send transaction
@@ -26594,7 +26665,7 @@ const useSquidChains = (direction) => {
26594
26665
  const chain = findChain(chainId);
26595
26666
  return chain?.chainType;
26596
26667
  }, [findChain]);
26597
- const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains, danalChains, } = React.useMemo(() => {
26668
+ const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains, } = React.useMemo(() => {
26598
26669
  return chains.reduce((acc, chain) => {
26599
26670
  switch (chain.chainType) {
26600
26671
  case squidTypes.ChainType.EVM:
@@ -26615,9 +26686,6 @@ const useSquidChains = (direction) => {
26615
26686
  case squidTypes.ChainType.CANTON:
26616
26687
  acc.cantonChains.push(chain);
26617
26688
  break;
26618
- case squidTypes.ChainType.DANAL:
26619
- acc.danalChains.push(chain);
26620
- break;
26621
26689
  }
26622
26690
  return acc;
26623
26691
  }, {
@@ -26627,7 +26695,6 @@ const useSquidChains = (direction) => {
26627
26695
  xrplChains: [],
26628
26696
  stellarChains: [],
26629
26697
  cantonChains: [],
26630
- danalChains: [],
26631
26698
  });
26632
26699
  }, [chains]);
26633
26700
  const { supportedSourceChains, supportedDestinationChains } = React.useMemo(() => {
@@ -26649,7 +26716,6 @@ const useSquidChains = (direction) => {
26649
26716
  xrplChains,
26650
26717
  stellarChains,
26651
26718
  cantonChains,
26652
- danalChains,
26653
26719
  getChainType,
26654
26720
  findChain,
26655
26721
  };
@@ -26963,7 +27029,343 @@ const useBitcoinContext = () => {
26963
27029
  return context;
26964
27030
  };
26965
27031
 
26966
- const REQUEST_TIMEOUT_MS = 60_000;
27032
+ const vParty = (party) => ({ party });
27033
+ const vText = (text) => ({ text });
27034
+ const vNumeric = (numeric) => ({ numeric });
27035
+ const vContractId = (contractId) => ({
27036
+ contractId,
27037
+ });
27038
+ const vList = (elements) => ({
27039
+ list: { elements },
27040
+ });
27041
+ const vRecord = (fields) => ({
27042
+ record: { fields: fields.map(([label, value]) => ({ label, value })) },
27043
+ });
27044
+ const vTextMap = (entries) => ({ textMap: { entries } });
27045
+ const vTimestamp = (isoString) => ({
27046
+ timestamp: String(Math.floor(new Date(isoString).getTime() * 1000)),
27047
+ });
27048
+ function isRecord(value) {
27049
+ return typeof value === "object" && value !== null && !Array.isArray(value);
27050
+ }
27051
+ function normalizeChoiceContextValue(value) {
27052
+ if (typeof value === "string")
27053
+ return value;
27054
+ if (!isRecord(value) || typeof value.tag !== "string")
27055
+ return null;
27056
+ switch (value.tag) {
27057
+ case "AV_Text":
27058
+ case "AV_ContractId":
27059
+ return typeof value.value === "string"
27060
+ ? { tag: value.tag, value: value.value }
27061
+ : null;
27062
+ case "AV_Bool":
27063
+ return typeof value.value === "boolean"
27064
+ ? { tag: value.tag, value: value.value }
27065
+ : null;
27066
+ case "AV_List":
27067
+ if (!Array.isArray(value.value))
27068
+ return null;
27069
+ return {
27070
+ tag: value.tag,
27071
+ value: value.value.flatMap((entry) => {
27072
+ const normalized = normalizeChoiceContextValue(entry);
27073
+ return normalized ? [normalized] : [];
27074
+ }),
27075
+ };
27076
+ default: {
27077
+ const inner = value.value;
27078
+ if (typeof inner === "string" ||
27079
+ typeof inner === "number" ||
27080
+ typeof inner === "boolean" ||
27081
+ inner === null) {
27082
+ return { tag: value.tag, value: inner };
27083
+ }
27084
+ if (Array.isArray(inner)) {
27085
+ return {
27086
+ tag: value.tag,
27087
+ value: inner.flatMap((entry) => {
27088
+ const normalized = normalizeChoiceContextValue(entry);
27089
+ return normalized ? [normalized] : [];
27090
+ }),
27091
+ };
27092
+ }
27093
+ if (!isRecord(inner))
27094
+ return null;
27095
+ const normalizedObject = {};
27096
+ for (const [key, entry] of Object.entries(inner)) {
27097
+ if (typeof entry !== "string" &&
27098
+ typeof entry !== "number" &&
27099
+ typeof entry !== "boolean" &&
27100
+ entry !== null) {
27101
+ return null;
27102
+ }
27103
+ normalizedObject[key] = entry;
27104
+ }
27105
+ return { tag: value.tag, value: normalizedObject };
27106
+ }
27107
+ }
27108
+ }
27109
+ function stringifyChoiceContextValue(value) {
27110
+ return typeof value === "string" ? value : String(value);
27111
+ }
27112
+ function choiceContextValueToAnyValue(value) {
27113
+ if (typeof value === "string") {
27114
+ return {
27115
+ variant: { constructor: "AV_Text", value: vText(value) },
27116
+ };
27117
+ }
27118
+ switch (value.tag) {
27119
+ case "AV_ContractId":
27120
+ if (typeof value.value !== "string") {
27121
+ throw new TypeError("AV_ContractId values must be strings");
27122
+ }
27123
+ return {
27124
+ variant: { constructor: value.tag, value: vContractId(value.value) },
27125
+ };
27126
+ case "AV_Bool":
27127
+ if (typeof value.value !== "boolean") {
27128
+ throw new TypeError("AV_Bool values must be booleans");
27129
+ }
27130
+ return {
27131
+ variant: { constructor: value.tag, value: { bool: value.value } },
27132
+ };
27133
+ case "AV_List":
27134
+ if (!Array.isArray(value.value)) {
27135
+ throw new TypeError("AV_List values must be arrays");
27136
+ }
27137
+ return {
27138
+ variant: {
27139
+ constructor: value.tag,
27140
+ value: vList(value.value.map(choiceContextValueToAnyValue)),
27141
+ },
27142
+ };
27143
+ default:
27144
+ return {
27145
+ variant: {
27146
+ constructor: value.tag,
27147
+ value: vText(stringifyChoiceContextValue(value.value)),
27148
+ },
27149
+ };
27150
+ }
27151
+ }
27152
+
27153
+ /** Metadata key used by Splice token-standard transfers to carry our Squid orderhash memo. */
27154
+ const CANTON_MEMO_KEY = "splice.lfdecentralizedtrust.org/reason";
27155
+ /** Interface id for the transfer factory contract returned by the token registry. */
27156
+ const TRANSFER_FACTORY_INTERFACE_ID = "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory";
27157
+ /** Choice exercised on the transfer factory to create the token-standard transfer. */
27158
+ const TRANSFER_FACTORY_CHOICE = "TransferFactory_Transfer";
27159
+ /** CIP-056 (non-Amulet) tokens expose a registrar parameterized by admin party. */
27160
+ const CIP056_REGISTRAR_BASE = "https://api.utilities.digitalasset.com/api/token-standard/v0/registrars";
27161
+ const TRANSFER_REQUESTED_AT_SKEW_MS = 60_000;
27162
+ const TRANSFER_EXECUTE_WINDOW_MS = 24 * 60 * 60 * 1000;
27163
+ function resolveCantonRegistryUrl(instrument) {
27164
+ if (instrument.id === CANTON_AMULET_INSTRUMENT_ID) {
27165
+ return CANTON_AMULET_REGISTRY_URL;
27166
+ }
27167
+ return `${CIP056_REGISTRAR_BASE}/${encodeURIComponent(instrument.admin)}`;
27168
+ }
27169
+ /**
27170
+ * Build the plain JSON arguments expected by the registry transfer-factory
27171
+ * endpoint before it adds choice context and disclosed contracts.
27172
+ */
27173
+ function buildTransferChoiceArgs({ sender, receiver, amount, instrument, inputHoldingCids, memo, nowMs, }) {
27174
+ return {
27175
+ expectedAdmin: instrument.admin,
27176
+ transfer: {
27177
+ sender,
27178
+ receiver,
27179
+ amount,
27180
+ instrumentId: { admin: instrument.admin, id: instrument.id },
27181
+ requestedAt: new Date(nowMs - TRANSFER_REQUESTED_AT_SKEW_MS).toISOString(),
27182
+ executeBefore: new Date(nowMs + TRANSFER_EXECUTE_WINDOW_MS).toISOString(),
27183
+ inputHoldingCids,
27184
+ meta: { values: { [CANTON_MEMO_KEY]: memo } },
27185
+ },
27186
+ extraArgs: { context: { values: {} }, meta: { values: {} } },
27187
+ };
27188
+ }
27189
+ /**
27190
+ * Canton's Holding contracts follow Bitcoin-style UTXOs model.
27191
+ *
27192
+ * Pick Holding contract ids covering `amount` for the instrument: prefer a
27193
+ * single exact-amount holding, else accumulate largest-first.
27194
+ */
27195
+ function selectInputHoldingCids(holdings, instrument, amount) {
27196
+ const spendableHoldings = holdings.filter((h) => !h.locked &&
27197
+ h.instrument.admin === instrument.admin &&
27198
+ h.instrument.id === instrument.id);
27199
+ const target = new BigNumber(amount);
27200
+ const exact = spendableHoldings.find((h) => new BigNumber(h.amount).eq(target));
27201
+ if (exact)
27202
+ return [exact.contractId];
27203
+ const sorted = [...spendableHoldings].sort((a, b) => new BigNumber(b.amount).comparedTo(new BigNumber(a.amount)));
27204
+ const selected = [];
27205
+ let sum = new BigNumber(0);
27206
+ for (const holding of sorted) {
27207
+ selected.push(holding.contractId);
27208
+ sum = sum.plus(holding.amount);
27209
+ if (sum.gte(target))
27210
+ break;
27211
+ }
27212
+ if (sum.lt(target)) {
27213
+ throw new Error("Insufficient Canton holdings to cover transfer amount");
27214
+ }
27215
+ return selected;
27216
+ }
27217
+ async function fetchInputHoldingCids(connector, party, instrument, amount) {
27218
+ const ledgerEnd = await connector.ledgerApi({
27219
+ requestMethod: "get",
27220
+ resource: "/v2/state/ledger-end",
27221
+ });
27222
+ const activeContracts = await connector.ledgerApi({
27223
+ requestMethod: "post",
27224
+ resource: "/v2/state/active-contracts",
27225
+ body: buildHoldingsAcsRequestBody(party, String(ledgerEnd.offset)),
27226
+ });
27227
+ return selectInputHoldingCids(parseCantonHoldings(activeContracts), instrument, amount);
27228
+ }
27229
+ /**
27230
+ * Ask the token registry for the transfer factory contract and extra context
27231
+ * required to exercise its transfer choice.
27232
+ */
27233
+ async function fetchTransferFactory(registryUrl, choiceArgs) {
27234
+ const response = await fetch(`${registryUrl}/registry/transfer-instruction/v1/transfer-factory`, {
27235
+ method: "POST",
27236
+ headers: { "Content-Type": "application/json" },
27237
+ body: JSON.stringify({
27238
+ choiceArguments: choiceArgs,
27239
+ excludeDebugFields: true,
27240
+ }),
27241
+ });
27242
+ if (!response.ok) {
27243
+ throw new Error(`Canton transfer-factory request failed (${response.status})`);
27244
+ }
27245
+ return response.json();
27246
+ }
27247
+ /**
27248
+ * Convert transfer choice args into strict Daml Value JSON for the wallet's
27249
+ * prepareExecute ExerciseCommand.
27250
+ */
27251
+ function buildPrepareChoiceArgument(choiceArgs) {
27252
+ const contextValues = Object.entries(choiceArgs.extraArgs.context?.values ?? {})
27253
+ .map(([key, value]) => {
27254
+ const normalized = normalizeChoiceContextValue(value);
27255
+ return normalized
27256
+ ? { key, value: choiceContextValueToAnyValue(normalized) }
27257
+ : undefined;
27258
+ })
27259
+ .filter((entry) => Boolean(entry));
27260
+ return vRecord([
27261
+ ["expectedAdmin", vParty(choiceArgs.expectedAdmin)],
27262
+ [
27263
+ "transfer",
27264
+ vRecord([
27265
+ ["sender", vParty(choiceArgs.transfer.sender)],
27266
+ ["receiver", vParty(choiceArgs.transfer.receiver)],
27267
+ ["amount", vNumeric(choiceArgs.transfer.amount)],
27268
+ [
27269
+ "instrumentId",
27270
+ vRecord([
27271
+ ["admin", vParty(choiceArgs.transfer.instrumentId.admin)],
27272
+ ["id", vText(choiceArgs.transfer.instrumentId.id)],
27273
+ ]),
27274
+ ],
27275
+ ["requestedAt", vTimestamp(choiceArgs.transfer.requestedAt)],
27276
+ ["executeBefore", vTimestamp(choiceArgs.transfer.executeBefore)],
27277
+ [
27278
+ "inputHoldingCids",
27279
+ vList(choiceArgs.transfer.inputHoldingCids.map(vContractId)),
27280
+ ],
27281
+ [
27282
+ "meta",
27283
+ vRecord([
27284
+ [
27285
+ "values",
27286
+ vTextMap(Object.entries(choiceArgs.transfer.meta.values).map(([key, value]) => ({ key, value: vText(value) }))),
27287
+ ],
27288
+ ]),
27289
+ ],
27290
+ ]),
27291
+ ],
27292
+ [
27293
+ "extraArgs",
27294
+ vRecord([
27295
+ ["context", vRecord([["values", vTextMap(contextValues)]])],
27296
+ ["meta", vRecord([["values", vTextMap([])]])],
27297
+ ]),
27298
+ ],
27299
+ ]);
27300
+ }
27301
+ /**
27302
+ * Convert a registry disclosed contract into the wallet submission shape,
27303
+ * dropping debug fields and entries without a usable createdEventBlob.
27304
+ */
27305
+ function toDisclosedContract(raw) {
27306
+ if (typeof raw.createdEventBlob !== "string")
27307
+ return null;
27308
+ return {
27309
+ createdEventBlob: raw.createdEventBlob,
27310
+ contractId: typeof raw.contractId === "string" ? raw.contractId : undefined,
27311
+ synchronizerId: typeof raw.synchronizerId === "string" ? raw.synchronizerId : undefined,
27312
+ };
27313
+ }
27314
+ /**
27315
+ * Build a Splice token-standard transfer of `amount` of `token` from `sender`
27316
+ * to `receiver`, carrying `memo`, ready to hand to a wallet's prepareExecute.
27317
+ * Returns the full prepareExecute params (commandId + commands + actAs +
27318
+ * disclosedContracts).
27319
+ */
27320
+ async function buildCantonTransfer(connector, { sender, receiver, amount, token, memo, nowMs, }) {
27321
+ if (!token.originalAddress) {
27322
+ throw new Error("Canton token originalAddress is required");
27323
+ }
27324
+ const instrument = parseCantonInstrument(token.originalAddress);
27325
+ if (!instrument) {
27326
+ throw new Error(`Invalid Canton token address: ${token.originalAddress}`);
27327
+ }
27328
+ const registryUrl = resolveCantonRegistryUrl(instrument);
27329
+ const inputHoldingCids = await fetchInputHoldingCids(connector, sender, instrument, amount);
27330
+ const choiceArgs = buildTransferChoiceArgs({
27331
+ sender,
27332
+ receiver,
27333
+ amount,
27334
+ instrument,
27335
+ inputHoldingCids,
27336
+ memo,
27337
+ nowMs,
27338
+ });
27339
+ const factory = await fetchTransferFactory(registryUrl, choiceArgs);
27340
+ const choiceContextData = factory.choiceContext.choiceContextData;
27341
+ if (!choiceContextData) {
27342
+ throw new Error("Transfer factory choice context is missing");
27343
+ }
27344
+ choiceArgs.extraArgs.context = choiceContextData;
27345
+ const disclosedContracts = (factory.choiceContext?.disclosedContracts ?? []).flatMap((raw) => {
27346
+ const disclosedContract = toDisclosedContract(raw);
27347
+ return disclosedContract ? [disclosedContract] : [];
27348
+ });
27349
+ // Command ID: client-supplied identifier used to deduplicate submissions
27350
+ const commandId = getCantonCommandId(memo);
27351
+ return {
27352
+ commandId,
27353
+ commands: [
27354
+ {
27355
+ ExerciseCommand: {
27356
+ templateId: TRANSFER_FACTORY_INTERFACE_ID,
27357
+ contractId: factory.factoryId,
27358
+ choice: TRANSFER_FACTORY_CHOICE,
27359
+ choiceArgument: buildPrepareChoiceArgument(choiceArgs),
27360
+ },
27361
+ },
27362
+ ],
27363
+ actAs: [sender],
27364
+ disclosedContracts,
27365
+ };
27366
+ }
27367
+
27368
+ const REQUEST_TIMEOUT_MS$1 = 60_000;
26967
27369
  function pickAddress(accounts) {
26968
27370
  return (accounts.find((a) => a.primary) ?? accounts[0])?.partyId;
26969
27371
  }
@@ -26972,6 +27374,7 @@ function pickAddress(accounts) {
26972
27374
  * (bound to its announce `target`) and exposes the lifecycle methods.
26973
27375
  */
26974
27376
  class CantonBrowserConnector {
27377
+ kind = "cip103";
26975
27378
  providerId;
26976
27379
  name;
26977
27380
  target;
@@ -26986,7 +27389,7 @@ class CantonBrowserConnector {
26986
27389
  client ??
26987
27390
  dapp.createCantonClient(new dapp.WindowTransport(win, {
26988
27391
  target: this.target,
26989
- timeout: REQUEST_TIMEOUT_MS,
27392
+ timeout: REQUEST_TIMEOUT_MS$1,
26990
27393
  }));
26991
27394
  }
26992
27395
  get accounts() {
@@ -27032,6 +27435,28 @@ class CantonBrowserConnector {
27032
27435
  this._accounts = await this.client.listAccounts();
27033
27436
  return this._accounts;
27034
27437
  }
27438
+ async getBalances(partyId) {
27439
+ const { offset } = await this.ledgerApi({
27440
+ requestMethod: "get",
27441
+ resource: "/v2/state/ledger-end",
27442
+ });
27443
+ const contracts = await this.ledgerApi({
27444
+ requestMethod: "post",
27445
+ resource: "/v2/state/active-contracts",
27446
+ body: buildHoldingsAcsRequestBody(partyId, String(offset)),
27447
+ });
27448
+ return groupCantonHoldingBalances(contracts);
27449
+ }
27450
+ async transfer(params) {
27451
+ const payload = await buildCantonTransfer(this, params);
27452
+ const result = await this.prepareExecuteAndWait(payload);
27453
+ const updateId = result.tx?.payload?.updateId ?? "";
27454
+ return {
27455
+ status: "completed",
27456
+ commandId: getCantonCommandId(params.memo),
27457
+ updateId,
27458
+ };
27459
+ }
27035
27460
  on(event, handler) {
27036
27461
  this.listeners[event].add(handler);
27037
27462
  }
@@ -27138,6 +27563,313 @@ function discoverInjectedCantonWallets(win) {
27138
27563
  });
27139
27564
  }
27140
27565
 
27566
+ class CantonConnectionCancelledError extends Error {
27567
+ code = 4001;
27568
+ constructor() {
27569
+ super("Wallet connection cancelled");
27570
+ }
27571
+ }
27572
+
27573
+ const REQUEST_TIMEOUT_MS = 5 * 60_000;
27574
+ const OVERLAY_ID = "loop-sdk-connect-overlay";
27575
+ class LoopSubmissionUnknownError extends Error {
27576
+ constructor(cause) {
27577
+ super("Loop transfer outcome is unknown. Check Loop before trying again.", {
27578
+ cause,
27579
+ });
27580
+ }
27581
+ }
27582
+ function parseLoopTransferUpdate(value, commandId) {
27583
+ if (!value || typeof value !== "object")
27584
+ return undefined;
27585
+ const response = value;
27586
+ if (response.command_id !== commandId)
27587
+ return undefined;
27588
+ if (response.status === "failed" || response.error) {
27589
+ const error = response.error;
27590
+ return {
27591
+ status: "failed",
27592
+ commandId,
27593
+ message: typeof error?.error_message === "string"
27594
+ ? error.error_message
27595
+ : "Loop transaction failed",
27596
+ };
27597
+ }
27598
+ if (typeof response.update_id === "string" && response.update_id) {
27599
+ return { status: "completed", commandId, updateId: response.update_id };
27600
+ }
27601
+ return undefined;
27602
+ }
27603
+ class LoopCantonConnector {
27604
+ loadSdk;
27605
+ kind = "loop";
27606
+ providerId = "canton-loop";
27607
+ name = "Loop";
27608
+ target = undefined;
27609
+ module;
27610
+ initialization;
27611
+ provider;
27612
+ pendingConnection;
27613
+ connectionStart = Promise.resolve();
27614
+ transfers = new Map();
27615
+ listeners = { connect: new Set(), disconnect: new Set() };
27616
+ constructor(loadSdk = () => import('@fivenorth/loop-sdk')) {
27617
+ this.loadSdk = loadSdk;
27618
+ }
27619
+ get accounts() {
27620
+ return this.provider
27621
+ ? [{ partyId: this.provider.party_id, primary: true }]
27622
+ : [];
27623
+ }
27624
+ get address() {
27625
+ return this.provider?.party_id;
27626
+ }
27627
+ connect() {
27628
+ return this.startConnection(true);
27629
+ }
27630
+ autoConnect() {
27631
+ return this.startConnection(false);
27632
+ }
27633
+ cancelConnect() {
27634
+ if (!this.pendingConnection)
27635
+ return;
27636
+ this.rejectConnection(new CantonConnectionCancelledError());
27637
+ this.module?.loop.logout();
27638
+ }
27639
+ async disconnect() {
27640
+ this.cancelConnect();
27641
+ this.module?.loop.logout();
27642
+ this.provider = undefined;
27643
+ this.emit("disconnect");
27644
+ }
27645
+ async getAccounts() {
27646
+ const provider = this.requireProvider();
27647
+ try {
27648
+ const account = await provider.getAccount();
27649
+ if (account.party_id !== provider.party_id) {
27650
+ await this.disconnect();
27651
+ throw new Error("Loop account changed. Connect again.");
27652
+ }
27653
+ return this.accounts;
27654
+ }
27655
+ catch (error) {
27656
+ if (this.module && error instanceof this.module.UnauthorizedError) {
27657
+ await this.disconnect();
27658
+ }
27659
+ throw error;
27660
+ }
27661
+ }
27662
+ async getBalances(partyId) {
27663
+ const provider = this.requireProvider();
27664
+ if (partyId !== provider.party_id) {
27665
+ throw new Error("Loop balances require the connected account");
27666
+ }
27667
+ const holdings = await provider.getHolding();
27668
+ return sumCantonHoldingBalances(holdings.map((holding) => {
27669
+ const amount = new BigNumber(holding.total_unlocked_coin);
27670
+ if (!amount.isFinite() || amount.isNegative()) {
27671
+ throw new Error("Loop returned an invalid balance");
27672
+ }
27673
+ return {
27674
+ instrument: holding.instrument_id,
27675
+ amount: holding.total_unlocked_coin,
27676
+ };
27677
+ }));
27678
+ }
27679
+ transfer(params) {
27680
+ const commandId = getCantonCommandId(params.memo);
27681
+ const existing = this.transfers.get(commandId);
27682
+ if (existing)
27683
+ return existing;
27684
+ const request = this.submitTransfer(params, commandId).catch((error) => {
27685
+ if (!(error instanceof LoopSubmissionUnknownError))
27686
+ this.transfers.delete(commandId);
27687
+ throw error;
27688
+ });
27689
+ this.transfers.set(commandId, request);
27690
+ return request;
27691
+ }
27692
+ on(event, handler) {
27693
+ this.listeners[event].add(handler);
27694
+ }
27695
+ off(event, handler) {
27696
+ this.listeners[event].delete(handler);
27697
+ }
27698
+ emit(event, ...args) {
27699
+ this.listeners[event].forEach((handler) => handler(...args));
27700
+ }
27701
+ initialize() {
27702
+ if (!this.initialization) {
27703
+ this.initialization = this.loadSdk()
27704
+ .then((module) => {
27705
+ module.loop.init({
27706
+ appName: SQUID_METADATA.name,
27707
+ network: "mainnet",
27708
+ onAccept: (provider) => this.acceptConnection(provider),
27709
+ onReject: () => {
27710
+ this.rejectConnection(new CantonConnectionCancelledError());
27711
+ this.provider = undefined;
27712
+ this.emit("disconnect");
27713
+ },
27714
+ });
27715
+ this.module = module;
27716
+ })
27717
+ .catch((error) => {
27718
+ this.initialization = undefined;
27719
+ throw error;
27720
+ });
27721
+ }
27722
+ return this.initialization;
27723
+ }
27724
+ startConnection(interactive) {
27725
+ if (this.pendingConnection)
27726
+ return this.pendingConnection.promise;
27727
+ let resolve;
27728
+ let reject;
27729
+ const promise = new Promise((accept, decline) => {
27730
+ resolve = accept;
27731
+ reject = decline;
27732
+ });
27733
+ const timer = setTimeout(() => {
27734
+ this.rejectConnection(new Error("Loop connection timed out"));
27735
+ this.module?.loop.logout();
27736
+ }, REQUEST_TIMEOUT_MS);
27737
+ let observer;
27738
+ const pending = {
27739
+ promise,
27740
+ resolve,
27741
+ reject,
27742
+ cleanup: () => {
27743
+ clearTimeout(timer);
27744
+ observer?.disconnect();
27745
+ },
27746
+ };
27747
+ this.pendingConnection = pending;
27748
+ this.connectionStart = this.connectionStart
27749
+ .then(async () => {
27750
+ await this.initialize();
27751
+ if (this.pendingConnection !== pending)
27752
+ return;
27753
+ if (interactive && typeof MutationObserver !== "undefined") {
27754
+ let sawOverlay = false;
27755
+ observer = new MutationObserver(() => {
27756
+ const overlay = document.getElementById(OVERLAY_ID);
27757
+ if (overlay)
27758
+ sawOverlay = true;
27759
+ if (sawOverlay && !overlay && this.pendingConnection === pending) {
27760
+ this.cancelConnect();
27761
+ }
27762
+ });
27763
+ observer.observe(document.body, { childList: true });
27764
+ }
27765
+ const sdk = this.module.loop;
27766
+ if (interactive)
27767
+ await sdk.connect();
27768
+ else
27769
+ await sdk.autoConnect();
27770
+ if (!interactive && this.pendingConnection === pending) {
27771
+ this.rejectConnection(new Error("No saved Loop connection"));
27772
+ }
27773
+ else if (interactive &&
27774
+ this.pendingConnection !== pending &&
27775
+ !this.provider) {
27776
+ sdk.logout();
27777
+ }
27778
+ })
27779
+ .catch((error) => {
27780
+ if (this.pendingConnection === pending)
27781
+ this.rejectConnection(error);
27782
+ });
27783
+ return promise;
27784
+ }
27785
+ acceptConnection(provider) {
27786
+ const pending = this.pendingConnection;
27787
+ if (!pending)
27788
+ return;
27789
+ if (!provider.party_id) {
27790
+ this.rejectConnection(new Error("Loop returned no account"));
27791
+ return;
27792
+ }
27793
+ this.provider = provider;
27794
+ this.pendingConnection = undefined;
27795
+ pending.cleanup();
27796
+ pending.resolve({ address: provider.party_id, accounts: this.accounts });
27797
+ this.emit("connect", provider.party_id);
27798
+ }
27799
+ rejectConnection(error) {
27800
+ const pending = this.pendingConnection;
27801
+ if (!pending)
27802
+ return;
27803
+ this.pendingConnection = undefined;
27804
+ pending.cleanup();
27805
+ pending.reject(error);
27806
+ }
27807
+ requireProvider() {
27808
+ if (!this.provider)
27809
+ throw new Error("Loop wallet is not connected");
27810
+ return this.provider;
27811
+ }
27812
+ async submitTransfer(params, commandId) {
27813
+ const provider = this.requireProvider();
27814
+ if (params.sender !== provider.party_id)
27815
+ throw new Error("Loop account does not match the sender");
27816
+ const instrument = params.token.originalAddress
27817
+ ? parseCantonInstrument(params.token.originalAddress)
27818
+ : null;
27819
+ if (!instrument)
27820
+ throw new Error("Canton token originalAddress is required");
27821
+ if (!new BigNumber(params.amount).isFinite() ||
27822
+ !new BigNumber(params.amount).isPositive()) {
27823
+ throw new Error("Canton transfer amount must be positive");
27824
+ }
27825
+ const payload = await provider.connection.prepareTransfer(provider.getAuthToken(), {
27826
+ recipient: params.receiver,
27827
+ amount: params.amount,
27828
+ instrument: {
27829
+ instrument_admin: instrument.admin,
27830
+ instrument_id: instrument.id,
27831
+ },
27832
+ memo: params.memo,
27833
+ requested_at: new Date(params.nowMs - 60_000).toISOString(),
27834
+ execute_before: new Date(params.nowMs + 24 * 60 * 60_000).toISOString(),
27835
+ });
27836
+ if (this.provider !== provider)
27837
+ throw new Error("Loop account disconnected before submission");
27838
+ let response;
27839
+ let timer;
27840
+ try {
27841
+ const submission = { ...payload, commandId };
27842
+ const submitted = provider.submitAndWaitForTransaction(submission, {
27843
+ requestTimeout: REQUEST_TIMEOUT_MS,
27844
+ });
27845
+ response = await Promise.race([
27846
+ submitted,
27847
+ new Promise((_, reject) => {
27848
+ timer = setTimeout(() => reject(new Error("Loop submission timed out")), REQUEST_TIMEOUT_MS);
27849
+ }),
27850
+ ]);
27851
+ }
27852
+ catch (error) {
27853
+ if (this.module && error instanceof this.module.RejectRequestError) {
27854
+ throw Object.assign(new Error("Loop transaction rejected"), {
27855
+ code: 4001,
27856
+ });
27857
+ }
27858
+ throw new LoopSubmissionUnknownError(error);
27859
+ }
27860
+ finally {
27861
+ clearTimeout(timer);
27862
+ }
27863
+ const update = parseLoopTransferUpdate(response, commandId);
27864
+ if (update?.status === "failed")
27865
+ throw new Error(update.message);
27866
+ if (!update)
27867
+ throw new LoopSubmissionUnknownError();
27868
+ return update;
27869
+ }
27870
+ }
27871
+ const loopCantonConnector = new LoopCantonConnector();
27872
+
27141
27873
  /**
27142
27874
  * Discovers Canton browser-extension wallets and maps each to a
27143
27875
  * {@link CantonWallet}: CIP-103 announce-protocol wallets (on mount and on
@@ -27184,7 +27916,19 @@ function useCantonWallets() {
27184
27916
  return;
27185
27917
  const announced = (await discoverCantonProviders()).map(toAnnouncedWallet);
27186
27918
  const injected = discoverInjectedCantonWallets().map(toInjectedWallet);
27187
- setWallets(mergeInjectedCantonWallets(announced, injected));
27919
+ const loopWallet = {
27920
+ ...formatCantonWallet({
27921
+ connectorId: "canton-loop",
27922
+ name: "Loop",
27923
+ icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/master/wallets/canton-loop.svg",
27924
+ connector: loopCantonConnector,
27925
+ }),
27926
+ links: { website: "https://cantonloop.com" },
27927
+ isInstalled: () => false,
27928
+ skipInstallCheck: true,
27929
+ isMobile: true,
27930
+ };
27931
+ setWallets(mergeInjectedCantonWallets(announced, [...injected, loopWallet]));
27188
27932
  }, [toAnnouncedWallet, toInjectedWallet]);
27189
27933
  React.useEffect(() => {
27190
27934
  refresh();
@@ -27215,7 +27959,9 @@ function useCanton() {
27215
27959
  // Try to reconnect silently first (existing session, no popup)
27216
27960
  result = await wallet.connector.autoConnect();
27217
27961
  }
27218
- catch {
27962
+ catch (error) {
27963
+ if (error instanceof CantonConnectionCancelledError)
27964
+ throw error;
27219
27965
  // Otherwise prompt the wallet to connect
27220
27966
  result = await wallet.connector.connect();
27221
27967
  }
@@ -27237,8 +27983,7 @@ function useCanton() {
27237
27983
  wallet: recentCantonWallet,
27238
27984
  address,
27239
27985
  });
27240
- // eslint-disable-next-line react-hooks/exhaustive-deps
27241
- }, [wallets.map((w) => w.connectorId), recentCantonWalletId]);
27986
+ }, [wallets, recentCantonWalletId, setConnectedWallet]);
27242
27987
  React.useEffect(() => {
27243
27988
  const wallet = connectedCantonWallet.wallet;
27244
27989
  if (!wallet) {
@@ -27495,7 +28240,7 @@ function useStellarWallets() {
27495
28240
  try {
27496
28241
  const { allowAllModules: initializeAllModules } = await import('@creit.tech/stellar-wallets-kit');
27497
28242
  const { LedgerModule } = await import('@creit.tech/stellar-wallets-kit/modules/ledger.module.mjs');
27498
- const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-D-zkMI5s.js'); });
28243
+ const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-fnyXRadt.js'); });
27499
28244
  const modules = [...initializeAllModules(), new LedgerModule()];
27500
28245
  const promises = modules.map(async (module) => {
27501
28246
  const isAvailable = await module.isAvailable();
@@ -28074,6 +28819,11 @@ const useWallet = () => {
28074
28819
  }
28075
28820
  }
28076
28821
  catch (error) {
28822
+ const connecting = useWalletStore.getState().connectingWalletState;
28823
+ if (connecting.wallet !== wallet ||
28824
+ connecting.status !== exports.ConnectingWalletStatus.CONNECTING) {
28825
+ return false;
28826
+ }
28077
28827
  console.error("Failed to connect wallet", error);
28078
28828
  walletConnectionError(wallet, error);
28079
28829
  return false;
@@ -28305,8 +29055,6 @@ const useMultiChainWallet = (chain) => {
28305
29055
  chainType: chain.chainType,
28306
29056
  }),
28307
29057
  };
28308
- case squidTypes.ChainType.DANAL:
28309
- return {};
28310
29058
  }
28311
29059
  }, [
28312
29060
  chain?.chainType,
@@ -28420,12 +29168,27 @@ const useMultiChainWallet = (chain) => {
28420
29168
  };
28421
29169
 
28422
29170
  const useSwap = () => {
28423
- const { initialAssets, defaultTokensPerChain, disabledChains, availableChains, } = useConfigStore((state) => state.config);
29171
+ const { initialAssets, defaultTokensPerChain, disabledChains, availableChains, availableTokens, } = useConfigStore((state) => state.config);
28424
29172
  const fromPrice = useTransactionStore((state) => state.fromPrice);
28425
- const { swapRoute } = useSwapRoutePersistStore();
29173
+ const { swapRoute: storedSwapRoute } = useSwapRoutePersistStore();
28426
29174
  const { tokens } = useSquidTokens();
28427
29175
  const queryClient = reactQuery.useQueryClient();
28428
29176
  const { chains, supportedDestinationChains, supportedSourceChains } = useSquidChains();
29177
+ const swapRoute = React.useMemo(() => storedSwapRoute
29178
+ ? normalizeSwapParamsForSelection({
29179
+ swapParams: storedSwapRoute,
29180
+ tokens,
29181
+ chains,
29182
+ config: { disabledChains, availableChains, availableTokens },
29183
+ })
29184
+ : undefined, [
29185
+ storedSwapRoute,
29186
+ tokens,
29187
+ chains,
29188
+ disabledChains,
29189
+ availableChains,
29190
+ availableTokens,
29191
+ ]);
28429
29192
  const sourceChainId = React.useMemo(() => swapRoute?.fromChainId ??
28430
29193
  (initialAssets?.from
28431
29194
  ? resolveChainIdFromAsset(initialAssets.from, supportedSourceChains)
@@ -28436,13 +29199,23 @@ const useSwap = () => {
28436
29199
  : undefined), [swapRoute?.toChainId, supportedDestinationChains, initialAssets?.to]);
28437
29200
  const fromChain = React.useMemo(() => supportedSourceChains.find((c) => c.chainId === swapRoute?.fromChainId), [swapRoute?.fromChainId, supportedSourceChains]);
28438
29201
  const toChain = React.useMemo(() => supportedDestinationChains.find((c) => c.chainId === swapRoute?.toChainId), [swapRoute?.toChainId, supportedDestinationChains]);
28439
- const fromTokens = React.useMemo(() => getTokensForChain(tokens, sourceChainId), [tokens, sourceChainId]);
29202
+ const fromTokens = React.useMemo(() => {
29203
+ const chainTokens = getTokensForChain(tokens, sourceChainId);
29204
+ return isArcChain(sourceChainId)
29205
+ ? filterViewableTokens(chainTokens, { availableTokens }, "from")
29206
+ : chainTokens;
29207
+ }, [tokens, sourceChainId, availableTokens]);
28440
29208
  const fromToken = React.useMemo(() => fromTokens.find((t) => t.address.toLowerCase() === swapRoute?.fromTokenAddress?.toLowerCase()), [fromTokens, swapRoute?.fromTokenAddress]);
28441
- const toTokens = React.useMemo(() => filterTokensForDestination({
28442
- tokens: getTokensForChain(tokens, destChainId),
28443
- selectedDestinationChain: toChain,
28444
- selectedSourceToken: fromToken,
28445
- }), [destChainId, toChain, fromToken, tokens]);
29209
+ const toTokens = React.useMemo(() => {
29210
+ const destinationTokens = filterTokensForDestination({
29211
+ tokens: getTokensForChain(tokens, destChainId),
29212
+ selectedDestinationChain: toChain,
29213
+ selectedSourceToken: fromToken,
29214
+ });
29215
+ return isArcChain(destChainId)
29216
+ ? filterViewableTokens(destinationTokens, { availableTokens }, "to")
29217
+ : destinationTokens;
29218
+ }, [destChainId, toChain, fromToken, tokens, availableTokens]);
28446
29219
  const toToken = React.useMemo(() => toTokens.find((t) => t.address.toLowerCase() === swapRoute?.toTokenAddress?.toLowerCase()), [toTokens, swapRoute?.toTokenAddress]);
28447
29220
  const isSameChain = sourceChainId === destChainId;
28448
29221
  const tokenItems = React.useMemo(() => ({ from: fromTokens, to: toTokens }), [fromTokens, toTokens]);
@@ -28490,6 +29263,7 @@ const useSwap = () => {
28490
29263
  defaultTokensPerChain,
28491
29264
  disabledChains,
28492
29265
  availableChains,
29266
+ availableTokens,
28493
29267
  },
28494
29268
  queryClient,
28495
29269
  connectedSourceAddress: connectedSourceAddress.address,
@@ -28529,6 +29303,7 @@ const useSwap = () => {
28529
29303
  defaultTokensPerChain,
28530
29304
  disabledChains,
28531
29305
  availableChains,
29306
+ availableTokens,
28532
29307
  initialAssets,
28533
29308
  queryClient,
28534
29309
  swapRoute,
@@ -28584,6 +29359,10 @@ const useSwap = () => {
28584
29359
  },
28585
29360
  direction: "to",
28586
29361
  });
29362
+ if ([defaultFromChainId, defaultToChainId].some((chainId) => isArcChain(chainId) &&
29363
+ !tokens.some((token) => token.chainId === chainId && !isArcNativeToken(token)))) {
29364
+ return;
29365
+ }
28587
29366
  const fromTokenAddress = getInitialOrDefaultTokenAddressForChain({
28588
29367
  tokens,
28589
29368
  config: {
@@ -28591,6 +29370,7 @@ const useSwap = () => {
28591
29370
  defaultTokensPerChain,
28592
29371
  disabledChains,
28593
29372
  availableChains,
29373
+ availableTokens,
28594
29374
  },
28595
29375
  chainId: defaultFromChainId,
28596
29376
  direction: "from",
@@ -28602,6 +29382,7 @@ const useSwap = () => {
28602
29382
  defaultTokensPerChain,
28603
29383
  disabledChains,
28604
29384
  availableChains,
29385
+ availableTokens,
28605
29386
  },
28606
29387
  chainId: defaultToChainId,
28607
29388
  direction: "to",
@@ -28626,6 +29407,7 @@ const useSwap = () => {
28626
29407
  initialAssets,
28627
29408
  disabledChains,
28628
29409
  availableChains,
29410
+ availableTokens,
28629
29411
  defaultTokensPerChain,
28630
29412
  tokens,
28631
29413
  ]);
@@ -29145,8 +29927,6 @@ async function createClient(chain) {
29145
29927
  return new StellarRpcClient(chain.rpc);
29146
29928
  case squidTypes.ChainType.CANTON:
29147
29929
  return null;
29148
- case squidTypes.ChainType.DANAL:
29149
- return null;
29150
29930
  }
29151
29931
  }
29152
29932
 
@@ -30161,8 +30941,7 @@ const getAllStellarTokensBalance = async (userAddress, stellarTokens, stellarCha
30161
30941
  };
30162
30942
  /**
30163
30943
  * Canton balances are private: they can only be read with a wallet-authorized
30164
- * `ledgerApi` session (the connected wallet supplies a JWT that can read as the
30165
- * party). So balance is fetchable only when a Canton wallet is connected, and
30944
+ * session. Balance is fetchable only when a Canton wallet is connected, and
30166
30945
  * only for that wallet's party — there's no address-only read.
30167
30946
  */
30168
30947
  const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
@@ -30171,30 +30950,14 @@ const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
30171
30950
  const instrument = parseCantonInstrument(token.originalAddress);
30172
30951
  if (!instrument)
30173
30952
  return "0";
30174
- const ledgerEnd = await cantonConnector.ledgerApi({ requestMethod: "get", resource: "/v2/state/ledger-end" });
30175
- const activeContracts = await cantonConnector.ledgerApi({
30176
- requestMethod: "post",
30177
- resource: "/v2/state/active-contracts",
30178
- body: buildHoldingsAcsRequestBody(userAddress, String(ledgerEnd.offset)),
30179
- });
30180
- const grouped = groupCantonHoldingBalances(activeContracts);
30953
+ const grouped = await cantonConnector.getBalances(userAddress);
30181
30954
  return grouped[cantonInstrumentKey(instrument)] ?? "0";
30182
30955
  };
30183
30956
  /**
30184
- * Fetch balances for all Canton tokens in one ACS query. Canton balances are
30185
- * private, so this requires a connected wallet's authorized `ledgerApi`.
30957
+ * Fetch balances for all Canton tokens through the connected wallet.
30186
30958
  */
30187
30959
  async function getAllCantonTokensBalance({ connector, partyId, tokens, }) {
30188
- const ledgerEnd = await connector.ledgerApi({
30189
- requestMethod: "get",
30190
- resource: "/v2/state/ledger-end",
30191
- });
30192
- const activeContracts = await connector.ledgerApi({
30193
- requestMethod: "post",
30194
- resource: "/v2/state/active-contracts",
30195
- body: buildHoldingsAcsRequestBody(partyId, String(ledgerEnd.offset)),
30196
- });
30197
- const grouped = groupCantonHoldingBalances(activeContracts);
30960
+ const grouped = await connector.getBalances(partyId);
30198
30961
  return tokens.map((token) => {
30199
30962
  const instrument = token.originalAddress
30200
30963
  ? parseCantonInstrument(token.originalAddress)
@@ -30413,7 +31176,7 @@ const useCantonBalance = ({ chain, token, userAddress, enabled = true, refreshIn
30413
31176
  };
30414
31177
 
30415
31178
  function useNativeTokenForChain(chain) {
30416
- const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, danalTokens, } = useSquidTokens();
31179
+ const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens();
30417
31180
  const getTokensForChainType = () => {
30418
31181
  if (!chain?.chainType)
30419
31182
  return [];
@@ -30434,21 +31197,20 @@ function useNativeTokenForChain(chain) {
30434
31197
  return stellarTokens;
30435
31198
  case squidTypes.ChainType.CANTON:
30436
31199
  return cantonTokens;
30437
- case squidTypes.ChainType.DANAL:
30438
- return danalTokens;
30439
31200
  }
30440
31201
  };
31202
+ const tokens = getTokensForChainType();
30441
31203
  const nativeTokenForChainType = React.useMemo(() => {
30442
31204
  if (!chain)
30443
31205
  return undefined;
30444
- return findNativeToken(getTokensForChainType(), chain);
30445
- }, [chain]);
31206
+ return findNativeToken(tokens, chain);
31207
+ }, [chain, tokens]);
30446
31208
  return { nativeToken: nativeTokenForChainType };
30447
31209
  }
30448
31210
 
30449
31211
  const useEvmNativeBalance = ({ address, chain, }) => {
30450
31212
  const { isChainTypeConnected } = useWallet();
30451
- const { data: nativeEvmBalance, isLoading } = wagmi.useBalance({
31213
+ const { data: nativeEvmBalance, isLoading, isError, } = wagmi.useBalance({
30452
31214
  address: address,
30453
31215
  chainId: Number(chain?.chainId),
30454
31216
  query: {
@@ -30460,6 +31222,8 @@ const useEvmNativeBalance = ({ address, chain, }) => {
30460
31222
  },
30461
31223
  });
30462
31224
  const balance = React.useMemo(() => {
31225
+ if (isArcChain(chain?.chainId) && isError)
31226
+ return undefined;
30463
31227
  if (nativeEvmBalance?.decimals != null && nativeEvmBalance?.value != null) {
30464
31228
  if (chain?.chainId === CHAIN_IDS.HEDERA) {
30465
31229
  return {
@@ -30473,7 +31237,12 @@ const useEvmNativeBalance = ({ address, chain, }) => {
30473
31237
  };
30474
31238
  }
30475
31239
  return;
30476
- }, [nativeEvmBalance?.decimals, nativeEvmBalance?.value, chain?.chainId]);
31240
+ }, [
31241
+ nativeEvmBalance?.decimals,
31242
+ nativeEvmBalance?.value,
31243
+ chain?.chainId,
31244
+ isError,
31245
+ ]);
30477
31246
  return {
30478
31247
  balance,
30479
31248
  isLoading,
@@ -30696,9 +31465,6 @@ const useNativeBalance = (chain) => {
30696
31465
  case squidTypes.ChainType.CANTON:
30697
31466
  balance = nativeCantonBalance;
30698
31467
  break;
30699
- case squidTypes.ChainType.DANAL:
30700
- balance = undefined;
30701
- break;
30702
31468
  }
30703
31469
  const balanceFormatted = !!balance
30704
31470
  ? formatBNToReadable(balance.value, balance.decimals)
@@ -30738,8 +31504,6 @@ const useNativeBalance = (chain) => {
30738
31504
  return isStellarLoading;
30739
31505
  case squidTypes.ChainType.CANTON:
30740
31506
  return isCantonLoading;
30741
- case squidTypes.ChainType.DANAL:
30742
- return false;
30743
31507
  }
30744
31508
  }, [
30745
31509
  chain?.chainType,
@@ -30965,7 +31729,7 @@ function hederaWalletConnect(parameters) {
30965
31729
  const optionalChains = config.chains.map((x) => x.id);
30966
31730
  if (!optionalChains.length)
30967
31731
  return;
30968
- const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-DtFDz6qc.js'); });
31732
+ const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-BVCh4tlV.js'); });
30969
31733
  const rawProvider = await EthereumProvider.init({
30970
31734
  ...restParameters,
30971
31735
  disableProviderPing: true,
@@ -31199,8 +31963,6 @@ const useSigner = ({ chain }) => {
31199
31963
  return isStellarSignerReady;
31200
31964
  case squidTypes.ChainType.CANTON:
31201
31965
  return isCantonSignerReady;
31202
- case squidTypes.ChainType.DANAL:
31203
- return false;
31204
31966
  }
31205
31967
  }, [
31206
31968
  chain?.chainType,
@@ -31653,12 +32415,8 @@ const useSendTransactionStore = zustand.create((set, get) => ({
31653
32415
  const useDepositAddressStore = zustand.create((set) => ({
31654
32416
  deposit: null,
31655
32417
  selectedPaymentMethod: "connectedWallet",
31656
- userDepositTxId: null,
31657
32418
  setDeposit: (data) => {
31658
- set({ deposit: data, userDepositTxId: null });
31659
- },
31660
- setUserDepositTxId: (txId) => {
31661
- set({ userDepositTxId: txId });
32419
+ set({ deposit: data });
31662
32420
  },
31663
32421
  setPaymentMethod: (method) => {
31664
32422
  set({ selectedPaymentMethod: method });
@@ -31670,7 +32428,7 @@ function useDepositAddress(squidRoute) {
31670
32428
  selectedPaymentMethod: state.selectedPaymentMethod,
31671
32429
  depositAddress: state.deposit?.depositAddress,
31672
32430
  }));
31673
- const { setDeposit, setPaymentMethod, deposit, userDepositTxId, setUserDepositTxId, } = useDepositAddressStore();
32431
+ const { setDeposit, setPaymentMethod, deposit } = useDepositAddressStore();
31674
32432
  const { squid } = useSquidStore();
31675
32433
  const { fromChain } = useSwap();
31676
32434
  const isAvailableAsPaymentMethod = React.useMemo(() => {
@@ -31680,13 +32438,11 @@ function useDepositAddress(squidRoute) {
31680
32438
  CHAIN_IDS.BITCOIN,
31681
32439
  CHAIN_IDS.SOLANA,
31682
32440
  CHAIN_IDS.CANTON,
31683
- CHAIN_IDS.DANAL_TESTNET,
31684
32441
  ];
31685
32442
  return chainsSupportingDepositAddress.includes(fromChain.chainId);
31686
32443
  }, [fromChain?.chainId]);
31687
- const routeProvidesDirectDepositAddress = (!!squidRoute?.transactionRequest &&
31688
- isDepositAddressDirectTransferRoute(squidRoute.transactionRequest)) ||
31689
- (!!squidRoute && isDanalDepositRoute(squidRoute));
32444
+ const routeProvidesDirectDepositAddress = !!squidRoute?.transactionRequest &&
32445
+ isDepositAddressDirectTransferRoute(squidRoute.transactionRequest);
31690
32446
  const swapWillGenerateDepositAddress = isChainflipDepositRoute(squidRoute) || routeProvidesDirectDepositAddress;
31691
32447
  const paymentMethod = isAvailableAsPaymentMethod
31692
32448
  ? selectedPaymentMethod
@@ -31712,19 +32468,6 @@ function useDepositAddress(squidRoute) {
31712
32468
  });
31713
32469
  return;
31714
32470
  }
31715
- else if (isDanalDepositRoute(route)) {
31716
- const transferBody = parseDanalTransferBody(route.transactionRequest.data);
31717
- if (!transferBody) {
31718
- throw new Error("Could not parse Danal transfer request body");
31719
- }
31720
- setDeposit({
31721
- amount: transferBody.amount,
31722
- depositAddress: route.transactionRequest.target,
31723
- statusTrackingId: transferBody.memo,
31724
- memo: transferBody.memo,
31725
- });
31726
- return;
31727
- }
31728
32471
  else if (isDepositAddressDirectTransferRoute(route.transactionRequest)) {
31729
32472
  // Canton case
31730
32473
  const orderHash = route.transactionRequest.data;
@@ -31747,8 +32490,6 @@ function useDepositAddress(squidRoute) {
31747
32490
  depositAddress,
31748
32491
  closeDepositChannel,
31749
32492
  depositData: deposit,
31750
- userDepositTxId,
31751
- setUserDepositTxId,
31752
32493
  };
31753
32494
  }
31754
32495
 
@@ -31776,7 +32517,7 @@ const useUrlSwapParams = () => {
31776
32517
 
31777
32518
  const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, queryOptions, }) => {
31778
32519
  const { evmChains, cosmosChains, suiChains, xrplChains, stellarChains } = useSquidChains(direction);
31779
- const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, danalTokens, } = useSquidTokens(direction);
32520
+ const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens(direction);
31780
32521
  const { keplrTypeWallet } = useCosmosContext();
31781
32522
  const cantonConnector = useWalletStore((store) => store.connectedWalletsByChainType[squidTypes.ChainType.CANTON]?.wallet?.connector);
31782
32523
  const placeholderData = React.useMemo(() => {
@@ -31789,7 +32530,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31789
32530
  [squidTypes.ChainType.XRPL]: xrplTokens.map((t) => ({ ...t, balance: "0" })),
31790
32531
  [squidTypes.ChainType.STELLAR]: stellarTokens.map((t) => ({ ...t, balance: "0" })),
31791
32532
  [squidTypes.ChainType.CANTON]: cantonTokens.map((t) => ({ ...t, balance: "0" })),
31792
- [squidTypes.ChainType.DANAL]: danalTokens.map((t) => ({ ...t, balance: "0" })),
31793
32533
  };
31794
32534
  if (!chainType) {
31795
32535
  // Return all tokens with zero balance
@@ -31812,7 +32552,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31812
32552
  xrplTokens,
31813
32553
  stellarTokens,
31814
32554
  cantonTokens,
31815
- danalTokens,
31816
32555
  ]);
31817
32556
  const isQueryEnabled = React.useMemo(() => {
31818
32557
  // Respect the queryOptions.enabled override if provided
@@ -31837,8 +32576,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31837
32576
  return stellarTokens.length > 0;
31838
32577
  case squidTypes.ChainType.CANTON:
31839
32578
  return cantonTokens.length > 0;
31840
- case squidTypes.ChainType.DANAL:
31841
- return danalTokens.length > 0;
31842
32579
  }
31843
32580
  }, [
31844
32581
  chainType,
@@ -31852,7 +32589,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31852
32589
  xrplTokens.length,
31853
32590
  stellarTokens.length,
31854
32591
  cantonTokens.length,
31855
- danalTokens.length,
31856
32592
  ]);
31857
32593
  const query = reactQuery.useQuery(keys().allTokensBalance(address, chainType, direction), async () => {
31858
32594
  // Return zero balances if no address
@@ -31977,12 +32713,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
31977
32713
  direction,
31978
32714
  queryOptions,
31979
32715
  });
31980
- const danalBalancesQuery = useAllTokensWithBalanceForChainType({
31981
- chainType: squidTypes.ChainType.DANAL,
31982
- address: connectedAddresses?.[squidTypes.ChainType.DANAL],
31983
- direction,
31984
- queryOptions,
31985
- });
31986
32716
  // Create a map of chain type to balance query results
31987
32717
  const balanceQueries = React.useMemo(() => ({
31988
32718
  [squidTypes.ChainType.EVM]: evmBalancesQuery,
@@ -31993,7 +32723,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
31993
32723
  [squidTypes.ChainType.XRPL]: xrplBalancesQuery,
31994
32724
  [squidTypes.ChainType.STELLAR]: stellarBalancesQuery,
31995
32725
  [squidTypes.ChainType.CANTON]: cantonBalancesQuery,
31996
- [squidTypes.ChainType.DANAL]: danalBalancesQuery,
31997
32726
  }), [
31998
32727
  evmBalancesQuery,
31999
32728
  cosmosBalancesQuery,
@@ -32003,7 +32732,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
32003
32732
  xrplBalancesQuery,
32004
32733
  stellarBalancesQuery,
32005
32734
  cantonBalancesQuery,
32006
- danalBalancesQuery,
32007
32735
  ]);
32008
32736
  // Combine all tokens from different chains
32009
32737
  const allTokens = React.useMemo(() => Object.values(balanceQueries).flatMap((query) => query.data?.tokens ?? []), [balanceQueries]);
@@ -32111,8 +32839,6 @@ const useMultiChainBalance = ({ chain, token, userAddress, enabled = true, }) =>
32111
32839
  return stellarBalance;
32112
32840
  case squidTypes.ChainType.CANTON:
32113
32841
  return cantonBalance;
32114
- case squidTypes.ChainType.DANAL:
32115
- return "0";
32116
32842
  }
32117
32843
  }, [
32118
32844
  chain?.chainType,
@@ -32318,6 +33044,27 @@ function useSourceChainGasToken({ fromChain, fromToken, }) {
32318
33044
  normalizeFee: convertTempoFeeToStablecoinUnits,
32319
33045
  };
32320
33046
  }
33047
+ if (isArcChain(fromChain.chainId)) {
33048
+ const isUsdc = isArcUsdcToken(fromToken);
33049
+ const isNative = isArcNativeToken(fromToken);
33050
+ if (fromToken.chainId !== fromChain.chainId ||
33051
+ nativeBalance.decimals !== 18 ||
33052
+ (isUsdc && fromToken.decimals !== 6) ||
33053
+ (isNative && fromToken.decimals !== 18)) {
33054
+ return null;
33055
+ }
33056
+ return {
33057
+ fromTokenPaysGasFees: isUsdc || isNative,
33058
+ gasTokenBalanceWei: nativeBalance.value,
33059
+ normalizeFee: (fee) => fee,
33060
+ fromAmountToGasUnits: isUsdc
33061
+ ? (amount) => amount * 10n ** 12n
33062
+ : undefined,
33063
+ gasUnitsToFromAmount: isUsdc
33064
+ ? (amount) => amount / 10n ** 12n
33065
+ : undefined,
33066
+ };
33067
+ }
32321
33068
  return {
32322
33069
  fromTokenPaysGasFees: fromToken.address.toLowerCase() ===
32323
33070
  chainTypeToNativeTokenAddressMap[fromToken.type].toLowerCase(),
@@ -32325,10 +33072,10 @@ function useSourceChainGasToken({ fromChain, fromToken, }) {
32325
33072
  normalizeFee: (fee) => fee,
32326
33073
  };
32327
33074
  }, [
32328
- fromToken?.address,
32329
- fromToken?.type,
33075
+ fromToken,
32330
33076
  fromChain?.chainId,
32331
33077
  nativeBalance?.value,
33078
+ nativeBalance?.decimals,
32332
33079
  tempoFeeData,
32333
33080
  ]);
32334
33081
  /**
@@ -32343,9 +33090,20 @@ function useSourceChainGasToken({ fromChain, fromToken, }) {
32343
33090
  return evmTokens.find((t) => t.chainId === fromChain.chainId &&
32344
33091
  t.address.toLowerCase() === tempoFeeData.gasTokenAddress.toLowerCase());
32345
33092
  }
33093
+ if (isArcChain(fromChain.chainId)) {
33094
+ return nativeToken?.decimals === 18
33095
+ ? {
33096
+ ...nativeToken,
33097
+ symbol: fromChain.nativeCurrency.symbol,
33098
+ name: fromChain.nativeCurrency.name,
33099
+ }
33100
+ : undefined;
33101
+ }
32346
33102
  return nativeToken;
32347
33103
  }, [
32348
33104
  fromChain?.chainId,
33105
+ fromChain?.nativeCurrency.symbol,
33106
+ fromChain?.nativeCurrency.name,
32349
33107
  fromToken?.address,
32350
33108
  nativeToken,
32351
33109
  tempoFeeData,
@@ -32504,22 +33262,57 @@ const calculateTotalNativeFees = ({ expressFeeCost, firstFeeCost, firstGasCost,
32504
33262
  (sameTokenBetweenFees
32505
33263
  ? BigInt(firstFeeCost?.amount ?? "0") + BigInt(firstGasCost?.amount ?? "0")
32506
33264
  : BigInt(firstGasCost?.amount ?? "0"));
33265
+ const addGasReserve = (fee) => (fee * 110n + 99n) / 100n;
33266
+ const calculateArcGasCosts = (gasCosts) => {
33267
+ const sourceGasCosts = gasCosts.filter((cost) => isArcChain(cost.token.chainId));
33268
+ if (sourceGasCosts.length === 0)
33269
+ return undefined;
33270
+ let total = 0n;
33271
+ for (const { amount, token } of sourceGasCosts) {
33272
+ const fee = BigInt(amount);
33273
+ if (fee <= 0n)
33274
+ return undefined;
33275
+ if (isArcUsdcToken(token) && token.decimals === 6) {
33276
+ total += fee * 10n ** 12n;
33277
+ }
33278
+ else if (isArcNativeToken(token) && token.decimals === 18) {
33279
+ total += fee;
33280
+ }
33281
+ else {
33282
+ return undefined;
33283
+ }
33284
+ }
33285
+ return total;
33286
+ };
33287
+ const calculateTotalGasBalanceNeeded = ({ fromAmount, networkFeesWei, chainFeeParams, }) => {
33288
+ if (!chainFeeParams || networkFeesWei == null)
33289
+ return undefined;
33290
+ if (!chainFeeParams.fromTokenPaysGasFees)
33291
+ return networkFeesWei;
33292
+ const amount = BigInt(fromAmount ?? "0");
33293
+ const amountInGasUnits = chainFeeParams.fromAmountToGasUnits?.(amount) ?? amount;
33294
+ return amountInGasUnits + networkFeesWei;
33295
+ };
32507
33296
  const checkIsGasBalanceEnough = ({ fromAmount, networkFeesWei, chainFeeParams, }) => {
32508
33297
  if (!chainFeeParams)
32509
33298
  return false;
32510
- if (chainFeeParams.fromTokenPaysGasFees) {
32511
- return (BigInt(fromAmount ?? "0") + networkFeesWei <=
32512
- chainFeeParams.gasTokenBalanceWei);
32513
- }
32514
- return networkFeesWei <= chainFeeParams.gasTokenBalanceWei;
33299
+ const total = calculateTotalGasBalanceNeeded({
33300
+ fromAmount,
33301
+ networkFeesWei,
33302
+ chainFeeParams,
33303
+ });
33304
+ return total != null && total <= chainFeeParams.gasTokenBalanceWei;
32515
33305
  };
32516
33306
  const calculateMinAmountValueWarnMsg = ({ networkFeesWei, chainFeeParams, fromTokenDecimals, }) => {
32517
- if (!chainFeeParams?.fromTokenPaysGasFees)
33307
+ if (!chainFeeParams?.fromTokenPaysGasFees || networkFeesWei == null)
32518
33308
  return undefined;
32519
33309
  const minAmount = chainFeeParams.gasTokenBalanceWei - networkFeesWei;
32520
33310
  if (minAmount <= BigInt(0))
32521
33311
  return undefined;
32522
- return formatBNToReadable(minAmount, fromTokenDecimals);
33312
+ const amountInTokenUnits = chainFeeParams.gasUnitsToFromAmount?.(minAmount) ?? minAmount;
33313
+ if (amountInTokenUnits <= 0n)
33314
+ return undefined;
33315
+ return formatBNToReadable(amountInTokenUnits, fromTokenDecimals);
32523
33316
  };
32524
33317
  /**
32525
33318
  * Calculates the estimated duration of a route
@@ -32568,14 +33361,22 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
32568
33361
  const expectedGasRefundCostUSD = convertTokenAmountToUSD(formatBNToReadable(expectedGasRefundCost, firstFeeCost?.token.decimals ?? 18), firstFeeCost?.token.usdPrice ?? "0");
32569
33362
  const sameTokenBetweenFees = firstFeeCost?.token.address === firstGasCost?.token.address &&
32570
33363
  firstFeeCost?.token.chainId === firstGasCost?.token.chainId;
32571
- const totalNativeFees = calculateTotalNativeFees({
32572
- expressFeeCost,
32573
- firstFeeCost,
32574
- firstGasCost,
32575
- sameTokenBetweenFees,
32576
- });
32577
- const totalFeesInNativeTokenPlusRatio = (totalNativeFees * BigInt(110)) / BigInt(100);
32578
- const networkFeesWei = chainFeeParams?.normalizeFee(totalFeesInNativeTokenPlusRatio) ?? BigInt(0);
33364
+ const totalNativeFees = isArcChain(fromChain?.chainId)
33365
+ ? calculateArcGasCosts(allGasCosts)
33366
+ : calculateTotalNativeFees({
33367
+ expressFeeCost,
33368
+ firstFeeCost,
33369
+ firstGasCost,
33370
+ sameTokenBetweenFees,
33371
+ });
33372
+ const totalFeesInNativeTokenPlusRatio = totalNativeFees == null
33373
+ ? undefined
33374
+ : isArcChain(fromChain?.chainId)
33375
+ ? addGasReserve(totalNativeFees)
33376
+ : (totalNativeFees * BigInt(110)) / BigInt(100);
33377
+ const networkFeesWei = totalFeesInNativeTokenPlusRatio == null
33378
+ ? undefined
33379
+ : chainFeeParams?.normalizeFee(totalFeesInNativeTokenPlusRatio);
32579
33380
  const isGasBalanceEnough = checkIsGasBalanceEnough({
32580
33381
  chainFeeParams,
32581
33382
  fromAmount,
@@ -32591,10 +33392,12 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
32591
33392
  estimatedRouteDuration: squidRoute?.estimate?.estimatedRouteDuration,
32592
33393
  isSingleChainRoute,
32593
33394
  });
32594
- // gas fees + fromAmount (if fromToken is gas token)
32595
- const totalGasBalanceNeeded = networkFeesWei +
32596
- BigInt(chainFeeParams?.fromTokenPaysGasFees ? (fromAmount ?? 0) : 0);
32597
- const gasBalanceNeeded = gasToken
33395
+ const totalGasBalanceNeeded = calculateTotalGasBalanceNeeded({
33396
+ fromAmount,
33397
+ networkFeesWei,
33398
+ chainFeeParams,
33399
+ });
33400
+ const gasBalanceNeeded = gasToken && totalGasBalanceNeeded != null
32598
33401
  ? formatBNToReadable(totalGasBalanceNeeded, gasToken.decimals)
32599
33402
  : undefined;
32600
33403
  return {
@@ -32616,7 +33419,7 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
32616
33419
  expectedGasRefundCostUSD,
32617
33420
  sameTokenBetweenFees,
32618
33421
  fromTokenPaysGasFees: chainFeeParams?.fromTokenPaysGasFees ?? false,
32619
- totalNativeFees,
33422
+ totalNativeFees: totalNativeFees ?? 0n,
32620
33423
  isGasBalanceEnough,
32621
33424
  minAmountValueWarnMsg,
32622
33425
  estimatedRouteDuration,
@@ -32665,8 +33468,12 @@ function useSendTransactionGas({ chain, token, from, }) {
32665
33468
  case squidTypes.ChainType.EVM: {
32666
33469
  const client = await getClient(chain);
32667
33470
  const feeData = await client.getFeeData();
32668
- if (!feeData.maxFeePerGas)
33471
+ if (!feeData.maxFeePerGas) {
33472
+ if (isArcChain(chain.chainId)) {
33473
+ throw new Error("Gas price is unavailable");
33474
+ }
32669
33475
  return BigInt(0);
33476
+ }
32670
33477
  let maxFeePerGas = feeData.maxFeePerGas;
32671
33478
  if (token.chainId === CHAIN_IDS.HEDERA) {
32672
33479
  maxFeePerGas = scaleWeiToHbar(BigInt(feeData.maxFeePerGas));
@@ -32686,6 +33493,9 @@ function useSendTransactionGas({ chain, token, from, }) {
32686
33493
  value: 1,
32687
33494
  chainId: chain.chainId,
32688
33495
  });
33496
+ if (isArcChain(chain.chainId) && gas <= 0n) {
33497
+ throw new Error("Gas estimate is unavailable");
33498
+ }
32689
33499
  return gas * maxFeePerGas;
32690
33500
  }
32691
33501
  const erc20Interface = new ethers.Interface(viem.erc20Abi);
@@ -32700,6 +33510,9 @@ function useSendTransactionGas({ chain, token, from, }) {
32700
33510
  chainId: chain.chainId,
32701
33511
  });
32702
33512
  const feeWei = gas * maxFeePerGas;
33513
+ if (isArcChain(chain.chainId) && feeWei <= 0n) {
33514
+ throw new Error("Gas estimate is unavailable");
33515
+ }
32703
33516
  // Tempo gas fees are denominated 18 units,
32704
33517
  // this value needs to be scaled to 6 decimals
32705
33518
  if (chain.chainId === CHAIN_IDS.TEMPO) {
@@ -32747,35 +33560,45 @@ function useSendTransactionGas({ chain, token, from, }) {
32747
33560
  }
32748
33561
 
32749
33562
  function useEstimateSendTransaction({ chain, token, amount, balance, from, }) {
32750
- const { data: estimatedGas = BigInt(0), isLoading } = useSendTransactionGas({
33563
+ const { data, isLoading, isFetching, isError, refetch } = useSendTransactionGas({
32751
33564
  chain,
32752
33565
  token,
32753
33566
  from,
32754
33567
  });
33568
+ const estimatedGas = isError || isFetching ? undefined : data;
33569
+ const networkFeesWei = estimatedGas == null
33570
+ ? undefined
33571
+ : isArcChain(chain?.chainId)
33572
+ ? addGasReserve(estimatedGas)
33573
+ : estimatedGas;
32755
33574
  const { chainFeeParams, gasToken } = useSourceChainGasToken({
32756
33575
  fromChain: chain,
32757
33576
  fromToken: token,
32758
33577
  });
32759
33578
  const isGasBalanceEnough = React.useMemo(() => {
32760
- if (!amount || !token?.decimals)
33579
+ if (!amount || token?.decimals == null || !gasToken)
32761
33580
  return false;
32762
33581
  return checkIsGasBalanceEnough({
32763
33582
  fromAmount: parseToBigInt(amount, token.decimals).toString(),
32764
- networkFeesWei: estimatedGas,
33583
+ networkFeesWei,
32765
33584
  chainFeeParams,
32766
33585
  });
32767
- }, [amount, estimatedGas, token, chainFeeParams]);
33586
+ }, [amount, networkFeesWei, token, chainFeeParams, gasToken]);
32768
33587
  const gasBalanceNeeded = React.useMemo(() => {
32769
- if (!amount || !token?.decimals || !gasToken?.decimals)
33588
+ if (!amount || token?.decimals == null || gasToken?.decimals == null)
33589
+ return undefined;
33590
+ const totalGasBalanceNeeded = calculateTotalGasBalanceNeeded({
33591
+ fromAmount: parseToBigInt(amount, token.decimals).toString(),
33592
+ networkFeesWei,
33593
+ chainFeeParams,
33594
+ });
33595
+ if (totalGasBalanceNeeded == null)
32770
33596
  return undefined;
32771
- // gas fees + fromAmount (if fromToken is gas token)
32772
- const totalGasBalanceNeeded = estimatedGas +
32773
- parseToBigInt(chainFeeParams?.fromTokenPaysGasFees ? (amount ?? "0") : "0", token.decimals);
32774
33597
  return formatBNToReadable(totalGasBalanceNeeded, gasToken.decimals);
32775
33598
  }, [
32776
33599
  amount,
32777
- chainFeeParams?.fromTokenPaysGasFees,
32778
- estimatedGas,
33600
+ chainFeeParams,
33601
+ networkFeesWei,
32779
33602
  gasToken?.decimals,
32780
33603
  token?.decimals,
32781
33604
  ]);
@@ -32786,20 +33609,22 @@ function useEstimateSendTransaction({ chain, token, amount, balance, from, }) {
32786
33609
  parseToBigInt(amount, token.decimals));
32787
33610
  }, [amount, balance, token?.decimals]);
32788
33611
  const minAmountValueWarnMsg = React.useMemo(() => {
32789
- if (!token?.address || !estimatedGas || !chainFeeParams)
33612
+ if (!token?.address || !gasToken || !chainFeeParams)
32790
33613
  return undefined;
32791
33614
  return calculateMinAmountValueWarnMsg({
32792
33615
  chainFeeParams,
32793
- networkFeesWei: estimatedGas,
33616
+ networkFeesWei,
32794
33617
  fromTokenDecimals: token.decimals,
32795
33618
  });
32796
- }, [estimatedGas, token, chainFeeParams]);
33619
+ }, [networkFeesWei, token, chainFeeParams, gasToken]);
32797
33620
  return {
32798
33621
  estimatedGas,
32799
33622
  gasBalanceNeeded,
32800
33623
  isBalanceEnough,
32801
33624
  tokenPaysGasFees: chainFeeParams?.fromTokenPaysGasFees ?? false,
32802
- isLoading,
33625
+ isLoading: isLoading || isFetching,
33626
+ isError,
33627
+ refetchGasEstimate: refetch,
32803
33628
  isGasBalanceEnough,
32804
33629
  minAmountValueWarnMsg,
32805
33630
  };
@@ -33222,9 +34047,8 @@ function useSendTransaction({ to, amount, token, chain, }) {
33222
34047
  chain,
33223
34048
  });
33224
34049
  break;
33225
- case squidTypes.ChainType.CANTON:
33226
- case squidTypes.ChainType.DANAL: {
33227
- throw new Error(`Send not implemented for chain type ${chain.chainType}`);
34050
+ case squidTypes.ChainType.CANTON: {
34051
+ throw new Error("Not implemented");
33228
34052
  }
33229
34053
  }
33230
34054
  return {
@@ -33430,8 +34254,7 @@ async function getSendTransactionStatus({ chain, txHash, }) {
33430
34254
  chain,
33431
34255
  });
33432
34256
  case squidTypes.ChainType.CANTON:
33433
- case squidTypes.ChainType.DANAL:
33434
- throw new Error(`Send not implemented for chain type ${chain.chainType}`);
34257
+ throw new Error("Not implemented");
33435
34258
  }
33436
34259
  }
33437
34260
 
@@ -33702,6 +34525,19 @@ const useApproval = ({ squidRoute, }) => {
33702
34525
  const { getChainType } = useSquidChains();
33703
34526
  const { switchChainAsync } = wagmi.useSwitchChain();
33704
34527
  const { connectedAddress: { address: sourceUserAddress }, } = useMultiChainWallet(fromChain);
34528
+ const arcNativeBalanceQuery = wagmi.useBalance({
34529
+ address: sourceUserAddress,
34530
+ chainId: Number(fromChain?.chainId),
34531
+ query: { enabled: false },
34532
+ });
34533
+ const arcTokenBalanceQuery = wagmi.useReadContract({
34534
+ address: fromToken?.address,
34535
+ abi: viem.erc20Abi,
34536
+ functionName: "balanceOf",
34537
+ args: [sourceUserAddress],
34538
+ chainId: Number(fromChain?.chainId),
34539
+ query: { enabled: false },
34540
+ });
33705
34541
  const isSagaEvmSameChainSwap = isSameChain &&
33706
34542
  fromChain?.chainId.toString() === CHAIN_IDS.SAGA_EVM.toString();
33707
34543
  // Swaps on SagaEVM are regular EVM transactions, following the standard ERC20 approve/allowance mechanism.
@@ -33862,13 +34698,20 @@ const useApproval = ({ squidRoute, }) => {
33862
34698
  return false;
33863
34699
  }
33864
34700
  }, {
33865
- onSuccess: async () => {
34701
+ onSuccess: async (approved) => {
34702
+ if (approved && isArcChain(fromChain?.chainId)) {
34703
+ await Promise.all([
34704
+ arcNativeBalanceQuery.refetch({ throwOnError: true }),
34705
+ arcTokenBalanceQuery.refetch({ throwOnError: true }),
34706
+ ]);
34707
+ queryClient.invalidateQueries(getPrefixKey(exports.QueryKeys.AllTokensBalance));
34708
+ }
33866
34709
  await allowanceQuery?.refetch();
33867
34710
  queryClient.invalidateQueries(getPrefixKey(exports.QueryKeys.RouteApproved));
33868
34711
  // After an approval, we refetch the transaction query with all required parameters
33869
34712
  // This is to ensure we're using the latest expiry timestamp
33870
34713
  if (squidRoute) {
33871
- queryClient.refetchQueries({
34714
+ await queryClient.refetchQueries({
33872
34715
  queryKey: keys().transaction(squidRoute.params.fromChain, squidRoute.params.toChain, squidRoute.params.toToken, squidRoute.params.fromToken, fromPrice, squidRoute.params.slippage, squidRoute.params.fromAddress, squidRoute.params.bypassGuardrails, squidRoute.params.toAddress, squidRoute.params.fallbackAddresses?.[0]?.address, squidRoute.params.quoteOnly, getChainType(squidRoute.params.fromChain), squidRoute.params.preHook, squidRoute.params.postHook,
33873
34716
  // TODO: update types
33874
34717
  squidRoute.params?.overrideGasRefundAddress),
@@ -37052,342 +37895,6 @@ coin.DecProto = {
37052
37895
 
37053
37896
  } (tx));
37054
37897
 
37055
- const vParty = (party) => ({ party });
37056
- const vText = (text) => ({ text });
37057
- const vNumeric = (numeric) => ({ numeric });
37058
- const vContractId = (contractId) => ({
37059
- contractId,
37060
- });
37061
- const vList = (elements) => ({
37062
- list: { elements },
37063
- });
37064
- const vRecord = (fields) => ({
37065
- record: { fields: fields.map(([label, value]) => ({ label, value })) },
37066
- });
37067
- const vTextMap = (entries) => ({ textMap: { entries } });
37068
- const vTimestamp = (isoString) => ({
37069
- timestamp: String(Math.floor(new Date(isoString).getTime() * 1000)),
37070
- });
37071
- function isRecord(value) {
37072
- return typeof value === "object" && value !== null && !Array.isArray(value);
37073
- }
37074
- function normalizeChoiceContextValue(value) {
37075
- if (typeof value === "string")
37076
- return value;
37077
- if (!isRecord(value) || typeof value.tag !== "string")
37078
- return null;
37079
- switch (value.tag) {
37080
- case "AV_Text":
37081
- case "AV_ContractId":
37082
- return typeof value.value === "string"
37083
- ? { tag: value.tag, value: value.value }
37084
- : null;
37085
- case "AV_Bool":
37086
- return typeof value.value === "boolean"
37087
- ? { tag: value.tag, value: value.value }
37088
- : null;
37089
- case "AV_List":
37090
- if (!Array.isArray(value.value))
37091
- return null;
37092
- return {
37093
- tag: value.tag,
37094
- value: value.value.flatMap((entry) => {
37095
- const normalized = normalizeChoiceContextValue(entry);
37096
- return normalized ? [normalized] : [];
37097
- }),
37098
- };
37099
- default: {
37100
- const inner = value.value;
37101
- if (typeof inner === "string" ||
37102
- typeof inner === "number" ||
37103
- typeof inner === "boolean" ||
37104
- inner === null) {
37105
- return { tag: value.tag, value: inner };
37106
- }
37107
- if (Array.isArray(inner)) {
37108
- return {
37109
- tag: value.tag,
37110
- value: inner.flatMap((entry) => {
37111
- const normalized = normalizeChoiceContextValue(entry);
37112
- return normalized ? [normalized] : [];
37113
- }),
37114
- };
37115
- }
37116
- if (!isRecord(inner))
37117
- return null;
37118
- const normalizedObject = {};
37119
- for (const [key, entry] of Object.entries(inner)) {
37120
- if (typeof entry !== "string" &&
37121
- typeof entry !== "number" &&
37122
- typeof entry !== "boolean" &&
37123
- entry !== null) {
37124
- return null;
37125
- }
37126
- normalizedObject[key] = entry;
37127
- }
37128
- return { tag: value.tag, value: normalizedObject };
37129
- }
37130
- }
37131
- }
37132
- function stringifyChoiceContextValue(value) {
37133
- return typeof value === "string" ? value : String(value);
37134
- }
37135
- function choiceContextValueToAnyValue(value) {
37136
- if (typeof value === "string") {
37137
- return {
37138
- variant: { constructor: "AV_Text", value: vText(value) },
37139
- };
37140
- }
37141
- switch (value.tag) {
37142
- case "AV_ContractId":
37143
- if (typeof value.value !== "string") {
37144
- throw new TypeError("AV_ContractId values must be strings");
37145
- }
37146
- return {
37147
- variant: { constructor: value.tag, value: vContractId(value.value) },
37148
- };
37149
- case "AV_Bool":
37150
- if (typeof value.value !== "boolean") {
37151
- throw new TypeError("AV_Bool values must be booleans");
37152
- }
37153
- return {
37154
- variant: { constructor: value.tag, value: { bool: value.value } },
37155
- };
37156
- case "AV_List":
37157
- if (!Array.isArray(value.value)) {
37158
- throw new TypeError("AV_List values must be arrays");
37159
- }
37160
- return {
37161
- variant: {
37162
- constructor: value.tag,
37163
- value: vList(value.value.map(choiceContextValueToAnyValue)),
37164
- },
37165
- };
37166
- default:
37167
- return {
37168
- variant: {
37169
- constructor: value.tag,
37170
- value: vText(stringifyChoiceContextValue(value.value)),
37171
- },
37172
- };
37173
- }
37174
- }
37175
-
37176
- /** Metadata key used by Splice token-standard transfers to carry our Squid orderhash memo. */
37177
- const CANTON_MEMO_KEY = "splice.lfdecentralizedtrust.org/reason";
37178
- /** Interface id for the transfer factory contract returned by the token registry. */
37179
- const TRANSFER_FACTORY_INTERFACE_ID = "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory";
37180
- /** Choice exercised on the transfer factory to create the token-standard transfer. */
37181
- const TRANSFER_FACTORY_CHOICE = "TransferFactory_Transfer";
37182
- /** CIP-056 (non-Amulet) tokens expose a registrar parameterized by admin party. */
37183
- const CIP056_REGISTRAR_BASE = "https://api.utilities.digitalasset.com/api/token-standard/v0/registrars";
37184
- const TRANSFER_REQUESTED_AT_SKEW_MS = 60_000;
37185
- const TRANSFER_EXECUTE_WINDOW_MS = 24 * 60 * 60 * 1000;
37186
- function resolveCantonRegistryUrl(instrument) {
37187
- if (instrument.id === CANTON_AMULET_INSTRUMENT_ID) {
37188
- return CANTON_AMULET_REGISTRY_URL;
37189
- }
37190
- return `${CIP056_REGISTRAR_BASE}/${encodeURIComponent(instrument.admin)}`;
37191
- }
37192
- /**
37193
- * Build the plain JSON arguments expected by the registry transfer-factory
37194
- * endpoint before it adds choice context and disclosed contracts.
37195
- */
37196
- function buildTransferChoiceArgs({ sender, receiver, amount, instrument, inputHoldingCids, memo, nowMs, }) {
37197
- return {
37198
- expectedAdmin: instrument.admin,
37199
- transfer: {
37200
- sender,
37201
- receiver,
37202
- amount,
37203
- instrumentId: { admin: instrument.admin, id: instrument.id },
37204
- requestedAt: new Date(nowMs - TRANSFER_REQUESTED_AT_SKEW_MS).toISOString(),
37205
- executeBefore: new Date(nowMs + TRANSFER_EXECUTE_WINDOW_MS).toISOString(),
37206
- inputHoldingCids,
37207
- meta: { values: { [CANTON_MEMO_KEY]: memo } },
37208
- },
37209
- extraArgs: { context: { values: {} }, meta: { values: {} } },
37210
- };
37211
- }
37212
- /**
37213
- * Canton's Holding contracts follow Bitcoin-style UTXOs model.
37214
- *
37215
- * Pick Holding contract ids covering `amount` for the instrument: prefer a
37216
- * single exact-amount holding, else accumulate largest-first.
37217
- */
37218
- function selectInputHoldingCids(holdings, instrument, amount) {
37219
- const spendableHoldings = holdings.filter((h) => !h.locked &&
37220
- h.instrument.admin === instrument.admin &&
37221
- h.instrument.id === instrument.id);
37222
- const target = new BigNumber(amount);
37223
- const exact = spendableHoldings.find((h) => new BigNumber(h.amount).eq(target));
37224
- if (exact)
37225
- return [exact.contractId];
37226
- const sorted = [...spendableHoldings].sort((a, b) => new BigNumber(b.amount).comparedTo(new BigNumber(a.amount)));
37227
- const selected = [];
37228
- let sum = new BigNumber(0);
37229
- for (const holding of sorted) {
37230
- selected.push(holding.contractId);
37231
- sum = sum.plus(holding.amount);
37232
- if (sum.gte(target))
37233
- break;
37234
- }
37235
- if (sum.lt(target)) {
37236
- throw new Error("Insufficient Canton holdings to cover transfer amount");
37237
- }
37238
- return selected;
37239
- }
37240
- async function fetchInputHoldingCids(connector, party, instrument, amount) {
37241
- const ledgerEnd = await connector.ledgerApi({
37242
- requestMethod: "get",
37243
- resource: "/v2/state/ledger-end",
37244
- });
37245
- const activeContracts = await connector.ledgerApi({
37246
- requestMethod: "post",
37247
- resource: "/v2/state/active-contracts",
37248
- body: buildHoldingsAcsRequestBody(party, String(ledgerEnd.offset)),
37249
- });
37250
- return selectInputHoldingCids(parseCantonHoldings(activeContracts), instrument, amount);
37251
- }
37252
- /**
37253
- * Ask the token registry for the transfer factory contract and extra context
37254
- * required to exercise its transfer choice.
37255
- */
37256
- async function fetchTransferFactory(registryUrl, choiceArgs) {
37257
- const response = await fetch(`${registryUrl}/registry/transfer-instruction/v1/transfer-factory`, {
37258
- method: "POST",
37259
- headers: { "Content-Type": "application/json" },
37260
- body: JSON.stringify({
37261
- choiceArguments: choiceArgs,
37262
- excludeDebugFields: true,
37263
- }),
37264
- });
37265
- if (!response.ok) {
37266
- throw new Error(`Canton transfer-factory request failed (${response.status})`);
37267
- }
37268
- return response.json();
37269
- }
37270
- /**
37271
- * Convert transfer choice args into strict Daml Value JSON for the wallet's
37272
- * prepareExecute ExerciseCommand.
37273
- */
37274
- function buildPrepareChoiceArgument(choiceArgs) {
37275
- const contextValues = Object.entries(choiceArgs.extraArgs.context?.values ?? {})
37276
- .map(([key, value]) => {
37277
- const normalized = normalizeChoiceContextValue(value);
37278
- return normalized
37279
- ? { key, value: choiceContextValueToAnyValue(normalized) }
37280
- : undefined;
37281
- })
37282
- .filter((entry) => Boolean(entry));
37283
- return vRecord([
37284
- ["expectedAdmin", vParty(choiceArgs.expectedAdmin)],
37285
- [
37286
- "transfer",
37287
- vRecord([
37288
- ["sender", vParty(choiceArgs.transfer.sender)],
37289
- ["receiver", vParty(choiceArgs.transfer.receiver)],
37290
- ["amount", vNumeric(choiceArgs.transfer.amount)],
37291
- [
37292
- "instrumentId",
37293
- vRecord([
37294
- ["admin", vParty(choiceArgs.transfer.instrumentId.admin)],
37295
- ["id", vText(choiceArgs.transfer.instrumentId.id)],
37296
- ]),
37297
- ],
37298
- ["requestedAt", vTimestamp(choiceArgs.transfer.requestedAt)],
37299
- ["executeBefore", vTimestamp(choiceArgs.transfer.executeBefore)],
37300
- [
37301
- "inputHoldingCids",
37302
- vList(choiceArgs.transfer.inputHoldingCids.map(vContractId)),
37303
- ],
37304
- [
37305
- "meta",
37306
- vRecord([
37307
- [
37308
- "values",
37309
- vTextMap(Object.entries(choiceArgs.transfer.meta.values).map(([key, value]) => ({ key, value: vText(value) }))),
37310
- ],
37311
- ]),
37312
- ],
37313
- ]),
37314
- ],
37315
- [
37316
- "extraArgs",
37317
- vRecord([
37318
- ["context", vRecord([["values", vTextMap(contextValues)]])],
37319
- ["meta", vRecord([["values", vTextMap([])]])],
37320
- ]),
37321
- ],
37322
- ]);
37323
- }
37324
- /**
37325
- * Convert a registry disclosed contract into the wallet submission shape,
37326
- * dropping debug fields and entries without a usable createdEventBlob.
37327
- */
37328
- function toDisclosedContract(raw) {
37329
- if (typeof raw.createdEventBlob !== "string")
37330
- return null;
37331
- return {
37332
- createdEventBlob: raw.createdEventBlob,
37333
- contractId: typeof raw.contractId === "string" ? raw.contractId : undefined,
37334
- synchronizerId: typeof raw.synchronizerId === "string" ? raw.synchronizerId : undefined,
37335
- };
37336
- }
37337
- /**
37338
- * Build a Splice token-standard transfer of `amount` of `token` from `sender`
37339
- * to `receiver`, carrying `memo`, ready to hand to a wallet's prepareExecute.
37340
- * Returns the full prepareExecute params (commandId + commands + actAs +
37341
- * disclosedContracts).
37342
- */
37343
- async function buildCantonTransfer(connector, { sender, receiver, amount, token, memo, nowMs, }) {
37344
- if (!token.originalAddress) {
37345
- throw new Error("Canton token originalAddress is required");
37346
- }
37347
- const instrument = parseCantonInstrument(token.originalAddress);
37348
- if (!instrument) {
37349
- throw new Error(`Invalid Canton token address: ${token.originalAddress}`);
37350
- }
37351
- const registryUrl = resolveCantonRegistryUrl(instrument);
37352
- const inputHoldingCids = await fetchInputHoldingCids(connector, sender, instrument, amount);
37353
- const choiceArgs = buildTransferChoiceArgs({
37354
- sender,
37355
- receiver,
37356
- amount,
37357
- instrument,
37358
- inputHoldingCids,
37359
- memo,
37360
- nowMs,
37361
- });
37362
- const factory = await fetchTransferFactory(registryUrl, choiceArgs);
37363
- const choiceContextData = factory.choiceContext.choiceContextData;
37364
- if (!choiceContextData) {
37365
- throw new Error("Transfer factory choice context is missing");
37366
- }
37367
- choiceArgs.extraArgs.context = choiceContextData;
37368
- const disclosedContracts = (factory.choiceContext?.disclosedContracts ?? []).flatMap((raw) => {
37369
- const disclosedContract = toDisclosedContract(raw);
37370
- return disclosedContract ? [disclosedContract] : [];
37371
- });
37372
- // Command ID: client-supplied identifier used to deduplicate submissions
37373
- const commandId = `canton-${memo}`;
37374
- return {
37375
- commandId,
37376
- commands: [
37377
- {
37378
- ExerciseCommand: {
37379
- templateId: TRANSFER_FACTORY_INTERFACE_ID,
37380
- contractId: factory.factoryId,
37381
- choice: TRANSFER_FACTORY_CHOICE,
37382
- choiceArgument: buildPrepareChoiceArgument(choiceArgs),
37383
- },
37384
- },
37385
- ],
37386
- actAs: [sender],
37387
- disclosedContracts,
37388
- };
37389
- }
37390
-
37391
37898
  const useExecuteTransaction = (squidRoute) => {
37392
37899
  const { fromChain, toChain, fromToken, toToken, isSameChain } = useSwap();
37393
37900
  // A route completes on source tx only if it's same-chain AND
@@ -38002,7 +38509,7 @@ const useExecuteTransaction = (squidRoute) => {
38002
38509
  }
38003
38510
  const amount = formatBNToReadable(fromAmount, token.decimals);
38004
38511
  dispatchSignatureRequestEvent(route);
38005
- const transferParams = await buildCantonTransfer(cantonSigner, {
38512
+ const result = await cantonSigner.transfer({
38006
38513
  sender: sourceUserAddress,
38007
38514
  receiver,
38008
38515
  amount,
@@ -38010,15 +38517,14 @@ const useExecuteTransaction = (squidRoute) => {
38010
38517
  memo: orderHash,
38011
38518
  nowMs: Date.now(),
38012
38519
  });
38013
- const result = await cantonSigner.prepareExecuteAndWait(transferParams);
38014
- const txHash = result.tx?.payload?.updateId ?? "";
38520
+ const txHash = result.updateId;
38015
38521
  if (txHash) {
38016
38522
  resetQueriesAfterTxSigned();
38017
38523
  }
38018
38524
  WidgetEvents.getInstance().dispatchSwapExecuteCall(route, txHash);
38019
38525
  const txParams = setTransactionState({
38020
38526
  route,
38021
- txHash: txHash,
38527
+ txHash,
38022
38528
  userAddress: sourceUserAddress,
38023
38529
  status: exports.TransactionStatus.INITIAL_LOADING,
38024
38530
  sourceStatus: exports.TransactionStatus.ONGOING,
@@ -38301,7 +38807,7 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
38301
38807
  //
38302
38808
  // If deposit address is not selected, we use the connected address as the source address instead
38303
38809
  const sourceUserAddress = isDepositAddressActive
38304
- ? depositRefundAddress ?? sourceConnectedAddress
38810
+ ? (depositRefundAddress ?? sourceConnectedAddress)
38305
38811
  : sourceConnectedAddress;
38306
38812
  const squidRouteQueryKeys = React.useMemo(() => keys().transaction(fromChain?.chainId, toChain?.chainId, toToken?.address, fromToken?.address, fromPrice, config.slippage, sourceUserAddress, config.degenMode, destinationAddress, fallbackAddress, quoteOnly, fromChain?.chainType, config.preHook, config.postHook, config.overrideGasRefundAddress), [
38307
38813
  fromChain?.chainId,
@@ -38593,6 +39099,10 @@ const useAutoConnect = () => {
38593
39099
  const { chains } = useSquidChains();
38594
39100
  const { isInitialized } = useConfigStore();
38595
39101
  const hasAttemptedReconnect = React.useRef(false);
39102
+ const hasAttemptedCantonReconnect = React.useRef(false);
39103
+ const { autoConnectCanton, wallets: cantonWallets } = useCantonContext();
39104
+ const recentCantonWalletId = useWalletStore((state) => state.recentConnectorIds[squidTypes.ChainType.CANTON]);
39105
+ const hasRecentCantonWallet = cantonWallets.some((wallet) => wallet.connectorId === recentCantonWalletId);
38596
39106
  const { autoConnectEvm } = useEvmContext();
38597
39107
  const { autoConnectSolana } = useSolanaContext();
38598
39108
  const { autoConnectSui } = useSuiContext();
@@ -38660,6 +39170,15 @@ const useAutoConnect = () => {
38660
39170
  React.useEffect(() => {
38661
39171
  autoConnect();
38662
39172
  }, [autoConnect]);
39173
+ React.useEffect(() => {
39174
+ if (!isInitialized ||
39175
+ chains.length === 0 ||
39176
+ !hasRecentCantonWallet ||
39177
+ hasAttemptedCantonReconnect.current)
39178
+ return;
39179
+ hasAttemptedCantonReconnect.current = true;
39180
+ autoConnectCanton().catch(() => undefined);
39181
+ }, [autoConnectCanton, hasRecentCantonWallet, isInitialized, chains.length]);
38663
39182
  return { autoConnect };
38664
39183
  };
38665
39184
 
@@ -39067,6 +39586,7 @@ exports.getStepsInfos = getStepsInfos;
39067
39586
  exports.getSuggestedAmountsForCurrency = getSuggestedAmountsForCurrency;
39068
39587
  exports.getSupportedChainIdsForDirection = getSupportedChainIdsForDirection;
39069
39588
  exports.getSwapTxStatusRefetchInterval = getSwapTxStatusRefetchInterval;
39589
+ exports.getTokenAddressForSelection = getTokenAddressForSelection;
39070
39590
  exports.getTokenAssetsKey = getTokenAssetsKey;
39071
39591
  exports.getTokenImage = getTokenImage;
39072
39592
  exports.getTokensForChain = getTokensForChain;
@@ -39087,9 +39607,6 @@ exports.isChainflipBridgeTransaction = isChainflipBridgeTransaction;
39087
39607
  exports.isChainflipDepositRoute = isChainflipDepositRoute;
39088
39608
  exports.isCoralBridgeAction = isCoralBridgeAction;
39089
39609
  exports.isCosmosAddressValid = isCosmosAddressValid;
39090
- exports.isDanalAddressValid = isDanalAddressValid;
39091
- exports.isDanalDepositRoute = isDanalDepositRoute;
39092
- exports.isDanalTxHashValid = isDanalTxHashValid;
39093
39610
  exports.isDepositAddressDirectTransferRoute = isDepositAddressDirectTransferRoute;
39094
39611
  exports.isEmptyObject = isEmptyObject;
39095
39612
  exports.isEvmChainNotSupportedError = isEvmChainNotSupportedError;
@@ -39126,8 +39643,8 @@ exports.nativeSuiTokenAddress = nativeSuiTokenAddress;
39126
39643
  exports.nativeXrplTokenAddress = nativeXrplTokenAddress;
39127
39644
  exports.normalizeError = normalizeError;
39128
39645
  exports.normalizeIbcAddress = normalizeIbcAddress;
39646
+ exports.normalizeSwapParamsForSelection = normalizeSwapParamsForSelection;
39129
39647
  exports.normalizeTokenSymbol = normalizeTokenSymbol;
39130
- exports.parseDanalTransferBody = parseDanalTransferBody;
39131
39648
  exports.parseEvmAddress = parseEvmAddress;
39132
39649
  exports.parseInitialAssetsFromUrl = parseInitialAssetsFromUrl;
39133
39650
  exports.parseToBigInt = parseToBigInt;
@@ -39240,4 +39757,4 @@ exports.useXrplTrustLine = useXrplTrustLine;
39240
39757
  exports.waitForReceiptWithRetry = waitForReceiptWithRetry;
39241
39758
  exports.walletIconBaseUrl = walletIconBaseUrl;
39242
39759
  exports.walletSupportsChainType = walletSupportsChainType;
39243
- //# sourceMappingURL=index-nTqoaD0B.js.map
39760
+ //# sourceMappingURL=index-BBJD55XX.js.map