@0xsquid/react-hooks 8.11.1-beta-danal.1 → 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 (52) 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/wallet.d.ts +1 -8
  8. package/dist/hooks/chains/useSquidChains.d.ts +0 -1
  9. package/dist/hooks/store/useDepositAddressStore.d.ts +0 -6
  10. package/dist/hooks/swap/useDepositAddress.d.ts +0 -2
  11. package/dist/hooks/tokens/useSquidTokens.d.ts +0 -1
  12. package/dist/hooks/wallet/useMultiChainWallet.d.ts +0 -38
  13. package/dist/hooks/wallet/useWallet.d.ts +0 -42
  14. package/dist/{index-D6rznmR_.js → index-C3owE9lb.js} +739 -512
  15. package/dist/index-C3owE9lb.js.map +1 -0
  16. package/dist/{index-BT6G_mgr.js → index-CJVX-7hj.js} +738 -515
  17. package/dist/index-CJVX-7hj.js.map +1 -0
  18. package/dist/{index.es-Dl19u5sy.js → index.es-09IsADsA.js} +2 -2
  19. package/dist/{index.es-Dl19u5sy.js.map → index.es-09IsADsA.js.map} +1 -1
  20. package/dist/{index.es-BZb8tg2z.js → index.es-CTKuR0LC.js} +2 -2
  21. package/dist/{index.es-BZb8tg2z.js.map → index.es-CTKuR0LC.js.map} +1 -1
  22. package/dist/index.esm.js +1 -1
  23. package/dist/index.js +1 -5
  24. package/dist/index.js.map +1 -1
  25. package/dist/{secretService-Dvc9Dg4U.js → secretService--tJFCkzT.js} +2 -2
  26. package/dist/{secretService-Dvc9Dg4U.js.map → secretService--tJFCkzT.js.map} +1 -1
  27. package/dist/{secretService-TP5V9Jwg.js → secretService-GHdReJBs.js} +2 -2
  28. package/dist/{secretService-TP5V9Jwg.js.map → secretService-GHdReJBs.js.map} +1 -1
  29. package/dist/server.esm.js +0 -6
  30. package/dist/server.esm.js.map +1 -1
  31. package/dist/server.js +0 -6
  32. package/dist/server.js.map +1 -1
  33. package/dist/services/external/rpcService.d.ts +2 -4
  34. package/dist/services/index.d.ts +0 -1
  35. package/dist/services/internal/assetsService.d.ts +1 -1
  36. package/dist/services/internal/cantonService.d.ts +2 -0
  37. package/dist/services/internal/cantonTransferService.d.ts +3 -3
  38. package/dist/services/internal/estimateService.d.ts +1 -1
  39. package/dist/services/internal/transactionService.d.ts +0 -8
  40. package/dist/{stellarService.client-c48cnGgL.js → stellarService.client-DAxgO3m3.js} +2 -2
  41. package/dist/{stellarService.client-c48cnGgL.js.map → stellarService.client-DAxgO3m3.js.map} +1 -1
  42. package/dist/{stellarService.client-WTkVJJhn.js → stellarService.client-DvA-2pXq.js} +2 -2
  43. package/dist/{stellarService.client-WTkVJJhn.js.map → stellarService.client-DvA-2pXq.js.map} +1 -1
  44. package/dist/tests/loopSdkContract.test.d.ts +1 -0
  45. package/dist/tests/useAutoConnectCanton.test.d.ts +1 -0
  46. package/dist/tests/useCantonRestoration.test.d.ts +1 -0
  47. package/package.json +3 -2
  48. package/dist/core/types/danal.d.ts +0 -1
  49. package/dist/index-BT6G_mgr.js.map +0 -1
  50. package/dist/index-D6rznmR_.js.map +0 -1
  51. package/dist/services/internal/danalService.d.ts +0 -8
  52. /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-TP5V9Jwg.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) {
@@ -25729,7 +25675,7 @@ const useSquidTokens = (direction) => {
25729
25675
  config.availableChains,
25730
25676
  direction,
25731
25677
  ]);
