@0xsquid/react-hooks 8.11.1-beta-danal.2 → 8.12.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 (55) 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 +0 -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/wallet/useMultiChainWallet.d.ts +0 -38
  15. package/dist/hooks/wallet/useWallet.d.ts +0 -42
  16. package/dist/{index-ByakOYXP.js → index-C3owE9lb.js} +740 -514
  17. package/dist/index-C3owE9lb.js.map +1 -0
  18. package/dist/{index-nTqoaD0B.js → index-CJVX-7hj.js} +739 -517
  19. package/dist/index-CJVX-7hj.js.map +1 -0
  20. package/dist/{index.es--AR2n7a0.js → index.es-09IsADsA.js} +2 -2
  21. package/dist/{index.es--AR2n7a0.js.map → index.es-09IsADsA.js.map} +1 -1
  22. package/dist/{index.es-DtFDz6qc.js → index.es-CTKuR0LC.js} +2 -2
  23. package/dist/{index.es-DtFDz6qc.js.map → index.es-CTKuR0LC.js.map} +1 -1
  24. package/dist/index.esm.js +1 -1
  25. package/dist/index.js +1 -5
  26. package/dist/index.js.map +1 -1
  27. package/dist/{secretService-cgr3P1zw.js → secretService--tJFCkzT.js} +2 -2
  28. package/dist/{secretService-cgr3P1zw.js.map → secretService--tJFCkzT.js.map} +1 -1
  29. package/dist/{secretService-29Ak2qp5.js → secretService-GHdReJBs.js} +2 -2
  30. package/dist/{secretService-29Ak2qp5.js.map → secretService-GHdReJBs.js.map} +1 -1
  31. package/dist/server.esm.js +0 -6
  32. package/dist/server.esm.js.map +1 -1
  33. package/dist/server.js +0 -6
  34. package/dist/server.js.map +1 -1
  35. package/dist/services/external/rpcService.d.ts +2 -4
  36. package/dist/services/index.d.ts +0 -1
  37. package/dist/services/internal/assetsService.d.ts +1 -1
  38. package/dist/services/internal/cantonService.d.ts +2 -0
  39. package/dist/services/internal/cantonTransferService.d.ts +3 -3
  40. package/dist/services/internal/estimateService.d.ts +1 -1
  41. package/dist/services/internal/searchService.d.ts +1 -2
  42. package/dist/services/internal/transactionService.d.ts +0 -8
  43. package/dist/{stellarService.client-B__dTUBZ.js → stellarService.client-DAxgO3m3.js} +2 -2
  44. package/dist/{stellarService.client-B__dTUBZ.js.map → stellarService.client-DAxgO3m3.js.map} +1 -1
  45. package/dist/{stellarService.client-D-zkMI5s.js → stellarService.client-DvA-2pXq.js} +2 -2
  46. package/dist/{stellarService.client-D-zkMI5s.js.map → stellarService.client-DvA-2pXq.js.map} +1 -1
  47. package/dist/tests/loopSdkContract.test.d.ts +1 -0
  48. package/dist/tests/useAutoConnectCanton.test.d.ts +1 -0
  49. package/dist/tests/useCantonRestoration.test.d.ts +1 -0
  50. package/package.json +3 -2
  51. package/dist/core/types/danal.d.ts +0 -1
  52. package/dist/index-ByakOYXP.js.map +0 -1
  53. package/dist/index-nTqoaD0B.js.map +0 -1
  54. package/dist/services/internal/danalService.d.ts +0 -8
  55. /package/dist/tests/{danalService.test.d.ts → loopCantonConnector.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";
@@ -166,7 +163,6 @@ const CHAIN_IDS = {
166
163
  STELLAR: "stellar-mainnet",
167
164
  STELLAR_TESTNET: "stellar-testnet",
168
165
  CANTON: "canton",
169
- DANAL_TESTNET: "danal-testnet",
170
166
  };
171
167
  const chainTypeToDefaultChainIdMap = {
172
168
  [squidTypes.ChainType.EVM]: CHAIN_IDS.ETHEREUM,
@@ -177,7 +173,6 @@ const chainTypeToDefaultChainIdMap = {
177
173
  [squidTypes.ChainType.XRPL]: CHAIN_IDS.XRPL,
178
174
  [squidTypes.ChainType.STELLAR]: CHAIN_IDS.STELLAR,
179
175
  [squidTypes.ChainType.CANTON]: CHAIN_IDS.CANTON,
180
- [squidTypes.ChainType.DANAL]: CHAIN_IDS.DANAL_TESTNET,
181
176
  };
182
177
  const chainTypeToNativeTokenAddressMap = {
183
178
  [squidTypes.ChainType.EVM]: nativeEvmTokenAddress,
@@ -188,7 +183,6 @@ const chainTypeToNativeTokenAddressMap = {
188
183
  [squidTypes.ChainType.XRPL]: nativeXrplTokenAddress,
189
184
  [squidTypes.ChainType.STELLAR]: nativeStellarTokenAddress,
190
185
  [squidTypes.ChainType.CANTON]: nativeCantonTokenAddress,
191
- [squidTypes.ChainType.DANAL]: nativeDanalTokenAddress,
192
186
  };
193
187
  const defaultConfigValues = {
194
188
  integratorId: INTEGRATOR_ID,
@@ -20970,6 +20964,9 @@ const isBitcoinAddressValid = (address) => {
20970
20964
  }
20971
20965
  };
20972
20966
 
20967
+ function getCantonCommandId(memo) {
20968
+ return `canton-${memo}`;
20969
+ }
20973
20970
  /**
20974
20971
  * Format a Canton connector into a Squid {@link CantonWallet}. Shared by the
20975
20972
  * announce-discovery and injected-provider paths.
@@ -21120,51 +21117,16 @@ function cantonInstrumentKey(instrument) {
21120
21117
  * is already a Decimal, not base units).
21121
21118
  */
21122
21119
  function groupCantonHoldingBalances(acsResponse) {
21120
+ return sumCantonHoldingBalances(parseCantonHoldings(acsResponse).filter((holding) => !holding.locked));
21121
+ }
21122
+ function sumCantonHoldingBalances(holdings) {
21123
21123
  const totals = {};
21124
- for (const holding of parseCantonHoldings(acsResponse)) {
21125
- // Locked holdings are not spendable, skip them
21126
- if (holding.locked)
21127
- continue;
21124
+ for (const holding of holdings) {
21128
21125
  const key = cantonInstrumentKey(holding.instrument);
21129
21126
  const total = totals[key] ?? new BigNumber(0);
21130
21127
  totals[key] = total.plus(new BigNumber(holding.amount));
21131
21128
  }
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
- }
21129
+ return Object.fromEntries(Object.entries(totals).map(([key, total]) => [key, total.toFixed()]));
21168
21130
  }
21169
21131
 
21170
21132
  const solanaTokenProgramIdByAddress = {
@@ -21503,15 +21465,6 @@ function isOnChainTxData(squidData) {
21503
21465
  function isDepositAddressDirectTransferRoute(squidData) {
21504
21466
  return squidData.type === squidTypes.SquidDataType.DepositAddressDirectTransfer;
21505
21467
  }
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
21468
  function getHistoryTransactionId(tx) {
21516
21469
  switch (tx.txType) {
21517
21470
  case exports.HistoryTxType.SWAP:
@@ -22036,11 +21989,6 @@ const chainTypeToTrimLength = {
22036
21989
  start: 3,
22037
21990
  end: 3,
22038
21991
  },
22039
- // PCI0180...E77E
22040
- [squidTypes.ChainType.DANAL]: {
22041
- start: 7,
22042
- end: 4,
22043
- },
22044
21992
  };
22045
21993
  const truncateWithEllipsis = (value, startLength, endLength) => value.length <= startLength + endLength
22046
21994
  ? value
@@ -22089,8 +22037,6 @@ const isWalletAddressValid = (chainData, address) => {
22089
22037
  return isStellarAddressValid(address);
22090
22038
  case squidTypes.ChainType.CANTON:
22091
22039
  return isCantonAddressValid(address);
22092
- case squidTypes.ChainType.DANAL:
22093
- return isDanalAddressValid(address);
22094
22040
  }
22095
22041
  };
22096
22042
  const redirectToExtensionsStore = (wallet) => {
@@ -22375,8 +22321,6 @@ const connectByChainType = async (chainType, wallet, defaultChain, params) => {
22375
22321
  connector,
22376
22322
  },
22377
22323
  });
22378
- case squidTypes.ChainType.DANAL:
22379
- return null;
22380
22324
  }
22381
22325
  };
22382
22326
  const getChainTypesToConnect = (selectedChainTypes, defaultChain, supportedChains) => {
@@ -22501,6 +22445,8 @@ const cancelConnectWallet = (wallet) => {
22501
22445
  return;
22502
22446
  }
22503
22447
  switch (wallet.type) {
22448
+ case squidTypes.ChainType.CANTON:
22449
+ return wallet.connector.cancelConnect?.();
22504
22450
  case squidTypes.ChainType.XRPL:
22505
22451
  return wallet.connector.cancelConnect?.();
22506
22452
  default:
@@ -23361,7 +23307,7 @@ const handleDestinationAddressOnSwapChange = ({ fromChainId, fromTokenAddress, t
23361
23307
  });
23362
23308
  let destinationTokenAddress = toChainId && !toTokenAddress
23363
23309
  ? defaultToAddress
23364
- : toTokenAddress ?? swapRoute?.toTokenAddress;
23310
+ : (toTokenAddress ?? swapRoute?.toTokenAddress);
23365
23311
  const lowerCaseDestinationTokenAddress = destinationTokenAddress?.toLowerCase();
23366
23312
  if (destinationTokensFiltered.find((t) => t.address === lowerCaseDestinationTokenAddress) === undefined) {
23367
23313
  destinationTokenAddress = defaultToAddress;
@@ -23413,7 +23359,7 @@ const getNewSwapParamsFromInput = ({ inputParams, initialSwapRoute, tokens, chai
23413
23359
  toTokenAddress === initialSwapRoute?.fromTokenAddress;
23414
23360
  const sourceTokenAddress = sameNewDestAndInitialSource || onlySourceChainIdChanged
23415
23361
  ? defaultFromAddress
23416
- : fromTokenAddress ?? initialSwapRoute?.fromTokenAddress;
23362
+ : (fromTokenAddress ?? initialSwapRoute?.fromTokenAddress);
23417
23363
  let destinationTokenAddress = undefined;
23418
23364
  if (!sameNewSourceAndInitialDest) {
23419
23365
  if (onlySourceChainIdChanged) {
@@ -23735,7 +23681,7 @@ const filterViewableTokens = (tokens, config, direction) => {
23735
23681
  };
23736
23682
  const getSecretNetworkBalances = async (chainData, cosmosAddress, squidTokens, keplrTypeWallet) => {
23737
23683
  const squidSecretTokens = squidTokens.filter((t) => t.chainId === CHAIN_IDS.SECRET);
23738
- const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-29Ak2qp5.js'); });
23684
+ const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-GHdReJBs.js'); });
23739
23685
  return fetchAllSecretBalances(chainData, cosmosAddress, squidSecretTokens, keplrTypeWallet);
23740
23686
  };
23741
23687
  function getTokenAssetsKey(token) {
@@ -25316,8 +25262,7 @@ const searchTokens = (query, tokens, chains) => {
25316
25262
  .filter((chain) => chain.nativeCurrency.symbol.toLowerCase() === query.toLowerCase() ||
25317
25263
  chain.nativeCurrency.name.toLowerCase().includes(query.toLowerCase()) ||
25318
25264
  chain.networkName.toLowerCase().includes(query.toLowerCase()) ||
25319
- (chain.axelarChainName?.toLowerCase().includes(query.toLowerCase()) ??
25320
- false))
25265
+ chain.axelarChainName.toLowerCase().includes(query.toLowerCase()))
25321
25266
  .map((chain) => tokens.find((token) => token.chainId === chain.chainId &&
25322
25267
  token.address.toLowerCase() === nativeEvmTokenAddress))
25323
25268
  .filter((token) => token !== undefined);
@@ -25730,7 +25675,7 @@ const useSquidTokens = (direction) => {
25730
25675
  config.availableChains,
25731
25676
  direction,
25732
25677
  ]);
25733
- const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, danalTokens, } = React.useMemo(() => {
25678
+ const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = React.useMemo(() => {
25734
25679
  return tokens?.reduce((acc, token) => {
25735
25680
  switch (token.type) {
25736
25681
  case squidTypes.ChainType.EVM:
@@ -25757,9 +25702,6 @@ const useSquidTokens = (direction) => {
25757
25702
  case squidTypes.ChainType.CANTON:
25758
25703
  acc.cantonTokens.push(token);
25759
25704
  break;
25760
- case squidTypes.ChainType.DANAL:
25761
- acc.danalTokens.push(token);
25762
- break;
25763
25705
  }
25764
25706
  return acc;
25765
25707
  }, {
@@ -25771,7 +25713,6 @@ const useSquidTokens = (direction) => {
25771
25713
  xrplTokens: [],
25772
25714
  stellarTokens: [],
25773
25715
  cantonTokens: [],
25774
- danalTokens: [],
25775
25716
  });
25776
25717
  }, [tokens]);
25777
25718
  const findToken = React.useCallback((address, chainId) => {
@@ -25790,7 +25731,6 @@ const useSquidTokens = (direction) => {
25790
25731
  xrplTokens,
25791
25732
  stellarTokens,
25792
25733
  cantonTokens,
25793
- danalTokens,
25794
25734
  };
25795
25735
  };
25796
25736
 
@@ -26327,7 +26267,6 @@ const chainTypeToRefetchInterval = {
26327
26267
  [squidTypes.ChainType.XRPL]: 1_000,
26328
26268
  [squidTypes.ChainType.STELLAR]: 1_000,
26329
26269
  [squidTypes.ChainType.CANTON]: 1_000,
26330
- [squidTypes.ChainType.DANAL]: 1_000,
26331
26270
  };
26332
26271
  /**
26333
26272
  * Returns the status refetch interval of a Send transaction
@@ -26594,7 +26533,7 @@ const useSquidChains = (direction) => {
26594
26533
  const chain = findChain(chainId);
26595
26534
  return chain?.chainType;
26596
26535
  }, [findChain]);
26597
- const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains, danalChains, } = React.useMemo(() => {
26536
+ const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains, } = React.useMemo(() => {
26598
26537
  return chains.reduce((acc, chain) => {
26599
26538
  switch (chain.chainType) {
26600
26539
  case squidTypes.ChainType.EVM:
@@ -26615,9 +26554,6 @@ const useSquidChains = (direction) => {
26615
26554
  case squidTypes.ChainType.CANTON:
26616
26555
  acc.cantonChains.push(chain);
26617
26556
  break;
26618
- case squidTypes.ChainType.DANAL:
26619
- acc.danalChains.push(chain);
26620
- break;
26621
26557
  }
26622
26558
  return acc;
26623
26559
  }, {
@@ -26627,7 +26563,6 @@ const useSquidChains = (direction) => {
26627
26563
  xrplChains: [],
26628
26564
  stellarChains: [],
26629
26565
  cantonChains: [],
26630
- danalChains: [],
26631
26566
  });
26632
26567
  }, [chains]);
26633
26568
  const { supportedSourceChains, supportedDestinationChains } = React.useMemo(() => {
@@ -26649,7 +26584,6 @@ const useSquidChains = (direction) => {
26649
26584
  xrplChains,
26650
26585
  stellarChains,
26651
26586
  cantonChains,
26652
- danalChains,
26653
26587
  getChainType,
26654
26588
  findChain,
26655
26589
  };
@@ -26963,7 +26897,343 @@ const useBitcoinContext = () => {
26963
26897
  return context;
26964
26898
  };
26965
26899
 
26966
- const REQUEST_TIMEOUT_MS = 60_000;
26900
+ const vParty = (party) => ({ party });
26901
+ const vText = (text) => ({ text });
26902
+ const vNumeric = (numeric) => ({ numeric });
26903
+ const vContractId = (contractId) => ({
26904
+ contractId,
26905
+ });
26906
+ const vList = (elements) => ({
26907
+ list: { elements },
26908
+ });
26909
+ const vRecord = (fields) => ({
26910
+ record: { fields: fields.map(([label, value]) => ({ label, value })) },
26911
+ });
26912
+ const vTextMap = (entries) => ({ textMap: { entries } });
26913
+ const vTimestamp = (isoString) => ({
26914
+ timestamp: String(Math.floor(new Date(isoString).getTime() * 1000)),
26915
+ });
26916
+ function isRecord(value) {
26917
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26918
+ }
26919
+ function normalizeChoiceContextValue(value) {
26920
+ if (typeof value === "string")
26921
+ return value;
26922
+ if (!isRecord(value) || typeof value.tag !== "string")
26923
+ return null;
26924
+ switch (value.tag) {
26925
+ case "AV_Text":
26926
+ case "AV_ContractId":
26927
+ return typeof value.value === "string"
26928
+ ? { tag: value.tag, value: value.value }
26929
+ : null;
26930
+ case "AV_Bool":
26931
+ return typeof value.value === "boolean"
26932
+ ? { tag: value.tag, value: value.value }
26933
+ : null;
26934
+ case "AV_List":
26935
+ if (!Array.isArray(value.value))
26936
+ return null;
26937
+ return {
26938
+ tag: value.tag,
26939
+ value: value.value.flatMap((entry) => {
26940
+ const normalized = normalizeChoiceContextValue(entry);
26941
+ return normalized ? [normalized] : [];
26942
+ }),
26943
+ };
26944
+ default: {
26945
+ const inner = value.value;
26946
+ if (typeof inner === "string" ||
26947
+ typeof inner === "number" ||
26948
+ typeof inner === "boolean" ||
26949
+ inner === null) {
26950
+ return { tag: value.tag, value: inner };
26951
+ }
26952
+ if (Array.isArray(inner)) {
26953
+ return {
26954
+ tag: value.tag,
26955
+ value: inner.flatMap((entry) => {
26956
+ const normalized = normalizeChoiceContextValue(entry);
26957
+ return normalized ? [normalized] : [];
26958
+ }),
26959
+ };
26960
+ }
26961
+ if (!isRecord(inner))
26962
+ return null;
26963
+ const normalizedObject = {};
26964
+ for (const [key, entry] of Object.entries(inner)) {
26965
+ if (typeof entry !== "string" &&
26966
+ typeof entry !== "number" &&
26967
+ typeof entry !== "boolean" &&
26968
+ entry !== null) {
26969
+ return null;
26970
+ }
26971
+ normalizedObject[key] = entry;
26972
+ }
26973
+ return { tag: value.tag, value: normalizedObject };
26974
+ }
26975
+ }
26976
+ }
26977
+ function stringifyChoiceContextValue(value) {
26978
+ return typeof value === "string" ? value : String(value);
26979
+ }
26980
+ function choiceContextValueToAnyValue(value) {
26981
+ if (typeof value === "string") {
26982
+ return {
26983
+ variant: { constructor: "AV_Text", value: vText(value) },
26984
+ };
26985
+ }
26986
+ switch (value.tag) {
26987
+ case "AV_ContractId":
26988
+ if (typeof value.value !== "string") {
26989
+ throw new TypeError("AV_ContractId values must be strings");
26990
+ }
26991
+ return {
26992
+ variant: { constructor: value.tag, value: vContractId(value.value) },
26993
+ };
26994
+ case "AV_Bool":
26995
+ if (typeof value.value !== "boolean") {
26996
+ throw new TypeError("AV_Bool values must be booleans");
26997
+ }
26998
+ return {
26999
+ variant: { constructor: value.tag, value: { bool: value.value } },
27000
+ };
27001
+ case "AV_List":
27002
+ if (!Array.isArray(value.value)) {
27003
+ throw new TypeError("AV_List values must be arrays");
27004
+ }
27005
+ return {
27006
+ variant: {
27007
+ constructor: value.tag,
27008
+ value: vList(value.value.map(choiceContextValueToAnyValue)),
27009
+ },
27010
+ };
27011
+ default:
27012
+ return {
27013
+ variant: {
27014
+ constructor: value.tag,
27015
+ value: vText(stringifyChoiceContextValue(value.value)),
27016
+ },
27017
+ };
27018
+ }
27019
+ }
27020
+
27021
+ /** Metadata key used by Splice token-standard transfers to carry our Squid orderhash memo. */
27022
+ const CANTON_MEMO_KEY = "splice.lfdecentralizedtrust.org/reason";
27023
+ /** Interface id for the transfer factory contract returned by the token registry. */
27024
+ const TRANSFER_FACTORY_INTERFACE_ID = "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory";
27025
+ /** Choice exercised on the transfer factory to create the token-standard transfer. */
27026
+ const TRANSFER_FACTORY_CHOICE = "TransferFactory_Transfer";
27027
+ /** CIP-056 (non-Amulet) tokens expose a registrar parameterized by admin party. */
27028
+ const CIP056_REGISTRAR_BASE = "https://api.utilities.digitalasset.com/api/token-standard/v0/registrars";
27029
+ const TRANSFER_REQUESTED_AT_SKEW_MS = 60_000;
27030
+ const TRANSFER_EXECUTE_WINDOW_MS = 24 * 60 * 60 * 1000;
27031
+ function resolveCantonRegistryUrl(instrument) {
27032
+ if (instrument.id === CANTON_AMULET_INSTRUMENT_ID) {
27033
+ return CANTON_AMULET_REGISTRY_URL;
27034
+ }
27035
+ return `${CIP056_REGISTRAR_BASE}/${encodeURIComponent(instrument.admin)}`;
27036
+ }
27037
+ /**
27038
+ * Build the plain JSON arguments expected by the registry transfer-factory
27039
+ * endpoint before it adds choice context and disclosed contracts.
27040
+ */
27041
+ function buildTransferChoiceArgs({ sender, receiver, amount, instrument, inputHoldingCids, memo, nowMs, }) {
27042
+ return {
27043
+ expectedAdmin: instrument.admin,
27044
+ transfer: {
27045
+ sender,
27046
+ receiver,
27047
+ amount,
27048
+ instrumentId: { admin: instrument.admin, id: instrument.id },
27049
+ requestedAt: new Date(nowMs - TRANSFER_REQUESTED_AT_SKEW_MS).toISOString(),
27050
+ executeBefore: new Date(nowMs + TRANSFER_EXECUTE_WINDOW_MS).toISOString(),
27051
+ inputHoldingCids,
27052
+ meta: { values: { [CANTON_MEMO_KEY]: memo } },
27053
+ },
27054
+ extraArgs: { context: { values: {} }, meta: { values: {} } },
27055
+ };
27056
+ }
27057
+ /**
27058
+ * Canton's Holding contracts follow Bitcoin-style UTXOs model.
27059
+ *
27060
+ * Pick Holding contract ids covering `amount` for the instrument: prefer a
27061
+ * single exact-amount holding, else accumulate largest-first.
27062
+ */
27063
+ function selectInputHoldingCids(holdings, instrument, amount) {
27064
+ const spendableHoldings = holdings.filter((h) => !h.locked &&
27065
+ h.instrument.admin === instrument.admin &&
27066
+ h.instrument.id === instrument.id);
27067
+ const target = new BigNumber(amount);
27068
+ const exact = spendableHoldings.find((h) => new BigNumber(h.amount).eq(target));
27069
+ if (exact)
27070
+ return [exact.contractId];
27071
+ const sorted = [...spendableHoldings].sort((a, b) => new BigNumber(b.amount).comparedTo(new BigNumber(a.amount)));
27072
+ const selected = [];
27073
+ let sum = new BigNumber(0);
27074
+ for (const holding of sorted) {
27075
+ selected.push(holding.contractId);
27076
+ sum = sum.plus(holding.amount);
27077
+ if (sum.gte(target))
27078
+ break;
27079
+ }
27080
+ if (sum.lt(target)) {
27081
+ throw new Error("Insufficient Canton holdings to cover transfer amount");
27082
+ }
27083
+ return selected;
27084
+ }
27085
+ async function fetchInputHoldingCids(connector, party, instrument, amount) {
27086
+ const ledgerEnd = await connector.ledgerApi({
27087
+ requestMethod: "get",
27088
+ resource: "/v2/state/ledger-end",
27089
+ });
27090
+ const activeContracts = await connector.ledgerApi({
27091
+ requestMethod: "post",
27092
+ resource: "/v2/state/active-contracts",
27093
+ body: buildHoldingsAcsRequestBody(party, String(ledgerEnd.offset)),
27094
+ });
27095
+ return selectInputHoldingCids(parseCantonHoldings(activeContracts), instrument, amount);
27096
+ }
27097
+ /**
27098
+ * Ask the token registry for the transfer factory contract and extra context
27099
+ * required to exercise its transfer choice.
27100
+ */
27101
+ async function fetchTransferFactory(registryUrl, choiceArgs) {
27102
+ const response = await fetch(`${registryUrl}/registry/transfer-instruction/v1/transfer-factory`, {
27103
+ method: "POST",
27104
+ headers: { "Content-Type": "application/json" },
27105
+ body: JSON.stringify({
27106
+ choiceArguments: choiceArgs,
27107
+ excludeDebugFields: true,
27108
+ }),
27109
+ });
27110
+ if (!response.ok) {
27111
+ throw new Error(`Canton transfer-factory request failed (${response.status})`);
27112
+ }
27113
+ return response.json();
27114
+ }
27115
+ /**
27116
+ * Convert transfer choice args into strict Daml Value JSON for the wallet's
27117
+ * prepareExecute ExerciseCommand.
27118
+ */
27119
+ function buildPrepareChoiceArgument(choiceArgs) {
27120
+ const contextValues = Object.entries(choiceArgs.extraArgs.context?.values ?? {})
27121
+ .map(([key, value]) => {
27122
+ const normalized = normalizeChoiceContextValue(value);
27123
+ return normalized
27124
+ ? { key, value: choiceContextValueToAnyValue(normalized) }
27125
+ : undefined;
27126
+ })
27127
+ .filter((entry) => Boolean(entry));
27128
+ return vRecord([
27129
+ ["expectedAdmin", vParty(choiceArgs.expectedAdmin)],
27130
+ [
27131
+ "transfer",
27132
+ vRecord([
27133
+ ["sender", vParty(choiceArgs.transfer.sender)],
27134
+ ["receiver", vParty(choiceArgs.transfer.receiver)],
27135
+ ["amount", vNumeric(choiceArgs.transfer.amount)],
27136
+ [
27137
+ "instrumentId",
27138
+ vRecord([
27139
+ ["admin", vParty(choiceArgs.transfer.instrumentId.admin)],
27140
+ ["id", vText(choiceArgs.transfer.instrumentId.id)],
27141
+ ]),
27142
+ ],
27143
+ ["requestedAt", vTimestamp(choiceArgs.transfer.requestedAt)],
27144
+ ["executeBefore", vTimestamp(choiceArgs.transfer.executeBefore)],
27145
+ [
27146
+ "inputHoldingCids",
27147
+ vList(choiceArgs.transfer.inputHoldingCids.map(vContractId)),
27148
+ ],
27149
+ [
27150
+ "meta",
27151
+ vRecord([
27152
+ [
27153
+ "values",
27154
+ vTextMap(Object.entries(choiceArgs.transfer.meta.values).map(([key, value]) => ({ key, value: vText(value) }))),
27155
+ ],
27156
+ ]),
27157
+ ],
27158
+ ]),
27159
+ ],
27160
+ [
27161
+ "extraArgs",
27162
+ vRecord([
27163
+ ["context", vRecord([["values", vTextMap(contextValues)]])],
27164
+ ["meta", vRecord([["values", vTextMap([])]])],
27165
+ ]),
27166
+ ],
27167
+ ]);
27168
+ }
27169
+ /**
27170
+ * Convert a registry disclosed contract into the wallet submission shape,
27171
+ * dropping debug fields and entries without a usable createdEventBlob.
27172
+ */
27173
+ function toDisclosedContract(raw) {
27174
+ if (typeof raw.createdEventBlob !== "string")
27175
+ return null;
27176
+ return {
27177
+ createdEventBlob: raw.createdEventBlob,
27178
+ contractId: typeof raw.contractId === "string" ? raw.contractId : undefined,
27179
+ synchronizerId: typeof raw.synchronizerId === "string" ? raw.synchronizerId : undefined,
27180
+ };
27181
+ }
27182
+ /**
27183
+ * Build a Splice token-standard transfer of `amount` of `token` from `sender`
27184
+ * to `receiver`, carrying `memo`, ready to hand to a wallet's prepareExecute.
27185
+ * Returns the full prepareExecute params (commandId + commands + actAs +
27186
+ * disclosedContracts).
27187
+ */
27188
+ async function buildCantonTransfer(connector, { sender, receiver, amount, token, memo, nowMs, }) {
27189
+ if (!token.originalAddress) {
27190
+ throw new Error("Canton token originalAddress is required");
27191
+ }
27192
+ const instrument = parseCantonInstrument(token.originalAddress);
27193
+ if (!instrument) {
27194
+ throw new Error(`Invalid Canton token address: ${token.originalAddress}`);
27195
+ }
27196
+ const registryUrl = resolveCantonRegistryUrl(instrument);
27197
+ const inputHoldingCids = await fetchInputHoldingCids(connector, sender, instrument, amount);
27198
+ const choiceArgs = buildTransferChoiceArgs({
27199
+ sender,
27200
+ receiver,
27201
+ amount,
27202
+ instrument,
27203
+ inputHoldingCids,
27204
+ memo,
27205
+ nowMs,
27206
+ });
27207
+ const factory = await fetchTransferFactory(registryUrl, choiceArgs);
27208
+ const choiceContextData = factory.choiceContext.choiceContextData;
27209
+ if (!choiceContextData) {
27210
+ throw new Error("Transfer factory choice context is missing");
27211
+ }
27212
+ choiceArgs.extraArgs.context = choiceContextData;
27213
+ const disclosedContracts = (factory.choiceContext?.disclosedContracts ?? []).flatMap((raw) => {
27214
+ const disclosedContract = toDisclosedContract(raw);
27215
+ return disclosedContract ? [disclosedContract] : [];
27216
+ });
27217
+ // Command ID: client-supplied identifier used to deduplicate submissions
27218
+ const commandId = getCantonCommandId(memo);
27219
+ return {
27220
+ commandId,
27221
+ commands: [
27222
+ {
27223
+ ExerciseCommand: {
27224
+ templateId: TRANSFER_FACTORY_INTERFACE_ID,
27225
+ contractId: factory.factoryId,
27226
+ choice: TRANSFER_FACTORY_CHOICE,
27227
+ choiceArgument: buildPrepareChoiceArgument(choiceArgs),
27228
+ },
27229
+ },
27230
+ ],
27231
+ actAs: [sender],
27232
+ disclosedContracts,
27233
+ };
27234
+ }
27235
+
27236
+ const REQUEST_TIMEOUT_MS$1 = 60_000;
26967
27237
  function pickAddress(accounts) {
26968
27238
  return (accounts.find((a) => a.primary) ?? accounts[0])?.partyId;
26969
27239
  }
@@ -26972,6 +27242,7 @@ function pickAddress(accounts) {
26972
27242
  * (bound to its announce `target`) and exposes the lifecycle methods.
26973
27243
  */
26974
27244
  class CantonBrowserConnector {
27245
+ kind = "cip103";
26975
27246
  providerId;
26976
27247
  name;
26977
27248
  target;
@@ -26986,7 +27257,7 @@ class CantonBrowserConnector {
26986
27257
  client ??
26987
27258
  dapp.createCantonClient(new dapp.WindowTransport(win, {
26988
27259
  target: this.target,
26989
- timeout: REQUEST_TIMEOUT_MS,
27260
+ timeout: REQUEST_TIMEOUT_MS$1,
26990
27261
  }));
26991
27262
  }
26992
27263
  get accounts() {
@@ -27032,6 +27303,28 @@ class CantonBrowserConnector {
27032
27303
  this._accounts = await this.client.listAccounts();
27033
27304
  return this._accounts;
27034
27305
  }
27306
+ async getBalances(partyId) {
27307
+ const { offset } = await this.ledgerApi({
27308
+ requestMethod: "get",
27309
+ resource: "/v2/state/ledger-end",
27310
+ });
27311
+ const contracts = await this.ledgerApi({
27312
+ requestMethod: "post",
27313
+ resource: "/v2/state/active-contracts",
27314
+ body: buildHoldingsAcsRequestBody(partyId, String(offset)),
27315
+ });
27316
+ return groupCantonHoldingBalances(contracts);
27317
+ }
27318
+ async transfer(params) {
27319
+ const payload = await buildCantonTransfer(this, params);
27320
+ const result = await this.prepareExecuteAndWait(payload);
27321
+ const updateId = result.tx?.payload?.updateId ?? "";
27322
+ return {
27323
+ status: "completed",
27324
+ commandId: getCantonCommandId(params.memo),
27325
+ updateId,
27326
+ };
27327
+ }
27035
27328
  on(event, handler) {
27036
27329
  this.listeners[event].add(handler);
27037
27330
  }
@@ -27138,6 +27431,313 @@ function discoverInjectedCantonWallets(win) {
27138
27431
  });
27139
27432
  }
27140
27433
 
27434
+ class CantonConnectionCancelledError extends Error {
27435
+ code = 4001;
27436
+ constructor() {
27437
+ super("Wallet connection cancelled");
27438
+ }
27439
+ }
27440
+
27441
+ const REQUEST_TIMEOUT_MS = 5 * 60_000;
27442
+ const OVERLAY_ID = "loop-sdk-connect-overlay";
27443
+ class LoopSubmissionUnknownError extends Error {
27444
+ constructor(cause) {
27445
+ super("Loop transfer outcome is unknown. Check Loop before trying again.", {
27446
+ cause,
27447
+ });
27448
+ }
27449
+ }
27450
+ function parseLoopTransferUpdate(value, commandId) {
27451
+ if (!value || typeof value !== "object")
27452
+ return undefined;
27453
+ const response = value;
27454
+ if (response.command_id !== commandId)
27455
+ return undefined;
27456
+ if (response.status === "failed" || response.error) {
27457
+ const error = response.error;
27458
+ return {
27459
+ status: "failed",
27460
+ commandId,
27461
+ message: typeof error?.error_message === "string"
27462
+ ? error.error_message
27463
+ : "Loop transaction failed",
27464
+ };
27465
+ }
27466
+ if (typeof response.update_id === "string" && response.update_id) {
27467
+ return { status: "completed", commandId, updateId: response.update_id };
27468
+ }
27469
+ return undefined;
27470
+ }
27471
+ class LoopCantonConnector {
27472
+ loadSdk;
27473
+ kind = "loop";
27474
+ providerId = "canton-loop";
27475
+ name = "Loop";
27476
+ target = undefined;
27477
+ module;
27478
+ initialization;
27479
+ provider;
27480
+ pendingConnection;
27481
+ connectionStart = Promise.resolve();
27482
+ transfers = new Map();
27483
+ listeners = { connect: new Set(), disconnect: new Set() };
27484
+ constructor(loadSdk = () => import('@fivenorth/loop-sdk')) {
27485
+ this.loadSdk = loadSdk;
27486
+ }
27487
+ get accounts() {
27488
+ return this.provider
27489
+ ? [{ partyId: this.provider.party_id, primary: true }]
27490
+ : [];
27491
+ }
27492
+ get address() {
27493
+ return this.provider?.party_id;
27494
+ }
27495
+ connect() {
27496
+ return this.startConnection(true);
27497
+ }
27498
+ autoConnect() {
27499
+ return this.startConnection(false);
27500
+ }
27501
+ cancelConnect() {
27502
+ if (!this.pendingConnection)
27503
+ return;
27504
+ this.rejectConnection(new CantonConnectionCancelledError());
27505
+ this.module?.loop.logout();
27506
+ }
27507
+ async disconnect() {
27508
+ this.cancelConnect();
27509
+ this.module?.loop.logout();
27510
+ this.provider = undefined;
27511
+ this.emit("disconnect");
27512
+ }
27513
+ async getAccounts() {
27514
+ const provider = this.requireProvider();
27515
+ try {
27516
+ const account = await provider.getAccount();
27517
+ if (account.party_id !== provider.party_id) {
27518
+ await this.disconnect();
27519
+ throw new Error("Loop account changed. Connect again.");
27520
+ }
27521
+ return this.accounts;
27522
+ }
27523
+ catch (error) {
27524
+ if (this.module && error instanceof this.module.UnauthorizedError) {
27525
+ await this.disconnect();
27526
+ }
27527
+ throw error;
27528
+ }
27529
+ }
27530
+ async getBalances(partyId) {
27531
+ const provider = this.requireProvider();
27532
+ if (partyId !== provider.party_id) {
27533
+ throw new Error("Loop balances require the connected account");
27534
+ }
27535
+ const holdings = await provider.getHolding();
27536
+ return sumCantonHoldingBalances(holdings.map((holding) => {
27537
+ const amount = new BigNumber(holding.total_unlocked_coin);
27538
+ if (!amount.isFinite() || amount.isNegative()) {
27539
+ throw new Error("Loop returned an invalid balance");
27540
+ }
27541
+ return {
27542
+ instrument: holding.instrument_id,
27543
+ amount: holding.total_unlocked_coin,
27544
+ };
27545
+ }));
27546
+ }
27547
+ transfer(params) {
27548
+ const commandId = getCantonCommandId(params.memo);
27549
+ const existing = this.transfers.get(commandId);
27550
+ if (existing)
27551
+ return existing;
27552
+ const request = this.submitTransfer(params, commandId).catch((error) => {
27553
+ if (!(error instanceof LoopSubmissionUnknownError))
27554
+ this.transfers.delete(commandId);
27555
+ throw error;
27556
+ });
27557
+ this.transfers.set(commandId, request);
27558
+ return request;
27559
+ }
27560
+ on(event, handler) {
27561
+ this.listeners[event].add(handler);
27562
+ }
27563
+ off(event, handler) {
27564
+ this.listeners[event].delete(handler);
27565
+ }
27566
+ emit(event, ...args) {
27567
+ this.listeners[event].forEach((handler) => handler(...args));
27568
+ }
27569
+ initialize() {
27570
+ if (!this.initialization) {
27571
+ this.initialization = this.loadSdk()
27572
+ .then((module) => {
27573
+ module.loop.init({
27574
+ appName: SQUID_METADATA.name,
27575
+ network: "mainnet",
27576
+ onAccept: (provider) => this.acceptConnection(provider),
27577
+ onReject: () => {
27578
+ this.rejectConnection(new CantonConnectionCancelledError());
27579
+ this.provider = undefined;
27580
+ this.emit("disconnect");
27581
+ },
27582
+ });
27583
+ this.module = module;
27584
+ })
27585
+ .catch((error) => {
27586
+ this.initialization = undefined;
27587
+ throw error;
27588
+ });
27589
+ }
27590
+ return this.initialization;
27591
+ }
27592
+ startConnection(interactive) {
27593
+ if (this.pendingConnection)
27594
+ return this.pendingConnection.promise;
27595
+ let resolve;
27596
+ let reject;
27597
+ const promise = new Promise((accept, decline) => {
27598
+ resolve = accept;
27599
+ reject = decline;
27600
+ });
27601
+ const timer = setTimeout(() => {
27602
+ this.rejectConnection(new Error("Loop connection timed out"));
27603
+ this.module?.loop.logout();
27604
+ }, REQUEST_TIMEOUT_MS);
27605
+ let observer;
27606
+ const pending = {
27607
+ promise,
27608
+ resolve,
27609
+ reject,
27610
+ cleanup: () => {
27611
+ clearTimeout(timer);
27612
+ observer?.disconnect();
27613
+ },
27614
+ };
27615
+ this.pendingConnection = pending;
27616
+ this.connectionStart = this.connectionStart
27617
+ .then(async () => {
27618
+ await this.initialize();
27619
+ if (this.pendingConnection !== pending)
27620
+ return;
27621
+ if (interactive && typeof MutationObserver !== "undefined") {
27622
+ let sawOverlay = false;
27623
+ observer = new MutationObserver(() => {
27624
+ const overlay = document.getElementById(OVERLAY_ID);
27625
+ if (overlay)
27626
+ sawOverlay = true;
27627
+ if (sawOverlay && !overlay && this.pendingConnection === pending) {
27628
+ this.cancelConnect();
27629
+ }
27630
+ });
27631
+ observer.observe(document.body, { childList: true });
27632
+ }
27633
+ const sdk = this.module.loop;
27634
+ if (interactive)
27635
+ await sdk.connect();
27636
+ else
27637
+ await sdk.autoConnect();
27638
+ if (!interactive && this.pendingConnection === pending) {
27639
+ this.rejectConnection(new Error("No saved Loop connection"));
27640
+ }
27641
+ else if (interactive &&
27642
+ this.pendingConnection !== pending &&
27643
+ !this.provider) {
27644
+ sdk.logout();
27645
+ }
27646
+ })
27647
+ .catch((error) => {
27648
+ if (this.pendingConnection === pending)
27649
+ this.rejectConnection(error);
27650
+ });
27651
+ return promise;
27652
+ }
27653
+ acceptConnection(provider) {
27654
+ const pending = this.pendingConnection;
27655
+ if (!pending)
27656
+ return;
27657
+ if (!provider.party_id) {
27658
+ this.rejectConnection(new Error("Loop returned no account"));
27659
+ return;
27660
+ }
27661
+ this.provider = provider;
27662
+ this.pendingConnection = undefined;
27663
+ pending.cleanup();
27664
+ pending.resolve({ address: provider.party_id, accounts: this.accounts });
27665
+ this.emit("connect", provider.party_id);
27666
+ }
27667
+ rejectConnection(error) {
27668
+ const pending = this.pendingConnection;
27669
+ if (!pending)
27670
+ return;
27671
+ this.pendingConnection = undefined;
27672
+ pending.cleanup();
27673
+ pending.reject(error);
27674
+ }
27675
+ requireProvider() {
27676
+ if (!this.provider)
27677
+ throw new Error("Loop wallet is not connected");
27678
+ return this.provider;
27679
+ }
27680
+ async submitTransfer(params, commandId) {
27681
+ const provider = this.requireProvider();
27682
+ if (params.sender !== provider.party_id)
27683
+ throw new Error("Loop account does not match the sender");
27684
+ const instrument = params.token.originalAddress
27685
+ ? parseCantonInstrument(params.token.originalAddress)
27686
+ : null;
27687
+ if (!instrument)
27688
+ throw new Error("Canton token originalAddress is required");
27689
+ if (!new BigNumber(params.amount).isFinite() ||
27690
+ !new BigNumber(params.amount).isPositive()) {
27691
+ throw new Error("Canton transfer amount must be positive");
27692
+ }
27693
+ const payload = await provider.connection.prepareTransfer(provider.getAuthToken(), {
27694
+ recipient: params.receiver,
27695
+ amount: params.amount,
27696
+ instrument: {
27697
+ instrument_admin: instrument.admin,
27698
+ instrument_id: instrument.id,
27699
+ },
27700
+ memo: params.memo,
27701
+ requested_at: new Date(params.nowMs - 60_000).toISOString(),
27702
+ execute_before: new Date(params.nowMs + 24 * 60 * 60_000).toISOString(),
27703
+ });
27704
+ if (this.provider !== provider)
27705
+ throw new Error("Loop account disconnected before submission");
27706
+ let response;
27707
+ let timer;
27708
+ try {
27709
+ const submission = { ...payload, commandId };
27710
+ const submitted = provider.submitAndWaitForTransaction(submission, {
27711
+ requestTimeout: REQUEST_TIMEOUT_MS,
27712
+ });
27713
+ response = await Promise.race([
27714
+ submitted,
27715
+ new Promise((_, reject) => {
27716
+ timer = setTimeout(() => reject(new Error("Loop submission timed out")), REQUEST_TIMEOUT_MS);
27717
+ }),
27718
+ ]);
27719
+ }
27720
+ catch (error) {
27721
+ if (this.module && error instanceof this.module.RejectRequestError) {
27722
+ throw Object.assign(new Error("Loop transaction rejected"), {
27723
+ code: 4001,
27724
+ });
27725
+ }
27726
+ throw new LoopSubmissionUnknownError(error);
27727
+ }
27728
+ finally {
27729
+ clearTimeout(timer);
27730
+ }
27731
+ const update = parseLoopTransferUpdate(response, commandId);
27732
+ if (update?.status === "failed")
27733
+ throw new Error(update.message);
27734
+ if (!update)
27735
+ throw new LoopSubmissionUnknownError();
27736
+ return update;
27737
+ }
27738
+ }
27739
+ const loopCantonConnector = new LoopCantonConnector();
27740
+
27141
27741
  /**
27142
27742
  * Discovers Canton browser-extension wallets and maps each to a
27143
27743
  * {@link CantonWallet}: CIP-103 announce-protocol wallets (on mount and on
@@ -27184,7 +27784,19 @@ function useCantonWallets() {
27184
27784
  return;
27185
27785
  const announced = (await discoverCantonProviders()).map(toAnnouncedWallet);
27186
27786
  const injected = discoverInjectedCantonWallets().map(toInjectedWallet);
27187
- setWallets(mergeInjectedCantonWallets(announced, injected));
27787
+ const loopWallet = {
27788
+ ...formatCantonWallet({
27789
+ connectorId: "canton-loop",
27790
+ name: "Loop",
27791
+ icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/master/wallets/canton-loop.svg",
27792
+ connector: loopCantonConnector,
27793
+ }),
27794
+ links: { website: "https://cantonloop.com" },
27795
+ isInstalled: () => false,
27796
+ skipInstallCheck: true,
27797
+ isMobile: true,
27798
+ };
27799
+ setWallets(mergeInjectedCantonWallets(announced, [...injected, loopWallet]));
27188
27800
  }, [toAnnouncedWallet, toInjectedWallet]);
27189
27801
  React.useEffect(() => {
27190
27802
  refresh();
@@ -27215,7 +27827,9 @@ function useCanton() {
27215
27827
  // Try to reconnect silently first (existing session, no popup)
27216
27828
  result = await wallet.connector.autoConnect();
27217
27829
  }
27218
- catch {
27830
+ catch (error) {
27831
+ if (error instanceof CantonConnectionCancelledError)
27832
+ throw error;
27219
27833
  // Otherwise prompt the wallet to connect
27220
27834
  result = await wallet.connector.connect();
27221
27835
  }
@@ -27237,8 +27851,7 @@ function useCanton() {
27237
27851
  wallet: recentCantonWallet,
27238
27852
  address,
27239
27853
  });
27240
- // eslint-disable-next-line react-hooks/exhaustive-deps
27241
- }, [wallets.map((w) => w.connectorId), recentCantonWalletId]);
27854
+ }, [wallets, recentCantonWalletId, setConnectedWallet]);
27242
27855
  React.useEffect(() => {
27243
27856
  const wallet = connectedCantonWallet.wallet;
27244
27857
  if (!wallet) {
@@ -27495,7 +28108,7 @@ function useStellarWallets() {
27495
28108
  try {
27496
28109
  const { allowAllModules: initializeAllModules } = await import('@creit.tech/stellar-wallets-kit');
27497
28110
  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'); });
28111
+ const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-DvA-2pXq.js'); });
27499
28112
  const modules = [...initializeAllModules(), new LedgerModule()];
27500
28113
  const promises = modules.map(async (module) => {
27501
28114
  const isAvailable = await module.isAvailable();
@@ -28074,6 +28687,11 @@ const useWallet = () => {
28074
28687
  }
28075
28688
  }
28076
28689
  catch (error) {
28690
+ const connecting = useWalletStore.getState().connectingWalletState;
28691
+ if (connecting.wallet !== wallet ||
28692
+ connecting.status !== exports.ConnectingWalletStatus.CONNECTING) {
28693
+ return false;
28694
+ }
28077
28695
  console.error("Failed to connect wallet", error);
28078
28696
  walletConnectionError(wallet, error);
28079
28697
  return false;
@@ -28305,8 +28923,6 @@ const useMultiChainWallet = (chain) => {
28305
28923
  chainType: chain.chainType,
28306
28924
  }),
28307
28925
  };
28308
- case squidTypes.ChainType.DANAL:
28309
- return {};
28310
28926
  }
28311
28927
  }, [
28312
28928
  chain?.chainType,
@@ -29145,8 +29761,6 @@ async function createClient(chain) {
29145
29761
  return new StellarRpcClient(chain.rpc);
29146
29762
  case squidTypes.ChainType.CANTON:
29147
29763
  return null;
29148
- case squidTypes.ChainType.DANAL:
29149
- return null;
29150
29764
  }
29151
29765
  }
29152
29766
 
@@ -30161,8 +30775,7 @@ const getAllStellarTokensBalance = async (userAddress, stellarTokens, stellarCha
30161
30775
  };
30162
30776
  /**
30163
30777
  * 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
30778
+ * session. Balance is fetchable only when a Canton wallet is connected, and
30166
30779
  * only for that wallet's party — there's no address-only read.
30167
30780
  */
30168
30781
  const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
@@ -30171,30 +30784,14 @@ const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
30171
30784
  const instrument = parseCantonInstrument(token.originalAddress);
30172
30785
  if (!instrument)
30173
30786
  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);
30787
+ const grouped = await cantonConnector.getBalances(userAddress);
30181
30788
  return grouped[cantonInstrumentKey(instrument)] ?? "0";
30182
30789
  };
30183
30790
  /**
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`.
30791
+ * Fetch balances for all Canton tokens through the connected wallet.
30186
30792
  */
30187
30793
  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);
30794
+ const grouped = await connector.getBalances(partyId);
30198
30795
  return tokens.map((token) => {
30199
30796
  const instrument = token.originalAddress
30200
30797
  ? parseCantonInstrument(token.originalAddress)
@@ -30413,7 +31010,7 @@ const useCantonBalance = ({ chain, token, userAddress, enabled = true, refreshIn
30413
31010
  };
30414
31011
 
30415
31012
  function useNativeTokenForChain(chain) {
30416
- const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, danalTokens, } = useSquidTokens();
31013
+ const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens();
30417
31014
  const getTokensForChainType = () => {
30418
31015
  if (!chain?.chainType)
30419
31016
  return [];
@@ -30434,8 +31031,6 @@ function useNativeTokenForChain(chain) {
30434
31031
  return stellarTokens;
30435
31032
  case squidTypes.ChainType.CANTON:
30436
31033
  return cantonTokens;
30437
- case squidTypes.ChainType.DANAL:
30438
- return danalTokens;
30439
31034
  }
30440
31035
  };
30441
31036
  const nativeTokenForChainType = React.useMemo(() => {
@@ -30696,9 +31291,6 @@ const useNativeBalance = (chain) => {
30696
31291
  case squidTypes.ChainType.CANTON:
30697
31292
  balance = nativeCantonBalance;
30698
31293
  break;
30699
- case squidTypes.ChainType.DANAL:
30700
- balance = undefined;
30701
- break;
30702
31294
  }
30703
31295
  const balanceFormatted = !!balance
30704
31296
  ? formatBNToReadable(balance.value, balance.decimals)
@@ -30738,8 +31330,6 @@ const useNativeBalance = (chain) => {
30738
31330
  return isStellarLoading;
30739
31331
  case squidTypes.ChainType.CANTON:
30740
31332
  return isCantonLoading;
30741
- case squidTypes.ChainType.DANAL:
30742
- return false;
30743
31333
  }
30744
31334
  }, [
30745
31335
  chain?.chainType,
@@ -30965,7 +31555,7 @@ function hederaWalletConnect(parameters) {
30965
31555
  const optionalChains = config.chains.map((x) => x.id);
30966
31556
  if (!optionalChains.length)
30967
31557
  return;
30968
- const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-DtFDz6qc.js'); });
31558
+ const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-CTKuR0LC.js'); });
30969
31559
  const rawProvider = await EthereumProvider.init({
30970
31560
  ...restParameters,
30971
31561
  disableProviderPing: true,
@@ -31199,8 +31789,6 @@ const useSigner = ({ chain }) => {
31199
31789
  return isStellarSignerReady;
31200
31790
  case squidTypes.ChainType.CANTON:
31201
31791
  return isCantonSignerReady;
31202
- case squidTypes.ChainType.DANAL:
31203
- return false;
31204
31792
  }
31205
31793
  }, [
31206
31794
  chain?.chainType,
@@ -31653,12 +32241,8 @@ const useSendTransactionStore = zustand.create((set, get) => ({
31653
32241
  const useDepositAddressStore = zustand.create((set) => ({
31654
32242
  deposit: null,
31655
32243
  selectedPaymentMethod: "connectedWallet",
31656
- userDepositTxId: null,
31657
32244
  setDeposit: (data) => {
31658
- set({ deposit: data, userDepositTxId: null });
31659
- },
31660
- setUserDepositTxId: (txId) => {
31661
- set({ userDepositTxId: txId });
32245
+ set({ deposit: data });
31662
32246
  },
31663
32247
  setPaymentMethod: (method) => {
31664
32248
  set({ selectedPaymentMethod: method });
@@ -31670,7 +32254,7 @@ function useDepositAddress(squidRoute) {
31670
32254
  selectedPaymentMethod: state.selectedPaymentMethod,
31671
32255
  depositAddress: state.deposit?.depositAddress,
31672
32256
  }));
31673
- const { setDeposit, setPaymentMethod, deposit, userDepositTxId, setUserDepositTxId, } = useDepositAddressStore();
32257
+ const { setDeposit, setPaymentMethod, deposit } = useDepositAddressStore();
31674
32258
  const { squid } = useSquidStore();
31675
32259
  const { fromChain } = useSwap();
31676
32260
  const isAvailableAsPaymentMethod = React.useMemo(() => {
@@ -31680,13 +32264,11 @@ function useDepositAddress(squidRoute) {
31680
32264
  CHAIN_IDS.BITCOIN,
31681
32265
  CHAIN_IDS.SOLANA,
31682
32266
  CHAIN_IDS.CANTON,
31683
- CHAIN_IDS.DANAL_TESTNET,
31684
32267
  ];
31685
32268
  return chainsSupportingDepositAddress.includes(fromChain.chainId);
31686
32269
  }, [fromChain?.chainId]);
31687
- const routeProvidesDirectDepositAddress = (!!squidRoute?.transactionRequest &&
31688
- isDepositAddressDirectTransferRoute(squidRoute.transactionRequest)) ||
31689
- (!!squidRoute && isDanalDepositRoute(squidRoute));
32270
+ const routeProvidesDirectDepositAddress = !!squidRoute?.transactionRequest &&
32271
+ isDepositAddressDirectTransferRoute(squidRoute.transactionRequest);
31690
32272
  const swapWillGenerateDepositAddress = isChainflipDepositRoute(squidRoute) || routeProvidesDirectDepositAddress;
31691
32273
  const paymentMethod = isAvailableAsPaymentMethod
31692
32274
  ? selectedPaymentMethod
@@ -31712,19 +32294,6 @@ function useDepositAddress(squidRoute) {
31712
32294
  });
31713
32295
  return;
31714
32296
  }
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
32297
  else if (isDepositAddressDirectTransferRoute(route.transactionRequest)) {
31729
32298
  // Canton case
31730
32299
  const orderHash = route.transactionRequest.data;
@@ -31747,8 +32316,6 @@ function useDepositAddress(squidRoute) {
31747
32316
  depositAddress,
31748
32317
  closeDepositChannel,
31749
32318
  depositData: deposit,
31750
- userDepositTxId,
31751
- setUserDepositTxId,
31752
32319
  };
31753
32320
  }
31754
32321
 
@@ -31776,7 +32343,7 @@ const useUrlSwapParams = () => {
31776
32343
 
31777
32344
  const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, queryOptions, }) => {
31778
32345
  const { evmChains, cosmosChains, suiChains, xrplChains, stellarChains } = useSquidChains(direction);
31779
- const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, danalTokens, } = useSquidTokens(direction);
32346
+ const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens(direction);
31780
32347
  const { keplrTypeWallet } = useCosmosContext();
31781
32348
  const cantonConnector = useWalletStore((store) => store.connectedWalletsByChainType[squidTypes.ChainType.CANTON]?.wallet?.connector);
31782
32349
  const placeholderData = React.useMemo(() => {
@@ -31789,7 +32356,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31789
32356
  [squidTypes.ChainType.XRPL]: xrplTokens.map((t) => ({ ...t, balance: "0" })),
31790
32357
  [squidTypes.ChainType.STELLAR]: stellarTokens.map((t) => ({ ...t, balance: "0" })),
31791
32358
  [squidTypes.ChainType.CANTON]: cantonTokens.map((t) => ({ ...t, balance: "0" })),
31792
- [squidTypes.ChainType.DANAL]: danalTokens.map((t) => ({ ...t, balance: "0" })),
31793
32359
  };
31794
32360
  if (!chainType) {
31795
32361
  // Return all tokens with zero balance
@@ -31812,7 +32378,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31812
32378
  xrplTokens,
31813
32379
  stellarTokens,
31814
32380
  cantonTokens,
31815
- danalTokens,
31816
32381
  ]);
31817
32382
  const isQueryEnabled = React.useMemo(() => {
31818
32383
  // Respect the queryOptions.enabled override if provided
@@ -31837,8 +32402,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31837
32402
  return stellarTokens.length > 0;
31838
32403
  case squidTypes.ChainType.CANTON:
31839
32404
  return cantonTokens.length > 0;
31840
- case squidTypes.ChainType.DANAL:
31841
- return danalTokens.length > 0;
31842
32405
  }
31843
32406
  }, [
31844
32407
  chainType,
@@ -31852,7 +32415,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31852
32415
  xrplTokens.length,
31853
32416
  stellarTokens.length,
31854
32417
  cantonTokens.length,
31855
- danalTokens.length,
31856
32418
  ]);
31857
32419
  const query = reactQuery.useQuery(keys().allTokensBalance(address, chainType, direction), async () => {
31858
32420
  // Return zero balances if no address
@@ -31977,12 +32539,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
31977
32539
  direction,
31978
32540
  queryOptions,
31979
32541
  });
31980
- const danalBalancesQuery = useAllTokensWithBalanceForChainType({
31981
- chainType: squidTypes.ChainType.DANAL,
31982
- address: connectedAddresses?.[squidTypes.ChainType.DANAL],
31983
- direction,
31984
- queryOptions,
31985
- });
31986
32542
  // Create a map of chain type to balance query results
31987
32543
  const balanceQueries = React.useMemo(() => ({
31988
32544
  [squidTypes.ChainType.EVM]: evmBalancesQuery,
@@ -31993,7 +32549,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
31993
32549
  [squidTypes.ChainType.XRPL]: xrplBalancesQuery,
31994
32550
  [squidTypes.ChainType.STELLAR]: stellarBalancesQuery,
31995
32551
  [squidTypes.ChainType.CANTON]: cantonBalancesQuery,
31996
- [squidTypes.ChainType.DANAL]: danalBalancesQuery,
31997
32552
  }), [
31998
32553
  evmBalancesQuery,
31999
32554
  cosmosBalancesQuery,
@@ -32003,7 +32558,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
32003
32558
  xrplBalancesQuery,
32004
32559
  stellarBalancesQuery,
32005
32560
  cantonBalancesQuery,
32006
- danalBalancesQuery,
32007
32561
  ]);
32008
32562
  // Combine all tokens from different chains
32009
32563
  const allTokens = React.useMemo(() => Object.values(balanceQueries).flatMap((query) => query.data?.tokens ?? []), [balanceQueries]);
@@ -32111,8 +32665,6 @@ const useMultiChainBalance = ({ chain, token, userAddress, enabled = true, }) =>
32111
32665
  return stellarBalance;
32112
32666
  case squidTypes.ChainType.CANTON:
32113
32667
  return cantonBalance;
32114
- case squidTypes.ChainType.DANAL:
32115
- return "0";
32116
32668
  }
32117
32669
  }, [
32118
32670
  chain?.chainType,
@@ -33222,9 +33774,8 @@ function useSendTransaction({ to, amount, token, chain, }) {
33222
33774
  chain,
33223
33775
  });
33224
33776
  break;
33225
- case squidTypes.ChainType.CANTON:
33226
- case squidTypes.ChainType.DANAL: {
33227
- throw new Error(`Send not implemented for chain type ${chain.chainType}`);
33777
+ case squidTypes.ChainType.CANTON: {
33778
+ throw new Error("Not implemented");
33228
33779
  }
33229
33780
  }
33230
33781
  return {
@@ -33430,8 +33981,7 @@ async function getSendTransactionStatus({ chain, txHash, }) {
33430
33981
  chain,
33431
33982
  });
33432
33983
  case squidTypes.ChainType.CANTON:
33433
- case squidTypes.ChainType.DANAL:
33434
- throw new Error(`Send not implemented for chain type ${chain.chainType}`);
33984
+ throw new Error("Not implemented");
33435
33985
  }
33436
33986
  }
33437
33987
 
@@ -37052,342 +37602,6 @@ coin.DecProto = {
37052
37602
 
37053
37603
  } (tx));
37054
37604
 
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
37605
  const useExecuteTransaction = (squidRoute) => {
37392
37606
  const { fromChain, toChain, fromToken, toToken, isSameChain } = useSwap();
37393
37607
  // A route completes on source tx only if it's same-chain AND
@@ -38002,7 +38216,7 @@ const useExecuteTransaction = (squidRoute) => {
38002
38216
  }
38003
38217
  const amount = formatBNToReadable(fromAmount, token.decimals);
38004
38218
  dispatchSignatureRequestEvent(route);
38005
- const transferParams = await buildCantonTransfer(cantonSigner, {
38219
+ const result = await cantonSigner.transfer({
38006
38220
  sender: sourceUserAddress,
38007
38221
  receiver,
38008
38222
  amount,
@@ -38010,15 +38224,14 @@ const useExecuteTransaction = (squidRoute) => {
38010
38224
  memo: orderHash,
38011
38225
  nowMs: Date.now(),
38012
38226
  });
38013
- const result = await cantonSigner.prepareExecuteAndWait(transferParams);
38014
- const txHash = result.tx?.payload?.updateId ?? "";
38227
+ const txHash = result.updateId;
38015
38228
  if (txHash) {
38016
38229
  resetQueriesAfterTxSigned();
38017
38230
  }
38018
38231
  WidgetEvents.getInstance().dispatchSwapExecuteCall(route, txHash);
38019
38232
  const txParams = setTransactionState({
38020
38233
  route,
38021
- txHash: txHash,
38234
+ txHash,
38022
38235
  userAddress: sourceUserAddress,
38023
38236
  status: exports.TransactionStatus.INITIAL_LOADING,
38024
38237
  sourceStatus: exports.TransactionStatus.ONGOING,
@@ -38301,7 +38514,7 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
38301
38514
  //
38302
38515
  // If deposit address is not selected, we use the connected address as the source address instead
38303
38516
  const sourceUserAddress = isDepositAddressActive
38304
- ? depositRefundAddress ?? sourceConnectedAddress
38517
+ ? (depositRefundAddress ?? sourceConnectedAddress)
38305
38518
  : sourceConnectedAddress;
38306
38519
  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
38520
  fromChain?.chainId,
@@ -38593,6 +38806,10 @@ const useAutoConnect = () => {
38593
38806
  const { chains } = useSquidChains();
38594
38807
  const { isInitialized } = useConfigStore();
38595
38808
  const hasAttemptedReconnect = React.useRef(false);
38809
+ const hasAttemptedCantonReconnect = React.useRef(false);
38810
+ const { autoConnectCanton, wallets: cantonWallets } = useCantonContext();
38811
+ const recentCantonWalletId = useWalletStore((state) => state.recentConnectorIds[squidTypes.ChainType.CANTON]);
38812
+ const hasRecentCantonWallet = cantonWallets.some((wallet) => wallet.connectorId === recentCantonWalletId);
38596
38813
  const { autoConnectEvm } = useEvmContext();
38597
38814
  const { autoConnectSolana } = useSolanaContext();
38598
38815
  const { autoConnectSui } = useSuiContext();
@@ -38660,6 +38877,15 @@ const useAutoConnect = () => {
38660
38877
  React.useEffect(() => {
38661
38878
  autoConnect();
38662
38879
  }, [autoConnect]);
38880
+ React.useEffect(() => {
38881
+ if (!isInitialized ||
38882
+ chains.length === 0 ||
38883
+ !hasRecentCantonWallet ||
38884
+ hasAttemptedCantonReconnect.current)
38885
+ return;
38886
+ hasAttemptedCantonReconnect.current = true;
38887
+ autoConnectCanton().catch(() => undefined);
38888
+ }, [autoConnectCanton, hasRecentCantonWallet, isInitialized, chains.length]);
38663
38889
  return { autoConnect };
38664
38890
  };
38665
38891
 
@@ -39087,9 +39313,6 @@ exports.isChainflipBridgeTransaction = isChainflipBridgeTransaction;
39087
39313
  exports.isChainflipDepositRoute = isChainflipDepositRoute;
39088
39314
  exports.isCoralBridgeAction = isCoralBridgeAction;
39089
39315
  exports.isCosmosAddressValid = isCosmosAddressValid;
39090
- exports.isDanalAddressValid = isDanalAddressValid;
39091
- exports.isDanalDepositRoute = isDanalDepositRoute;
39092
- exports.isDanalTxHashValid = isDanalTxHashValid;
39093
39316
  exports.isDepositAddressDirectTransferRoute = isDepositAddressDirectTransferRoute;
39094
39317
  exports.isEmptyObject = isEmptyObject;
39095
39318
  exports.isEvmChainNotSupportedError = isEvmChainNotSupportedError;
@@ -39127,7 +39350,6 @@ exports.nativeXrplTokenAddress = nativeXrplTokenAddress;
39127
39350
  exports.normalizeError = normalizeError;
39128
39351
  exports.normalizeIbcAddress = normalizeIbcAddress;
39129
39352
  exports.normalizeTokenSymbol = normalizeTokenSymbol;
39130
- exports.parseDanalTransferBody = parseDanalTransferBody;
39131
39353
  exports.parseEvmAddress = parseEvmAddress;
39132
39354
  exports.parseInitialAssetsFromUrl = parseInitialAssetsFromUrl;
39133
39355
  exports.parseToBigInt = parseToBigInt;
@@ -39240,4 +39462,4 @@ exports.useXrplTrustLine = useXrplTrustLine;
39240
39462
  exports.waitForReceiptWithRetry = waitForReceiptWithRetry;
39241
39463
  exports.walletIconBaseUrl = walletIconBaseUrl;
39242
39464
  exports.walletSupportsChainType = walletSupportsChainType;
39243
- //# sourceMappingURL=index-nTqoaD0B.js.map
39465
+ //# sourceMappingURL=index-CJVX-7hj.js.map