@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.
- package/dist/core/canton/cantonConnector.d.ts +5 -2
- package/dist/core/canton/errors.d.ts +4 -0
- package/dist/core/canton/loopCantonConnector.d.ts +38 -0
- package/dist/core/client/index.d.ts +0 -1
- package/dist/core/constants.d.ts +0 -2
- package/dist/core/types/canton.d.ts +37 -3
- package/dist/core/types/index.d.ts +1 -1
- package/dist/core/types/tokens.d.ts +1 -8
- package/dist/core/types/wallet.d.ts +1 -8
- package/dist/hooks/chains/useSquidChains.d.ts +0 -1
- package/dist/hooks/store/useDepositAddressStore.d.ts +0 -6
- package/dist/hooks/swap/useDepositAddress.d.ts +0 -2
- package/dist/hooks/tokens/useSquidTokens.d.ts +0 -1
- package/dist/hooks/wallet/useMultiChainWallet.d.ts +0 -38
- package/dist/hooks/wallet/useWallet.d.ts +0 -42
- package/dist/{index-ByakOYXP.js → index-C3owE9lb.js} +740 -514
- package/dist/index-C3owE9lb.js.map +1 -0
- package/dist/{index-nTqoaD0B.js → index-CJVX-7hj.js} +739 -517
- package/dist/index-CJVX-7hj.js.map +1 -0
- package/dist/{index.es--AR2n7a0.js → index.es-09IsADsA.js} +2 -2
- package/dist/{index.es--AR2n7a0.js.map → index.es-09IsADsA.js.map} +1 -1
- package/dist/{index.es-DtFDz6qc.js → index.es-CTKuR0LC.js} +2 -2
- package/dist/{index.es-DtFDz6qc.js.map → index.es-CTKuR0LC.js.map} +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/{secretService-cgr3P1zw.js → secretService--tJFCkzT.js} +2 -2
- package/dist/{secretService-cgr3P1zw.js.map → secretService--tJFCkzT.js.map} +1 -1
- package/dist/{secretService-29Ak2qp5.js → secretService-GHdReJBs.js} +2 -2
- package/dist/{secretService-29Ak2qp5.js.map → secretService-GHdReJBs.js.map} +1 -1
- package/dist/server.esm.js +0 -6
- package/dist/server.esm.js.map +1 -1
- package/dist/server.js +0 -6
- package/dist/server.js.map +1 -1
- package/dist/services/external/rpcService.d.ts +2 -4
- package/dist/services/index.d.ts +0 -1
- package/dist/services/internal/assetsService.d.ts +1 -1
- package/dist/services/internal/cantonService.d.ts +2 -0
- package/dist/services/internal/cantonTransferService.d.ts +3 -3
- package/dist/services/internal/estimateService.d.ts +1 -1
- package/dist/services/internal/searchService.d.ts +1 -2
- package/dist/services/internal/transactionService.d.ts +0 -8
- package/dist/{stellarService.client-B__dTUBZ.js → stellarService.client-DAxgO3m3.js} +2 -2
- package/dist/{stellarService.client-B__dTUBZ.js.map → stellarService.client-DAxgO3m3.js.map} +1 -1
- package/dist/{stellarService.client-D-zkMI5s.js → stellarService.client-DvA-2pXq.js} +2 -2
- package/dist/{stellarService.client-D-zkMI5s.js.map → stellarService.client-DvA-2pXq.js.map} +1 -1
- package/dist/tests/loopSdkContract.test.d.ts +1 -0
- package/dist/tests/useAutoConnectCanton.test.d.ts +1 -0
- package/dist/tests/useCantonRestoration.test.d.ts +1 -0
- package/package.json +3 -2
- package/dist/core/types/danal.d.ts +0 -1
- package/dist/index-ByakOYXP.js.map +0 -1
- package/dist/index-nTqoaD0B.js.map +0 -1
- package/dist/services/internal/danalService.d.ts +0 -8
- /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
|
|
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.
|
|
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-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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-
|
|
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
|
|
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
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
39465
|
+
//# sourceMappingURL=index-CJVX-7hj.js.map
|