25732
- const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, danalTokens, } = React.useMemo(() => {
25678
+ const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = React.useMemo(() => {
25733
25679
  return tokens?.reduce((acc, token) => {
25734
25680
  switch (token.type) {
25735
25681
  case squidTypes.ChainType.EVM:
@@ -25756,9 +25702,6 @@ const useSquidTokens = (direction) => {
25756
25702
  case squidTypes.ChainType.CANTON:
25757
25703
  acc.cantonTokens.push(token);
25758
25704
  break;
25759
- case squidTypes.ChainType.DANAL:
25760
- acc.danalTokens.push(token);
25761
- break;
25762
25705
  }
25763
25706
  return acc;
25764
25707
  }, {
@@ -25770,7 +25713,6 @@ const useSquidTokens = (direction) => {
25770
25713
  xrplTokens: [],
25771
25714
  stellarTokens: [],
25772
25715
  cantonTokens: [],
25773
- danalTokens: [],
25774
25716
  });
25775
25717
  }, [tokens]);
25776
25718
  const findToken = React.useCallback((address, chainId) => {
@@ -25789,7 +25731,6 @@ const useSquidTokens = (direction) => {
25789
25731
  xrplTokens,
25790
25732
  stellarTokens,
25791
25733
  cantonTokens,
25792
- danalTokens,
25793
25734
  };
25794
25735
  };
25795
25736
 
@@ -26326,7 +26267,6 @@ const chainTypeToRefetchInterval = {
26326
26267
  [squidTypes.ChainType.XRPL]: 1_000,
26327
26268
  [squidTypes.ChainType.STELLAR]: 1_000,
26328
26269
  [squidTypes.ChainType.CANTON]: 1_000,
26329
- [squidTypes.ChainType.DANAL]: 1_000,
26330
26270
  };
26331
26271
  /**
26332
26272
  * Returns the status refetch interval of a Send transaction
@@ -26593,7 +26533,7 @@ const useSquidChains = (direction) => {
26593
26533
  const chain = findChain(chainId);
26594
26534
  return chain?.chainType;
26595
26535
  }, [findChain]);
26596
- const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains, danalChains, } = React.useMemo(() => {
26536
+ const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains, } = React.useMemo(() => {
26597
26537
  return chains.reduce((acc, chain) => {
26598
26538
  switch (chain.chainType) {
26599
26539
  case squidTypes.ChainType.EVM:
@@ -26614,9 +26554,6 @@ const useSquidChains = (direction) => {
26614
26554
  case squidTypes.ChainType.CANTON:
26615
26555
  acc.cantonChains.push(chain);
26616
26556
  break;
26617
- case squidTypes.ChainType.DANAL:
26618
- acc.danalChains.push(chain);
26619
- break;
26620
26557
  }
26621
26558
  return acc;
26622
26559
  }, {
@@ -26626,7 +26563,6 @@ const useSquidChains = (direction) => {
26626
26563
  xrplChains: [],
26627
26564
  stellarChains: [],
26628
26565
  cantonChains: [],
26629
- danalChains: [],
26630
26566
  });
26631
26567
  }, [chains]);
26632
26568
  const { supportedSourceChains, supportedDestinationChains } = React.useMemo(() => {
@@ -26648,7 +26584,6 @@ const useSquidChains = (direction) => {
26648
26584
  xrplChains,
26649
26585
  stellarChains,
26650
26586
  cantonChains,
26651
- danalChains,
26652
26587
  getChainType,
26653
26588
  findChain,
26654
26589
  };
@@ -26962,7 +26897,343 @@ const useBitcoinContext = () => {
26962
26897
  return context;
26963
26898
  };
26964
26899
 
26965
- 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;
26966
27237
  function pickAddress(accounts) {
26967
27238
  return (accounts.find((a) => a.primary) ?? accounts[0])?.partyId;
26968
27239
  }
@@ -26971,6 +27242,7 @@ function pickAddress(accounts) {
26971
27242
  * (bound to its announce `target`) and exposes the lifecycle methods.
26972
27243
  */
26973
27244
  class CantonBrowserConnector {
27245
+ kind = "cip103";
26974
27246
  providerId;
26975
27247
  name;
26976
27248
  target;
@@ -26985,7 +27257,7 @@ class CantonBrowserConnector {
26985
27257
  client ??
26986
27258
  dapp.createCantonClient(new dapp.WindowTransport(win, {
26987
27259
  target: this.target,
26988
- timeout: REQUEST_TIMEOUT_MS,
27260
+ timeout: REQUEST_TIMEOUT_MS$1,
26989
27261
  }));
26990
27262
  }
26991
27263
  get accounts() {
@@ -27031,6 +27303,28 @@ class CantonBrowserConnector {
27031
27303
  this._accounts = await this.client.listAccounts();
27032
27304
  return this._accounts;
27033
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
+ }
27034
27328
  on(event, handler) {
27035
27329
  this.listeners[event].add(handler);
27036
27330
  }
@@ -27137,6 +27431,313 @@ function discoverInjectedCantonWallets(win) {
27137
27431
  });
27138
27432
  }
27139
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
+
27140
27741
  /**
27141
27742
  * Discovers Canton browser-extension wallets and maps each to a
27142
27743
  * {@link CantonWallet}: CIP-103 announce-protocol wallets (on mount and on
@@ -27183,7 +27784,19 @@ function useCantonWallets() {
27183
27784
  return;
27184
27785
  const announced = (await discoverCantonProviders()).map(toAnnouncedWallet);
27185
27786
  const injected = discoverInjectedCantonWallets().map(toInjectedWallet);
27186
- 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]));
27187
27800
  }, [toAnnouncedWallet, toInjectedWallet]);
27188
27801
  React.useEffect(() => {
27189
27802
  refresh();
@@ -27214,7 +27827,9 @@ function useCanton() {
27214
27827
  // Try to reconnect silently first (existing session, no popup)
27215
27828
  result = await wallet.connector.autoConnect();
27216
27829
  }
27217
- catch {
27830
+ catch (error) {
27831
+ if (error instanceof CantonConnectionCancelledError)
27832
+ throw error;
27218
27833
  // Otherwise prompt the wallet to connect
27219
27834
  result = await wallet.connector.connect();
27220
27835
  }
@@ -27236,8 +27851,7 @@ function useCanton() {
27236
27851
  wallet: recentCantonWallet,
27237
27852
  address,
27238
27853
  });
27239
- // eslint-disable-next-line react-hooks/exhaustive-deps
27240
- }, [wallets.map((w) => w.connectorId), recentCantonWalletId]);
27854
+ }, [wallets, recentCantonWalletId, setConnectedWallet]);
27241
27855
  React.useEffect(() => {
27242
27856
  const wallet = connectedCantonWallet.wallet;
27243
27857
  if (!wallet) {
@@ -27494,7 +28108,7 @@ function useStellarWallets() {
27494
28108
  try {
27495
28109
  const { allowAllModules: initializeAllModules } = await import('@creit.tech/stellar-wallets-kit');
27496
28110
  const { LedgerModule } = await import('@creit.tech/stellar-wallets-kit/modules/ledger.module.mjs');
27497
- const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-WTkVJJhn.js'); });
28111
+ const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-DvA-2pXq.js'); });
27498
28112
  const modules = [...initializeAllModules(), new LedgerModule()];
27499
28113
  const promises = modules.map(async (module) => {
27500
28114
  const isAvailable = await module.isAvailable();
@@ -28073,6 +28687,11 @@ const useWallet = () => {
28073
28687
  }
28074
28688
  }
28075
28689
  catch (error) {
28690
+ const connecting = useWalletStore.getState().connectingWalletState;
28691
+ if (connecting.wallet !== wallet ||
28692
+ connecting.status !== exports.ConnectingWalletStatus.CONNECTING) {
28693
+ return false;
28694
+ }
28076
28695
  console.error("Failed to connect wallet", error);
28077
28696
  walletConnectionError(wallet, error);
28078
28697
  return false;
@@ -28304,8 +28923,6 @@ const useMultiChainWallet = (chain) => {
28304
28923
  chainType: chain.chainType,
28305
28924
  }),
28306
28925
  };
28307
- case squidTypes.ChainType.DANAL:
28308
- return {};
28309
28926
  }
28310
28927
  }, [
28311
28928
  chain?.chainType,
@@ -29144,8 +29761,6 @@ async function createClient(chain) {
29144
29761
  return new StellarRpcClient(chain.rpc);
29145
29762
  case squidTypes.ChainType.CANTON:
29146
29763
  return null;
29147
- case squidTypes.ChainType.DANAL:
29148
- return null;
29149
29764
  }
29150
29765
  }
29151
29766
 
@@ -30160,8 +30775,7 @@ const getAllStellarTokensBalance = async (userAddress, stellarTokens, stellarCha
30160
30775
  };
30161
30776
  /**
30162
30777
  * Canton balances are private: they can only be read with a wallet-authorized
30163
- * `ledgerApi` session (the connected wallet supplies a JWT that can read as the
30164
- * 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
30165
30779
  * only for that wallet's party — there's no address-only read.
30166
30780
  */
30167
30781
  const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
@@ -30170,30 +30784,14 @@ const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
30170
30784
  const instrument = parseCantonInstrument(token.originalAddress);
30171
30785
  if (!instrument)
30172
30786
  return "0";
30173
- const ledgerEnd = await cantonConnector.ledgerApi({ requestMethod: "get", resource: "/v2/state/ledger-end" });
30174
- const activeContracts = await cantonConnector.ledgerApi({
30175
- requestMethod: "post",
30176
- resource: "/v2/state/active-contracts",
30177
- body: buildHoldingsAcsRequestBody(userAddress, String(ledgerEnd.offset)),
30178
- });
30179
- const grouped = groupCantonHoldingBalances(activeContracts);
30787
+ const grouped = await cantonConnector.getBalances(userAddress);
30180
30788
  return grouped[cantonInstrumentKey(instrument)] ?? "0";
30181
30789
  };
30182
30790
  /**
30183
- * Fetch balances for all Canton tokens in one ACS query. Canton balances are
30184
- * private, so this requires a connected wallet's authorized `ledgerApi`.
30791
+ * Fetch balances for all Canton tokens through the connected wallet.
30185
30792
  */
30186
30793
  async function getAllCantonTokensBalance({ connector, partyId, tokens, }) {
30187
- const ledgerEnd = await connector.ledgerApi({
30188
- requestMethod: "get",
30189
- resource: "/v2/state/ledger-end",
30190
- });
30191
- const activeContracts = await connector.ledgerApi({
30192
- requestMethod: "post",
30193
- resource: "/v2/state/active-contracts",
30194
- body: buildHoldingsAcsRequestBody(partyId, String(ledgerEnd.offset)),
30195
- });
30196
- const grouped = groupCantonHoldingBalances(activeContracts);
30794
+ const grouped = await connector.getBalances(partyId);
30197
30795
  return tokens.map((token) => {
30198
30796
  const instrument = token.originalAddress
30199
30797
  ? parseCantonInstrument(token.originalAddress)
@@ -30412,7 +31010,7 @@ const useCantonBalance = ({ chain, token, userAddress, enabled = true, refreshIn
30412
31010
  };
30413
31011
 
30414
31012
  function useNativeTokenForChain(chain) {
30415
- const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, danalTokens, } = useSquidTokens();
31013
+ const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens();
30416
31014
  const getTokensForChainType = () => {
30417
31015
  if (!chain?.chainType)
30418
31016
  return [];
@@ -30433,8 +31031,6 @@ function useNativeTokenForChain(chain) {
30433
31031
  return stellarTokens;
30434
31032
  case squidTypes.ChainType.CANTON:
30435
31033
  return cantonTokens;
30436
- case squidTypes.ChainType.DANAL:
30437
- return danalTokens;
30438
31034
  }
30439
31035
  };
30440
31036
  const nativeTokenForChainType = React.useMemo(() => {
@@ -30695,9 +31291,6 @@ const useNativeBalance = (chain) => {
30695
31291
  case squidTypes.ChainType.CANTON:
30696
31292
  balance = nativeCantonBalance;
30697
31293
  break;
30698
- case squidTypes.ChainType.DANAL:
30699
- balance = undefined;
30700
- break;
30701
31294
  }
30702
31295
  const balanceFormatted = !!balance
30703
31296
  ? formatBNToReadable(balance.value, balance.decimals)
@@ -30737,8 +31330,6 @@ const useNativeBalance = (chain) => {
30737
31330
  return isStellarLoading;
30738
31331
  case squidTypes.ChainType.CANTON:
30739
31332
  return isCantonLoading;
30740
- case squidTypes.ChainType.DANAL:
30741
- return false;
30742
31333
  }
30743
31334
  }, [
30744
31335
  chain?.chainType,
@@ -30964,7 +31555,7 @@ function hederaWalletConnect(parameters) {
30964
31555
  const optionalChains = config.chains.map((x) => x.id);
30965
31556
  if (!optionalChains.length)
30966
31557
  return;
30967
- const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-BZb8tg2z.js'); });
31558
+ const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-CTKuR0LC.js'); });
30968
31559
  const rawProvider = await EthereumProvider.init({
30969
31560
  ...restParameters,
30970
31561
  disableProviderPing: true,
@@ -31198,8 +31789,6 @@ const useSigner = ({ chain }) => {
31198
31789
  return isStellarSignerReady;
31199
31790
  case squidTypes.ChainType.CANTON:
31200
31791
  return isCantonSignerReady;
31201
- case squidTypes.ChainType.DANAL:
31202
- return false;
31203
31792
  }
31204
31793
  }, [
31205
31794
  chain?.chainType,
@@ -31652,12 +32241,8 @@ const useSendTransactionStore = zustand.create((set, get) => ({
31652
32241
  const useDepositAddressStore = zustand.create((set) => ({
31653
32242
  deposit: null,
31654
32243
  selectedPaymentMethod: "connectedWallet",
31655
- userDepositTxId: null,
31656
32244
  setDeposit: (data) => {
31657
- set({ deposit: data, userDepositTxId: null });
31658
- },
31659
- setUserDepositTxId: (txId) => {
31660
- set({ userDepositTxId: txId });
32245
+ set({ deposit: data });
31661
32246
  },
31662
32247
  setPaymentMethod: (method) => {
31663
32248
  set({ selectedPaymentMethod: method });
@@ -31669,7 +32254,7 @@ function useDepositAddress(squidRoute) {
31669
32254
  selectedPaymentMethod: state.selectedPaymentMethod,
31670
32255
  depositAddress: state.deposit?.depositAddress,
31671
32256
  }));
31672
- const { setDeposit, setPaymentMethod, deposit, userDepositTxId, setUserDepositTxId, } = useDepositAddressStore();
32257
+ const { setDeposit, setPaymentMethod, deposit } = useDepositAddressStore();
31673
32258
  const { squid } = useSquidStore();
31674
32259
  const { fromChain } = useSwap();
31675
32260
  const isAvailableAsPaymentMethod = React.useMemo(() => {
@@ -31679,13 +32264,11 @@ function useDepositAddress(squidRoute) {
31679
32264
  CHAIN_IDS.BITCOIN,
31680
32265
  CHAIN_IDS.SOLANA,
31681
32266
  CHAIN_IDS.CANTON,
31682
- CHAIN_IDS.DANAL_TESTNET,
31683
32267
  ];
31684
32268
  return chainsSupportingDepositAddress.includes(fromChain.chainId);
31685
32269
  }, [fromChain?.chainId]);
31686
- const routeProvidesDirectDepositAddress = (!!squidRoute?.transactionRequest &&
31687
- isDepositAddressDirectTransferRoute(squidRoute.transactionRequest)) ||
31688
- (!!squidRoute && isDanalDepositRoute(squidRoute));
32270
+ const routeProvidesDirectDepositAddress = !!squidRoute?.transactionRequest &&
32271
+ isDepositAddressDirectTransferRoute(squidRoute.transactionRequest);
31689
32272
  const swapWillGenerateDepositAddress = isChainflipDepositRoute(squidRoute) || routeProvidesDirectDepositAddress;
31690
32273
  const paymentMethod = isAvailableAsPaymentMethod
31691
32274
  ? selectedPaymentMethod
@@ -31711,19 +32294,6 @@ function useDepositAddress(squidRoute) {
31711
32294
  });
31712
32295
  return;
31713
32296
  }
31714
- else if (isDanalDepositRoute(route)) {
31715
- const transferBody = parseDanalTransferBody(route.transactionRequest.data);
31716
- if (!transferBody) {
31717
- throw new Error("Could not parse Danal transfer request body");
31718
- }
31719
- setDeposit({
31720
- amount: transferBody.amount,
31721
- depositAddress: route.transactionRequest.target,
31722
- statusTrackingId: transferBody.memo,
31723
- memo: transferBody.memo,
31724
- });
31725
- return;
31726
- }
31727
32297
  else if (isDepositAddressDirectTransferRoute(route.transactionRequest)) {
31728
32298
  // Canton case
31729
32299
  const orderHash = route.transactionRequest.data;
@@ -31746,8 +32316,6 @@ function useDepositAddress(squidRoute) {
31746
32316
  depositAddress,
31747
32317
  closeDepositChannel,
31748
32318
  depositData: deposit,
31749
- userDepositTxId,
31750
- setUserDepositTxId,
31751
32319
  };
31752
32320
  }
31753
32321
 
@@ -31775,7 +32343,7 @@ const useUrlSwapParams = () => {
31775
32343
 
31776
32344
  const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, queryOptions, }) => {
31777
32345
  const { evmChains, cosmosChains, suiChains, xrplChains, stellarChains } = useSquidChains(direction);
31778
- const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, danalTokens, } = useSquidTokens(direction);
32346
+ const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens(direction);
31779
32347
  const { keplrTypeWallet } = useCosmosContext();
31780
32348
  const cantonConnector = useWalletStore((store) => store.connectedWalletsByChainType[squidTypes.ChainType.CANTON]?.wallet?.connector);
31781
32349
  const placeholderData = React.useMemo(() => {
@@ -31788,7 +32356,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31788
32356
  [squidTypes.ChainType.XRPL]: xrplTokens.map((t) => ({ ...t, balance: "0" })),
31789
32357
  [squidTypes.ChainType.STELLAR]: stellarTokens.map((t) => ({ ...t, balance: "0" })),
31790
32358
  [squidTypes.ChainType.CANTON]: cantonTokens.map((t) => ({ ...t, balance: "0" })),
31791
- [squidTypes.ChainType.DANAL]: danalTokens.map((t) => ({ ...t, balance: "0" })),
31792
32359
  };
31793
32360
  if (!chainType) {
31794
32361
  // Return all tokens with zero balance
@@ -31811,7 +32378,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31811
32378
  xrplTokens,
31812
32379
  stellarTokens,
31813
32380
  cantonTokens,
31814
- danalTokens,
31815
32381
  ]);
31816
32382
  const isQueryEnabled = React.useMemo(() => {
31817
32383
  // Respect the queryOptions.enabled override if provided
@@ -31836,8 +32402,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31836
32402
  return stellarTokens.length > 0;
31837
32403
  case squidTypes.ChainType.CANTON:
31838
32404
  return cantonTokens.length > 0;
31839
- case squidTypes.ChainType.DANAL:
31840
- return danalTokens.length > 0;
31841
32405
  }
31842
32406
  }, [
31843
32407
  chainType,
@@ -31851,7 +32415,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
31851
32415
  xrplTokens.length,
31852
32416
  stellarTokens.length,
31853
32417
  cantonTokens.length,
31854
- danalTokens.length,
31855
32418
  ]);
31856
32419
  const query = reactQuery.useQuery(keys().allTokensBalance(address, chainType, direction), async () => {
31857
32420
  // Return zero balances if no address
@@ -31976,12 +32539,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
31976
32539
  direction,
31977
32540
  queryOptions,
31978
32541
  });
31979
- const danalBalancesQuery = useAllTokensWithBalanceForChainType({
31980
- chainType: squidTypes.ChainType.DANAL,
31981
- address: connectedAddresses?.[squidTypes.ChainType.DANAL],
31982
- direction,
31983
- queryOptions,
31984
- });
31985
32542
  // Create a map of chain type to balance query results
31986
32543
  const balanceQueries = React.useMemo(() => ({
31987
32544
  [squidTypes.ChainType.EVM]: evmBalancesQuery,
@@ -31992,7 +32549,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
31992
32549
  [squidTypes.ChainType.XRPL]: xrplBalancesQuery,
31993
32550
  [squidTypes.ChainType.STELLAR]: stellarBalancesQuery,
31994
32551
  [squidTypes.ChainType.CANTON]: cantonBalancesQuery,
31995
- [squidTypes.ChainType.DANAL]: danalBalancesQuery,
31996
32552
  }), [
31997
32553
  evmBalancesQuery,
31998
32554
  cosmosBalancesQuery,
@@ -32002,7 +32558,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
32002
32558
  xrplBalancesQuery,
32003
32559
  stellarBalancesQuery,
32004
32560
  cantonBalancesQuery,
32005
- danalBalancesQuery,
32006
32561
  ]);
32007
32562
  // Combine all tokens from different chains
32008
32563
  const allTokens = React.useMemo(() => Object.values(balanceQueries).flatMap((query) => query.data?.tokens ?? []), [balanceQueries]);
@@ -32110,8 +32665,6 @@ const useMultiChainBalance = ({ chain, token, userAddress, enabled = true, }) =>
32110
32665
  return stellarBalance;
32111
32666
  case squidTypes.ChainType.CANTON:
32112
32667
  return cantonBalance;
32113
- case squidTypes.ChainType.DANAL:
32114
- return "0";
32115
32668
  }
32116
32669
  }, [
32117
32670
  chain?.chainType,
@@ -33221,9 +33774,8 @@ function useSendTransaction({ to, amount, token, chain, }) {
33221
33774
  chain,
33222
33775
  });
33223
33776
  break;
33224
- case squidTypes.ChainType.CANTON:
33225
- case squidTypes.ChainType.DANAL: {
33226
- throw new Error(`Send not implemented for chain type ${chain.chainType}`);
33777
+ case squidTypes.ChainType.CANTON: {
33778
+ throw new Error("Not implemented");
33227
33779
  }
33228
33780
  }
33229
33781
  return {
@@ -33429,8 +33981,7 @@ async function getSendTransactionStatus({ chain, txHash, }) {
33429
33981
  chain,
33430
33982
  });
33431
33983
  case squidTypes.ChainType.CANTON:
33432
- case squidTypes.ChainType.DANAL:
33433
- throw new Error(`Send not implemented for chain type ${chain.chainType}`);
33984
+ throw new Error("Not implemented");
33434
33985
  }
33435
33986
  }
33436
33987
 
@@ -37051,342 +37602,6 @@ coin.DecProto = {
37051
37602
 
37052
37603
  } (tx));
37053
37604
 
37054
- const vParty = (party) => ({ party });
37055
- const vText = (text) => ({ text });
37056
- const vNumeric = (numeric) => ({ numeric });
37057
- const vContractId = (contractId) => ({
37058
- contractId,
37059
- });
37060
- const vList = (elements) => ({
37061
- list: { elements },
37062
- });
37063
- const vRecord = (fields) => ({
37064
- record: { fields: fields.map(([label, value]) => ({ label, value })) },
37065
- });
37066
- const vTextMap = (entries) => ({ textMap: { entries } });
37067
- const vTimestamp = (isoString) => ({
37068
- timestamp: String(Math.floor(new Date(isoString).getTime() * 1000)),
37069
- });
37070
- function isRecord(value) {
37071
- return typeof value === "object" && value !== null && !Array.isArray(value);
37072
- }
37073
- function normalizeChoiceContextValue(value) {
37074
- if (typeof value === "string")
37075
- return value;
37076
- if (!isRecord(value) || typeof value.tag !== "string")
37077
- return null;
37078
- switch (value.tag) {
37079
- case "AV_Text":
37080
- case "AV_ContractId":
37081
- return typeof value.value === "string"
37082
- ? { tag: value.tag, value: value.value }
37083
- : null;
37084
- case "AV_Bool":
37085
- return typeof value.value === "boolean"
37086
- ? { tag: value.tag, value: value.value }
37087
- : null;
37088
- case "AV_List":
37089
- if (!Array.isArray(value.value))
37090
- return null;
37091
- return {
37092
- tag: value.tag,
37093
- value: value.value.flatMap((entry) => {
37094
- const normalized = normalizeChoiceContextValue(entry);
37095
- return normalized ? [normalized] : [];
37096
- }),
37097
- };
37098
- default: {
37099
- const inner = value.value;
37100
- if (typeof inner === "string" ||
37101
- typeof inner === "number" ||
37102
- typeof inner === "boolean" ||
37103
- inner === null) {
37104
- return { tag: value.tag, value: inner };
37105
- }
37106
- if (Array.isArray(inner)) {
37107
- return {
37108
- tag: value.tag,
37109
- value: inner.flatMap((entry) => {
37110
- const normalized = normalizeChoiceContextValue(entry);
37111
- return normalized ? [normalized] : [];
37112
- }),
37113
- };
37114
- }
37115
- if (!isRecord(inner))
37116
- return null;
37117
- const normalizedObject = {};
37118
- for (const [key, entry] of Object.entries(inner)) {
37119
- if (typeof entry !== "string" &&
37120
- typeof entry !== "number" &&
37121
- typeof entry !== "boolean" &&
37122
- entry !== null) {
37123
- return null;
37124
- }
37125
- normalizedObject[key] = entry;
37126
- }
37127
- return { tag: value.tag, value: normalizedObject };
37128
- }
37129
- }
37130
- }
37131
- function stringifyChoiceContextValue(value) {
37132
- return typeof value === "string" ? value : String(value);
37133
- }
37134
- function choiceContextValueToAnyValue(value) {
37135
- if (typeof value === "string") {
37136
- return {
37137
- variant: { constructor: "AV_Text", value: vText(value) },
37138
- };
37139
- }
37140
- switch (value.tag) {
37141
- case "AV_ContractId":
37142
- if (typeof value.value !== "string") {
37143
- throw new TypeError("AV_ContractId values must be strings");
37144
- }
37145
- return {
37146
- variant: { constructor: value.tag, value: vContractId(value.value) },
37147
- };
37148
- case "AV_Bool":
37149
- if (typeof value.value !== "boolean") {
37150
- throw new TypeError("AV_Bool values must be booleans");
37151
- }
37152
- return {
37153
- variant: { constructor: value.tag, value: { bool: value.value } },
37154
- };
37155
- case "AV_List":
37156
- if (!Array.isArray(value.value)) {
37157
- throw new TypeError("AV_List values must be arrays");
37158
- }
37159
- return {
37160
- variant: {
37161
- constructor: value.tag,
37162
- value: vList(value.value.map(choiceContextValueToAnyValue)),
37163
- },
37164
- };
37165
- default:
37166
- return {
37167
- variant: {
37168
- constructor: value.tag,
37169
- value: vText(stringifyChoiceContextValue(value.value)),
37170
- },
37171
- };
37172
- }
37173
- }
37174
-
37175
- /** Metadata key used by Splice token-standard transfers to carry our Squid orderhash memo. */
37176
- const CANTON_MEMO_KEY = "splice.lfdecentralizedtrust.org/reason";
37177
- /** Interface id for the transfer factory contract returned by the token registry. */
37178
- const TRANSFER_FACTORY_INTERFACE_ID = "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory";
37179
- /** Choice exercised on the transfer factory to create the token-standard transfer. */
37180
- const TRANSFER_FACTORY_CHOICE = "TransferFactory_Transfer";
37181
- /** CIP-056 (non-Amulet) tokens expose a registrar parameterized by admin party. */
37182
- const CIP056_REGISTRAR_BASE = "https://api.utilities.digitalasset.com/api/token-standard/v0/registrars";
37183
- const TRANSFER_REQUESTED_AT_SKEW_MS = 60_000;
37184
- const TRANSFER_EXECUTE_WINDOW_MS = 24 * 60 * 60 * 1000;
37185
- function resolveCantonRegistryUrl(instrument) {
37186
- if (instrument.id === CANTON_AMULET_INSTRUMENT_ID) {
37187
- return CANTON_AMULET_REGISTRY_URL;
37188
- }
37189
- return `${CIP056_REGISTRAR_BASE}/${encodeURIComponent(instrument.admin)}`;
37190
- }
37191
- /**
37192
- * Build the plain JSON arguments expected by the registry transfer-factory
37193
- * endpoint before it adds choice context and disclosed contracts.
37194
- */
37195
- function buildTransferChoiceArgs({ sender, receiver, amount, instrument, inputHoldingCids, memo, nowMs, }) {
37196
- return {
37197
- expectedAdmin: instrument.admin,
37198
- transfer: {
37199
- sender,
37200
- receiver,
37201
- amount,
37202
- instrumentId: { admin: instrument.admin, id: instrument.id },
37203
- requestedAt: new Date(nowMs - TRANSFER_REQUESTED_AT_SKEW_MS).toISOString(),
37204
- executeBefore: new Date(nowMs + TRANSFER_EXECUTE_WINDOW_MS).toISOString(),
37205
- inputHoldingCids,
37206
- meta: { values: { [CANTON_MEMO_KEY]: memo } },
37207
- },
37208
- extraArgs: { context: { values: {} }, meta: { values: {} } },
37209
- };
37210
- }
37211
- /**
37212
- * Canton's Holding contracts follow Bitcoin-style UTXOs model.
37213
- *
37214
- * Pick Holding contract ids covering `amount` for the instrument: prefer a
37215
- * single exact-amount holding, else accumulate largest-first.
37216
- */
37217
- function selectInputHoldingCids(holdings, instrument, amount) {
37218
- const spendableHoldings = holdings.filter((h) => !h.locked &&
37219
- h.instrument.admin === instrument.admin &&
37220
- h.instrument.id === instrument.id);
37221
- const target = new BigNumber(amount);
37222
- const exact = spendableHoldings.find((h) => new BigNumber(h.amount).eq(target));
37223
- if (exact)
37224
- return [exact.contractId];
37225
- const sorted = [...spendableHoldings].sort((a, b) => new BigNumber(b.amount).comparedTo(new BigNumber(a.amount)));
37226
- const selected = [];
37227
- let sum = new BigNumber(0);
37228
- for (const holding of sorted) {
37229
- selected.push(holding.contractId);
37230
- sum = sum.plus(holding.amount);
37231
- if (sum.gte(target))
37232
- break;
37233
- }
37234
- if (sum.lt(target)) {
37235
- throw new Error("Insufficient Canton holdings to cover transfer amount");
37236
- }
37237
- return selected;
37238
- }
37239
- async function fetchInputHoldingCids(connector, party, instrument, amount) {
37240
- const ledgerEnd = await connector.ledgerApi({
37241
- requestMethod: "get",
37242
- resource: "/v2/state/ledger-end",
37243
- });
37244
- const activeContracts = await connector.ledgerApi({
37245
- requestMethod: "post",
37246
- resource: "/v2/state/active-contracts",
37247
- body: buildHoldingsAcsRequestBody(party, String(ledgerEnd.offset)),
37248
- });
37249
- return selectInputHoldingCids(parseCantonHoldings(activeContracts), instrument, amount);
37250
- }
37251
- /**
37252
- * Ask the token registry for the transfer factory contract and extra context
37253
- * required to exercise its transfer choice.
37254
- */
37255
- async function fetchTransferFactory(registryUrl, choiceArgs) {
37256
- const response = await fetch(`${registryUrl}/registry/transfer-instruction/v1/transfer-factory`, {
37257
- method: "POST",
37258
- headers: { "Content-Type": "application/json" },
37259
- body: JSON.stringify({
37260
- choiceArguments: choiceArgs,
37261
- excludeDebugFields: true,
37262
- }),
37263
- });
37264
- if (!response.ok) {
37265
- throw new Error(`Canton transfer-factory request failed (${response.status})`);
37266
- }
37267
- return response.json();
37268
- }
37269
- /**
37270
- * Convert transfer choice args into strict Daml Value JSON for the wallet's
37271
- * prepareExecute ExerciseCommand.
37272
- */
37273
- function buildPrepareChoiceArgument(choiceArgs) {
37274
- const contextValues = Object.entries(choiceArgs.extraArgs.context?.values ?? {})
37275
- .map(([key, value]) => {
37276
- const normalized = normalizeChoiceContextValue(value);
37277
- return normalized
37278
- ? { key, value: choiceContextValueToAnyValue(normalized) }
37279
- : undefined;
37280
- })
37281
- .filter((entry) => Boolean(entry));
37282
- return vRecord([
37283
- ["expectedAdmin", vParty(choiceArgs.expectedAdmin)],
37284
- [
37285
- "transfer",
37286
- vRecord([
37287
- ["sender", vParty(choiceArgs.transfer.sender)],
37288
- ["receiver", vParty(choiceArgs.transfer.receiver)],
37289
- ["amount", vNumeric(choiceArgs.transfer.amount)],
37290
- [
37291
- "instrumentId",
37292
- vRecord([
37293
- ["admin", vParty(choiceArgs.transfer.instrumentId.admin)],
37294
- ["id", vText(choiceArgs.transfer.instrumentId.id)],
37295
- ]),
37296
- ],
37297
- ["requestedAt", vTimestamp(choiceArgs.transfer.requestedAt)],
37298
- ["executeBefore", vTimestamp(choiceArgs.transfer.executeBefore)],
37299
- [
37300
- "inputHoldingCids",
37301
- vList(choiceArgs.transfer.inputHoldingCids.map(vContractId)),
37302
- ],
37303
- [
37304
- "meta",
37305
- vRecord([
37306
- [
37307
- "values",
37308
- vTextMap(Object.entries(choiceArgs.transfer.meta.values).map(([key, value]) => ({ key, value: vText(value) }))),
37309
- ],
37310
- ]),
37311
- ],
37312
- ]),
37313
- ],
37314
- [
37315
- "extraArgs",
37316
- vRecord([
37317
- ["context", vRecord([["values", vTextMap(contextValues)]])],
37318
- ["meta", vRecord([["values", vTextMap([])]])],
37319
- ]),
37320
- ],
37321
- ]);
37322
- }
37323
- /**
37324
- * Convert a registry disclosed contract into the wallet submission shape,
37325
- * dropping debug fields and entries without a usable createdEventBlob.
37326
- */
37327
- function toDisclosedContract(raw) {
37328
- if (typeof raw.createdEventBlob !== "string")
37329
- return null;
37330
- return {
37331
- createdEventBlob: raw.createdEventBlob,
37332
- contractId: typeof raw.contractId === "string" ? raw.contractId : undefined,
37333
- synchronizerId: typeof raw.synchronizerId === "string" ? raw.synchronizerId : undefined,
37334
- };
37335
- }
37336
- /**
37337
- * Build a Splice token-standard transfer of `amount` of `token` from `sender`
37338
- * to `receiver`, carrying `memo`, ready to hand to a wallet's prepareExecute.
37339
- * Returns the full prepareExecute params (commandId + commands + actAs +
37340
- * disclosedContracts).
37341
- */
37342
- async function buildCantonTransfer(connector, { sender, receiver, amount, token, memo, nowMs, }) {
37343
- if (!token.originalAddress) {
37344
- throw new Error("Canton token originalAddress is required");
37345
- }
37346
- const instrument = parseCantonInstrument(token.originalAddress);
37347
- if (!instrument) {
37348
- throw new Error(`Invalid Canton token address: ${token.originalAddress}`);
37349
- }
37350
- const registryUrl = resolveCantonRegistryUrl(instrument);
37351
- const inputHoldingCids = await fetchInputHoldingCids(connector, sender, instrument, amount);
37352
- const choiceArgs = buildTransferChoiceArgs({
37353
- sender,
37354
- receiver,
37355
- amount,
37356
- instrument,
37357
- inputHoldingCids,
37358
- memo,
37359
- nowMs,
37360
- });
37361
- const factory = await fetchTransferFactory(registryUrl, choiceArgs);
37362
- const choiceContextData = factory.choiceContext.choiceContextData;
37363
- if (!choiceContextData) {
37364
- throw new Error("Transfer factory choice context is missing");
37365
- }
37366
- choiceArgs.extraArgs.context = choiceContextData;
37367
- const disclosedContracts = (factory.choiceContext?.disclosedContracts ?? []).flatMap((raw) => {
37368
- const disclosedContract = toDisclosedContract(raw);
37369
- return disclosedContract ? [disclosedContract] : [];
37370
- });
37371
- // Command ID: client-supplied identifier used to deduplicate submissions
37372
- const commandId = `canton-${memo}`;
37373
- return {
37374
- commandId,
37375
- commands: [
37376
- {
37377
- ExerciseCommand: {
37378
- templateId: TRANSFER_FACTORY_INTERFACE_ID,
37379
- contractId: factory.factoryId,
37380
- choice: TRANSFER_FACTORY_CHOICE,
37381
- choiceArgument: buildPrepareChoiceArgument(choiceArgs),
37382
- },
37383
- },
37384
- ],
37385
- actAs: [sender],
37386
- disclosedContracts,
37387
- };
37388
- }
37389
-
37390
37605
  const useExecuteTransaction = (squidRoute) => {
37391
37606
  const { fromChain, toChain, fromToken, toToken, isSameChain } = useSwap();
37392
37607
  // A route completes on source tx only if it's same-chain AND
@@ -38001,7 +38216,7 @@ const useExecuteTransaction = (squidRoute) => {
38001
38216
  }
38002
38217
  const amount = formatBNToReadable(fromAmount, token.decimals);
38003
38218
  dispatchSignatureRequestEvent(route);
38004
- const transferParams = await buildCantonTransfer(cantonSigner, {
38219
+ const result = await cantonSigner.transfer({
38005
38220
  sender: sourceUserAddress,
38006
38221
  receiver,
38007
38222
  amount,
@@ -38009,15 +38224,14 @@ const useExecuteTransaction = (squidRoute) => {
38009
38224
  memo: orderHash,
38010
38225
  nowMs: Date.now(),
38011
38226
  });
38012
- const result = await cantonSigner.prepareExecuteAndWait(transferParams);
38013
- const txHash = result.tx?.payload?.updateId ?? "";
38227
+ const txHash = result.updateId;
38014
38228
  if (txHash) {
38015
38229
  resetQueriesAfterTxSigned();
38016
38230
  }
38017
38231
  WidgetEvents.getInstance().dispatchSwapExecuteCall(route, txHash);
38018
38232
  const txParams = setTransactionState({
38019
38233
  route,
38020
- txHash: txHash,
38234
+ txHash,
38021
38235
  userAddress: sourceUserAddress,
38022
38236
  status: exports.TransactionStatus.INITIAL_LOADING,
38023
38237
  sourceStatus: exports.TransactionStatus.ONGOING,
@@ -38300,7 +38514,7 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
38300
38514
  //
38301
38515
  // If deposit address is not selected, we use the connected address as the source address instead
38302
38516
  const sourceUserAddress = isDepositAddressActive
38303
- ? depositRefundAddress ?? sourceConnectedAddress
38517
+ ? (depositRefundAddress ?? sourceConnectedAddress)
38304
38518
  : sourceConnectedAddress;
38305
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), [
38306
38520
  fromChain?.chainId,
@@ -38592,6 +38806,10 @@ const useAutoConnect = () => {
38592
38806
  const { chains } = useSquidChains();
38593
38807
  const { isInitialized } = useConfigStore();
38594
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);
38595
38813
  const { autoConnectEvm } = useEvmContext();
38596
38814
  const { autoConnectSolana } = useSolanaContext();
38597
38815
  const { autoConnectSui } = useSuiContext();
@@ -38659,6 +38877,15 @@ const useAutoConnect = () => {
38659
38877
  React.useEffect(() => {
38660
38878
  autoConnect();
38661
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]);
38662
38889
  return { autoConnect };
38663
38890
  };
38664
38891
 
@@ -39086,9 +39313,6 @@ exports.isChainflipBridgeTransaction = isChainflipBridgeTransaction;
39086
39313
  exports.isChainflipDepositRoute = isChainflipDepositRoute;
39087
39314
  exports.isCoralBridgeAction = isCoralBridgeAction;
39088
39315
  exports.isCosmosAddressValid = isCosmosAddressValid;
39089
- exports.isDanalAddressValid = isDanalAddressValid;
39090
- exports.isDanalDepositRoute = isDanalDepositRoute;
39091
- exports.isDanalTxHashValid = isDanalTxHashValid;
39092
39316
  exports.isDepositAddressDirectTransferRoute = isDepositAddressDirectTransferRoute;
39093
39317
  exports.isEmptyObject = isEmptyObject;
39094
39318
  exports.isEvmChainNotSupportedError = isEvmChainNotSupportedError;
@@ -39126,7 +39350,6 @@ exports.nativeXrplTokenAddress = nativeXrplTokenAddress;
39126
39350
  exports.normalizeError = normalizeError;
39127
39351
  exports.normalizeIbcAddress = normalizeIbcAddress;
39128
39352
  exports.normalizeTokenSymbol = normalizeTokenSymbol;
39129
- exports.parseDanalTransferBody = parseDanalTransferBody;
39130
39353
  exports.parseEvmAddress = parseEvmAddress;
39131
39354
  exports.parseInitialAssetsFromUrl = parseInitialAssetsFromUrl;
39132
39355
  exports.parseToBigInt = parseToBigInt;
@@ -39239,4 +39462,4 @@ exports.useXrplTrustLine = useXrplTrustLine;
39239
39462
  exports.waitForReceiptWithRetry = waitForReceiptWithRetry;
39240
39463
  exports.walletIconBaseUrl = walletIconBaseUrl;
39241
39464
  exports.walletSupportsChainType = walletSupportsChainType;
39242
- //# sourceMappingURL=index-BT6G_mgr.js.map
39465
+ //# sourceMappingURL=index-CJVX-7hj.js.map