@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.
- 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/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-D6rznmR_.js → index-C3owE9lb.js} +739 -512
- package/dist/index-C3owE9lb.js.map +1 -0
- package/dist/{index-BT6G_mgr.js → index-CJVX-7hj.js} +738 -515
- package/dist/index-CJVX-7hj.js.map +1 -0
- package/dist/{index.es-Dl19u5sy.js → index.es-09IsADsA.js} +2 -2
- package/dist/{index.es-Dl19u5sy.js.map → index.es-09IsADsA.js.map} +1 -1
- package/dist/{index.es-BZb8tg2z.js → index.es-CTKuR0LC.js} +2 -2
- package/dist/{index.es-BZb8tg2z.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-Dvc9Dg4U.js → secretService--tJFCkzT.js} +2 -2
- package/dist/{secretService-Dvc9Dg4U.js.map → secretService--tJFCkzT.js.map} +1 -1
- package/dist/{secretService-TP5V9Jwg.js → secretService-GHdReJBs.js} +2 -2
- package/dist/{secretService-TP5V9Jwg.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/transactionService.d.ts +0 -8
- package/dist/{stellarService.client-c48cnGgL.js → stellarService.client-DAxgO3m3.js} +2 -2
- package/dist/{stellarService.client-c48cnGgL.js.map → stellarService.client-DAxgO3m3.js.map} +1 -1
- package/dist/{stellarService.client-WTkVJJhn.js → stellarService.client-DvA-2pXq.js} +2 -2
- package/dist/{stellarService.client-WTkVJJhn.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-BT6G_mgr.js.map +0 -1
- package/dist/index-D6rznmR_.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
|
@@ -59,7 +59,6 @@ const suiZeroAddress = "0x0000000000000000000000000000000000000000";
|
|
|
59
59
|
const xrplZeroAddress = "rrrrrrrrrrrrrrrrrrrrrhoLvTp";
|
|
60
60
|
const stellarZeroAddress = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF";
|
|
61
61
|
const cantonZeroAddress = "none::12200000000000000000000000000000000000000000000000000000000000000000";
|
|
62
|
-
const danalZeroAddress = "PCI00000000000000000000000000000000000000000000000000";
|
|
63
62
|
const chainTypeToZeroAddressMap = {
|
|
64
63
|
[ChainType.EVM]: zeroAddress$1,
|
|
65
64
|
[ChainType.COSMOS]: cosmosZeroAddress,
|
|
@@ -69,7 +68,6 @@ const chainTypeToZeroAddressMap = {
|
|
|
69
68
|
[ChainType.XRPL]: xrplZeroAddress,
|
|
70
69
|
[ChainType.STELLAR]: stellarZeroAddress,
|
|
71
70
|
[ChainType.CANTON]: cantonZeroAddress,
|
|
72
|
-
[ChainType.DANAL]: danalZeroAddress,
|
|
73
71
|
};
|
|
74
72
|
const nativeEvmTokenAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
|
75
73
|
const nativeCosmosTokenAddress = "uosmo";
|
|
@@ -79,7 +77,6 @@ const nativeSuiTokenAddress = "0x00000000000000000000000000000000000000000000000
|
|
|
79
77
|
const nativeXrplTokenAddress = "xrp";
|
|
80
78
|
const nativeStellarTokenAddress = "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA";
|
|
81
79
|
const nativeCantonTokenAddress = "DSO::1220b1431ef217342db44d516bb9befde802be7d8899637d290895fa58880f19accc";
|
|
82
|
-
const nativeDanalTokenAddress = "pci";
|
|
83
80
|
// by setting slippage to undefined, it's set to "auto"
|
|
84
81
|
const defaultSlippage = undefined;
|
|
85
82
|
const destinationAddressResetValue = "null";
|
|
@@ -145,7 +142,6 @@ const CHAIN_IDS = {
|
|
|
145
142
|
STELLAR: "stellar-mainnet",
|
|
146
143
|
STELLAR_TESTNET: "stellar-testnet",
|
|
147
144
|
CANTON: "canton",
|
|
148
|
-
DANAL_TESTNET: "danal-testnet",
|
|
149
145
|
};
|
|
150
146
|
const chainTypeToDefaultChainIdMap = {
|
|
151
147
|
[ChainType.EVM]: CHAIN_IDS.ETHEREUM,
|
|
@@ -156,7 +152,6 @@ const chainTypeToDefaultChainIdMap = {
|
|
|
156
152
|
[ChainType.XRPL]: CHAIN_IDS.XRPL,
|
|
157
153
|
[ChainType.STELLAR]: CHAIN_IDS.STELLAR,
|
|
158
154
|
[ChainType.CANTON]: CHAIN_IDS.CANTON,
|
|
159
|
-
[ChainType.DANAL]: CHAIN_IDS.DANAL_TESTNET,
|
|
160
155
|
};
|
|
161
156
|
const chainTypeToNativeTokenAddressMap = {
|
|
162
157
|
[ChainType.EVM]: nativeEvmTokenAddress,
|
|
@@ -167,7 +162,6 @@ const chainTypeToNativeTokenAddressMap = {
|
|
|
167
162
|
[ChainType.XRPL]: nativeXrplTokenAddress,
|
|
168
163
|
[ChainType.STELLAR]: nativeStellarTokenAddress,
|
|
169
164
|
[ChainType.CANTON]: nativeCantonTokenAddress,
|
|
170
|
-
[ChainType.DANAL]: nativeDanalTokenAddress,
|
|
171
165
|
};
|
|
172
166
|
const defaultConfigValues = {
|
|
173
167
|
integratorId: INTEGRATOR_ID,
|
|
@@ -20949,6 +20943,9 @@ const isBitcoinAddressValid = (address$1) => {
|
|
|
20949
20943
|
}
|
|
20950
20944
|
};
|
|
20951
20945
|
|
|
20946
|
+
function getCantonCommandId(memo) {
|
|
20947
|
+
return `canton-${memo}`;
|
|
20948
|
+
}
|
|
20952
20949
|
/**
|
|
20953
20950
|
* Format a Canton connector into a Squid {@link CantonWallet}. Shared by the
|
|
20954
20951
|
* announce-discovery and injected-provider paths.
|
|
@@ -21099,51 +21096,16 @@ function cantonInstrumentKey(instrument) {
|
|
|
21099
21096
|
* is already a Decimal, not base units).
|
|
21100
21097
|
*/
|
|
21101
21098
|
function groupCantonHoldingBalances(acsResponse) {
|
|
21099
|
+
return sumCantonHoldingBalances(parseCantonHoldings(acsResponse).filter((holding) => !holding.locked));
|
|
21100
|
+
}
|
|
21101
|
+
function sumCantonHoldingBalances(holdings) {
|
|
21102
21102
|
const totals = {};
|
|
21103
|
-
for (const holding of
|
|
21104
|
-
// Locked holdings are not spendable, skip them
|
|
21105
|
-
if (holding.locked)
|
|
21106
|
-
continue;
|
|
21103
|
+
for (const holding of holdings) {
|
|
21107
21104
|
const key = cantonInstrumentKey(holding.instrument);
|
|
21108
21105
|
const total = totals[key] ?? new BigNumber(0);
|
|
21109
21106
|
totals[key] = total.plus(new BigNumber(holding.amount));
|
|
21110
21107
|
}
|
|
21111
|
-
return Object.fromEntries(Object.entries(totals).map(([key, total]) => [key, total.
|
|
21112
|
-
}
|
|
21113
|
-
|
|
21114
|
-
// Matches the backend validation: "PCI" + 50 uppercase-hex chars, case-sensitive.
|
|
21115
|
-
const DANAL_ADDRESS_REGEX = /^PCI[0-9A-F]{50}$/;
|
|
21116
|
-
function isDanalAddressValid(address) {
|
|
21117
|
-
if (!address)
|
|
21118
|
-
return false;
|
|
21119
|
-
return DANAL_ADDRESS_REGEX.test(address);
|
|
21120
|
-
}
|
|
21121
|
-
// Fabric transaction id: 64 hex chars, no 0x prefix
|
|
21122
|
-
const DANAL_TX_HASH_REGEX = /^[0-9a-fA-F]{64}$/;
|
|
21123
|
-
function isDanalTxHashValid(hash) {
|
|
21124
|
-
if (!hash)
|
|
21125
|
-
return false;
|
|
21126
|
-
return DANAL_TX_HASH_REGEX.test(hash);
|
|
21127
|
-
}
|
|
21128
|
-
function isDanalTransferRequestBody(value) {
|
|
21129
|
-
if (typeof value !== "object" || value === null)
|
|
21130
|
-
return false;
|
|
21131
|
-
const { receiver, amount, memo } = value;
|
|
21132
|
-
return (typeof receiver === "string" &&
|
|
21133
|
-
receiver.length > 0 &&
|
|
21134
|
-
typeof amount === "string" &&
|
|
21135
|
-
amount.length > 0 &&
|
|
21136
|
-
typeof memo === "string" &&
|
|
21137
|
-
memo.length > 0);
|
|
21138
|
-
}
|
|
21139
|
-
function parseDanalTransferBody(data) {
|
|
21140
|
-
try {
|
|
21141
|
-
const parsed = JSON.parse(data);
|
|
21142
|
-
return isDanalTransferRequestBody(parsed) ? parsed : null;
|
|
21143
|
-
}
|
|
21144
|
-
catch {
|
|
21145
|
-
return null;
|
|
21146
|
-
}
|
|
21108
|
+
return Object.fromEntries(Object.entries(totals).map(([key, total]) => [key, total.toFixed()]));
|
|
21147
21109
|
}
|
|
21148
21110
|
|
|
21149
21111
|
const solanaTokenProgramIdByAddress = {
|
|
@@ -21482,15 +21444,6 @@ function isOnChainTxData(squidData) {
|
|
|
21482
21444
|
function isDepositAddressDirectTransferRoute(squidData) {
|
|
21483
21445
|
return squidData.type === SquidDataType.DepositAddressDirectTransfer;
|
|
21484
21446
|
}
|
|
21485
|
-
/**
|
|
21486
|
-
* Danal keeps SquidDataType.DepositAddressWithMemo, which XRPL/Stellar use for
|
|
21487
|
-
* wallet-signed transactions — so the deposit treatment is gated on the chain,
|
|
21488
|
-
* not just the type
|
|
21489
|
-
*/
|
|
21490
|
-
function isDanalDepositRoute(route) {
|
|
21491
|
-
return (route.params.fromChain === CHAIN_IDS.DANAL_TESTNET &&
|
|
21492
|
-
route.transactionRequest?.type === SquidDataType.DepositAddressWithMemo);
|
|
21493
|
-
}
|
|
21494
21447
|
function getHistoryTransactionId(tx) {
|
|
21495
21448
|
switch (tx.txType) {
|
|
21496
21449
|
case HistoryTxType.SWAP:
|
|
@@ -22015,11 +21968,6 @@ const chainTypeToTrimLength = {
|
|
|
22015
21968
|
start: 3,
|
|
22016
21969
|
end: 3,
|
|
22017
21970
|
},
|
|
22018
|
-
// PCI0180...E77E
|
|
22019
|
-
[ChainType.DANAL]: {
|
|
22020
|
-
start: 7,
|
|
22021
|
-
end: 4,
|
|
22022
|
-
},
|
|
22023
21971
|
};
|
|
22024
21972
|
const truncateWithEllipsis = (value, startLength, endLength) => value.length <= startLength + endLength
|
|
22025
21973
|
? value
|
|
@@ -22068,8 +22016,6 @@ const isWalletAddressValid = (chainData, address) => {
|
|
|
22068
22016
|
return isStellarAddressValid(address);
|
|
22069
22017
|
case ChainType.CANTON:
|
|
22070
22018
|
return isCantonAddressValid(address);
|
|
22071
|
-
case ChainType.DANAL:
|
|
22072
|
-
return isDanalAddressValid(address);
|
|
22073
22019
|
}
|
|
22074
22020
|
};
|
|
22075
22021
|
const redirectToExtensionsStore = (wallet) => {
|
|
@@ -22354,8 +22300,6 @@ const connectByChainType = async (chainType, wallet, defaultChain, params) => {
|
|
|
22354
22300
|
connector,
|
|
22355
22301
|
},
|
|
22356
22302
|
});
|
|
22357
|
-
case ChainType.DANAL:
|
|
22358
|
-
return null;
|
|
22359
22303
|
}
|
|
22360
22304
|
};
|
|
22361
22305
|
const getChainTypesToConnect = (selectedChainTypes, defaultChain, supportedChains) => {
|
|
@@ -22480,6 +22424,8 @@ const cancelConnectWallet = (wallet) => {
|
|
|
22480
22424
|
return;
|
|
22481
22425
|
}
|
|
22482
22426
|
switch (wallet.type) {
|
|
22427
|
+
case ChainType.CANTON:
|
|
22428
|
+
return wallet.connector.cancelConnect?.();
|
|
22483
22429
|
case ChainType.XRPL:
|
|
22484
22430
|
return wallet.connector.cancelConnect?.();
|
|
22485
22431
|
default:
|
|
@@ -23340,7 +23286,7 @@ const handleDestinationAddressOnSwapChange = ({ fromChainId, fromTokenAddress, t
|
|
|
23340
23286
|
});
|
|
23341
23287
|
let destinationTokenAddress = toChainId && !toTokenAddress
|
|
23342
23288
|
? defaultToAddress
|
|
23343
|
-
: toTokenAddress ?? swapRoute?.toTokenAddress;
|
|
23289
|
+
: (toTokenAddress ?? swapRoute?.toTokenAddress);
|
|
23344
23290
|
const lowerCaseDestinationTokenAddress = destinationTokenAddress?.toLowerCase();
|
|
23345
23291
|
if (destinationTokensFiltered.find((t) => t.address === lowerCaseDestinationTokenAddress) === undefined) {
|
|
23346
23292
|
destinationTokenAddress = defaultToAddress;
|
|
@@ -23392,7 +23338,7 @@ const getNewSwapParamsFromInput = ({ inputParams, initialSwapRoute, tokens, chai
|
|
|
23392
23338
|
toTokenAddress === initialSwapRoute?.fromTokenAddress;
|
|
23393
23339
|
const sourceTokenAddress = sameNewDestAndInitialSource || onlySourceChainIdChanged
|
|
23394
23340
|
? defaultFromAddress
|
|
23395
|
-
: fromTokenAddress ?? initialSwapRoute?.fromTokenAddress;
|
|
23341
|
+
: (fromTokenAddress ?? initialSwapRoute?.fromTokenAddress);
|
|
23396
23342
|
let destinationTokenAddress = undefined;
|
|
23397
23343
|
if (!sameNewSourceAndInitialDest) {
|
|
23398
23344
|
if (onlySourceChainIdChanged) {
|
|
@@ -23714,7 +23660,7 @@ const filterViewableTokens = (tokens, config, direction) => {
|
|
|
23714
23660
|
};
|
|
23715
23661
|
const getSecretNetworkBalances = async (chainData, cosmosAddress, squidTokens, keplrTypeWallet) => {
|
|
23716
23662
|
const squidSecretTokens = squidTokens.filter((t) => t.chainId === CHAIN_IDS.SECRET);
|
|
23717
|
-
const { fetchAllSecretBalances } = await import('./secretService
|
|
23663
|
+
const { fetchAllSecretBalances } = await import('./secretService--tJFCkzT.js');
|
|
23718
23664
|
return fetchAllSecretBalances(chainData, cosmosAddress, squidSecretTokens, keplrTypeWallet);
|
|
23719
23665
|
};
|
|
23720
23666
|
function getTokenAssetsKey(token) {
|
|
@@ -25708,7 +25654,7 @@ const useSquidTokens = (direction) => {
|
|
|
25708
25654
|
config.availableChains,
|
|
25709
25655
|
direction,
|
|
25710
25656
|
]);
|
|
25711
|
-
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens,
|
|
25657
|
+
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useMemo(() => {
|
|
25712
25658
|
return tokens?.reduce((acc, token) => {
|
|
25713
25659
|
switch (token.type) {
|
|
25714
25660
|
case ChainType.EVM:
|
|
@@ -25735,9 +25681,6 @@ const useSquidTokens = (direction) => {
|
|
|
25735
25681
|
case ChainType.CANTON:
|
|
25736
25682
|
acc.cantonTokens.push(token);
|
|
25737
25683
|
break;
|
|
25738
|
-
case ChainType.DANAL:
|
|
25739
|
-
acc.danalTokens.push(token);
|
|
25740
|
-
break;
|
|
25741
25684
|
}
|
|
25742
25685
|
return acc;
|
|
25743
25686
|
}, {
|
|
@@ -25749,7 +25692,6 @@ const useSquidTokens = (direction) => {
|
|
|
25749
25692
|
xrplTokens: [],
|
|
25750
25693
|
stellarTokens: [],
|
|
25751
25694
|
cantonTokens: [],
|
|
25752
|
-
danalTokens: [],
|
|
25753
25695
|
});
|
|
25754
25696
|
}, [tokens]);
|
|
25755
25697
|
const findToken = useCallback((address, chainId) => {
|
|
@@ -25768,7 +25710,6 @@ const useSquidTokens = (direction) => {
|
|
|
25768
25710
|
xrplTokens,
|
|
25769
25711
|
stellarTokens,
|
|
25770
25712
|
cantonTokens,
|
|
25771
|
-
danalTokens,
|
|
25772
25713
|
};
|
|
25773
25714
|
};
|
|
25774
25715
|
|
|
@@ -26305,7 +26246,6 @@ const chainTypeToRefetchInterval = {
|
|
|
26305
26246
|
[ChainType.XRPL]: 1_000,
|
|
26306
26247
|
[ChainType.STELLAR]: 1_000,
|
|
26307
26248
|
[ChainType.CANTON]: 1_000,
|
|
26308
|
-
[ChainType.DANAL]: 1_000,
|
|
26309
26249
|
};
|
|
26310
26250
|
/**
|
|
26311
26251
|
* Returns the status refetch interval of a Send transaction
|
|
@@ -26572,7 +26512,7 @@ const useSquidChains = (direction) => {
|
|
|
26572
26512
|
const chain = findChain(chainId);
|
|
26573
26513
|
return chain?.chainType;
|
|
26574
26514
|
}, [findChain]);
|
|
26575
|
-
const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains,
|
|
26515
|
+
const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains, } = useMemo(() => {
|
|
26576
26516
|
return chains.reduce((acc, chain) => {
|
|
26577
26517
|
switch (chain.chainType) {
|
|
26578
26518
|
case ChainType.EVM:
|
|
@@ -26593,9 +26533,6 @@ const useSquidChains = (direction) => {
|
|
|
26593
26533
|
case ChainType.CANTON:
|
|
26594
26534
|
acc.cantonChains.push(chain);
|
|
26595
26535
|
break;
|
|
26596
|
-
case ChainType.DANAL:
|
|
26597
|
-
acc.danalChains.push(chain);
|
|
26598
|
-
break;
|
|
26599
26536
|
}
|
|
26600
26537
|
return acc;
|
|
26601
26538
|
}, {
|
|
@@ -26605,7 +26542,6 @@ const useSquidChains = (direction) => {
|
|
|
26605
26542
|
xrplChains: [],
|
|
26606
26543
|
stellarChains: [],
|
|
26607
26544
|
cantonChains: [],
|
|
26608
|
-
danalChains: [],
|
|
26609
26545
|
});
|
|
26610
26546
|
}, [chains]);
|
|
26611
26547
|
const { supportedSourceChains, supportedDestinationChains } = useMemo(() => {
|
|
@@ -26627,7 +26563,6 @@ const useSquidChains = (direction) => {
|
|
|
26627
26563
|
xrplChains,
|
|
26628
26564
|
stellarChains,
|
|
26629
26565
|
cantonChains,
|
|
26630
|
-
danalChains,
|
|
26631
26566
|
getChainType,
|
|
26632
26567
|
findChain,
|
|
26633
26568
|
};
|
|
@@ -26941,7 +26876,343 @@ const useBitcoinContext = () => {
|
|
|
26941
26876
|
return context;
|
|
26942
26877
|
};
|
|
26943
26878
|
|
|
26944
|
-
const
|
|
26879
|
+
const vParty = (party) => ({ party });
|
|
26880
|
+
const vText = (text) => ({ text });
|
|
26881
|
+
const vNumeric = (numeric) => ({ numeric });
|
|
26882
|
+
const vContractId = (contractId) => ({
|
|
26883
|
+
contractId,
|
|
26884
|
+
});
|
|
26885
|
+
const vList = (elements) => ({
|
|
26886
|
+
list: { elements },
|
|
26887
|
+
});
|
|
26888
|
+
const vRecord = (fields) => ({
|
|
26889
|
+
record: { fields: fields.map(([label, value]) => ({ label, value })) },
|
|
26890
|
+
});
|
|
26891
|
+
const vTextMap = (entries) => ({ textMap: { entries } });
|
|
26892
|
+
const vTimestamp = (isoString) => ({
|
|
26893
|
+
timestamp: String(Math.floor(new Date(isoString).getTime() * 1000)),
|
|
26894
|
+
});
|
|
26895
|
+
function isRecord(value) {
|
|
26896
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26897
|
+
}
|
|
26898
|
+
function normalizeChoiceContextValue(value) {
|
|
26899
|
+
if (typeof value === "string")
|
|
26900
|
+
return value;
|
|
26901
|
+
if (!isRecord(value) || typeof value.tag !== "string")
|
|
26902
|
+
return null;
|
|
26903
|
+
switch (value.tag) {
|
|
26904
|
+
case "AV_Text":
|
|
26905
|
+
case "AV_ContractId":
|
|
26906
|
+
return typeof value.value === "string"
|
|
26907
|
+
? { tag: value.tag, value: value.value }
|
|
26908
|
+
: null;
|
|
26909
|
+
case "AV_Bool":
|
|
26910
|
+
return typeof value.value === "boolean"
|
|
26911
|
+
? { tag: value.tag, value: value.value }
|
|
26912
|
+
: null;
|
|
26913
|
+
case "AV_List":
|
|
26914
|
+
if (!Array.isArray(value.value))
|
|
26915
|
+
return null;
|
|
26916
|
+
return {
|
|
26917
|
+
tag: value.tag,
|
|
26918
|
+
value: value.value.flatMap((entry) => {
|
|
26919
|
+
const normalized = normalizeChoiceContextValue(entry);
|
|
26920
|
+
return normalized ? [normalized] : [];
|
|
26921
|
+
}),
|
|
26922
|
+
};
|
|
26923
|
+
default: {
|
|
26924
|
+
const inner = value.value;
|
|
26925
|
+
if (typeof inner === "string" ||
|
|
26926
|
+
typeof inner === "number" ||
|
|
26927
|
+
typeof inner === "boolean" ||
|
|
26928
|
+
inner === null) {
|
|
26929
|
+
return { tag: value.tag, value: inner };
|
|
26930
|
+
}
|
|
26931
|
+
if (Array.isArray(inner)) {
|
|
26932
|
+
return {
|
|
26933
|
+
tag: value.tag,
|
|
26934
|
+
value: inner.flatMap((entry) => {
|
|
26935
|
+
const normalized = normalizeChoiceContextValue(entry);
|
|
26936
|
+
return normalized ? [normalized] : [];
|
|
26937
|
+
}),
|
|
26938
|
+
};
|
|
26939
|
+
}
|
|
26940
|
+
if (!isRecord(inner))
|
|
26941
|
+
return null;
|
|
26942
|
+
const normalizedObject = {};
|
|
26943
|
+
for (const [key, entry] of Object.entries(inner)) {
|
|
26944
|
+
if (typeof entry !== "string" &&
|
|
26945
|
+
typeof entry !== "number" &&
|
|
26946
|
+
typeof entry !== "boolean" &&
|
|
26947
|
+
entry !== null) {
|
|
26948
|
+
return null;
|
|
26949
|
+
}
|
|
26950
|
+
normalizedObject[key] = entry;
|
|
26951
|
+
}
|
|
26952
|
+
return { tag: value.tag, value: normalizedObject };
|
|
26953
|
+
}
|
|
26954
|
+
}
|
|
26955
|
+
}
|
|
26956
|
+
function stringifyChoiceContextValue(value) {
|
|
26957
|
+
return typeof value === "string" ? value : String(value);
|
|
26958
|
+
}
|
|
26959
|
+
function choiceContextValueToAnyValue(value) {
|
|
26960
|
+
if (typeof value === "string") {
|
|
26961
|
+
return {
|
|
26962
|
+
variant: { constructor: "AV_Text", value: vText(value) },
|
|
26963
|
+
};
|
|
26964
|
+
}
|
|
26965
|
+
switch (value.tag) {
|
|
26966
|
+
case "AV_ContractId":
|
|
26967
|
+
if (typeof value.value !== "string") {
|
|
26968
|
+
throw new TypeError("AV_ContractId values must be strings");
|
|
26969
|
+
}
|
|
26970
|
+
return {
|
|
26971
|
+
variant: { constructor: value.tag, value: vContractId(value.value) },
|
|
26972
|
+
};
|
|
26973
|
+
case "AV_Bool":
|
|
26974
|
+
if (typeof value.value !== "boolean") {
|
|
26975
|
+
throw new TypeError("AV_Bool values must be booleans");
|
|
26976
|
+
}
|
|
26977
|
+
return {
|
|
26978
|
+
variant: { constructor: value.tag, value: { bool: value.value } },
|
|
26979
|
+
};
|
|
26980
|
+
case "AV_List":
|
|
26981
|
+
if (!Array.isArray(value.value)) {
|
|
26982
|
+
throw new TypeError("AV_List values must be arrays");
|
|
26983
|
+
}
|
|
26984
|
+
return {
|
|
26985
|
+
variant: {
|
|
26986
|
+
constructor: value.tag,
|
|
26987
|
+
value: vList(value.value.map(choiceContextValueToAnyValue)),
|
|
26988
|
+
},
|
|
26989
|
+
};
|
|
26990
|
+
default:
|
|
26991
|
+
return {
|
|
26992
|
+
variant: {
|
|
26993
|
+
constructor: value.tag,
|
|
26994
|
+
value: vText(stringifyChoiceContextValue(value.value)),
|
|
26995
|
+
},
|
|
26996
|
+
};
|
|
26997
|
+
}
|
|
26998
|
+
}
|
|
26999
|
+
|
|
27000
|
+
/** Metadata key used by Splice token-standard transfers to carry our Squid orderhash memo. */
|
|
27001
|
+
const CANTON_MEMO_KEY = "splice.lfdecentralizedtrust.org/reason";
|
|
27002
|
+
/** Interface id for the transfer factory contract returned by the token registry. */
|
|
27003
|
+
const TRANSFER_FACTORY_INTERFACE_ID = "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory";
|
|
27004
|
+
/** Choice exercised on the transfer factory to create the token-standard transfer. */
|
|
27005
|
+
const TRANSFER_FACTORY_CHOICE = "TransferFactory_Transfer";
|
|
27006
|
+
/** CIP-056 (non-Amulet) tokens expose a registrar parameterized by admin party. */
|
|
27007
|
+
const CIP056_REGISTRAR_BASE = "https://api.utilities.digitalasset.com/api/token-standard/v0/registrars";
|
|
27008
|
+
const TRANSFER_REQUESTED_AT_SKEW_MS = 60_000;
|
|
27009
|
+
const TRANSFER_EXECUTE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
27010
|
+
function resolveCantonRegistryUrl(instrument) {
|
|
27011
|
+
if (instrument.id === CANTON_AMULET_INSTRUMENT_ID) {
|
|
27012
|
+
return CANTON_AMULET_REGISTRY_URL;
|
|
27013
|
+
}
|
|
27014
|
+
return `${CIP056_REGISTRAR_BASE}/${encodeURIComponent(instrument.admin)}`;
|
|
27015
|
+
}
|
|
27016
|
+
/**
|
|
27017
|
+
* Build the plain JSON arguments expected by the registry transfer-factory
|
|
27018
|
+
* endpoint before it adds choice context and disclosed contracts.
|
|
27019
|
+
*/
|
|
27020
|
+
function buildTransferChoiceArgs({ sender, receiver, amount, instrument, inputHoldingCids, memo, nowMs, }) {
|
|
27021
|
+
return {
|
|
27022
|
+
expectedAdmin: instrument.admin,
|
|
27023
|
+
transfer: {
|
|
27024
|
+
sender,
|
|
27025
|
+
receiver,
|
|
27026
|
+
amount,
|
|
27027
|
+
instrumentId: { admin: instrument.admin, id: instrument.id },
|
|
27028
|
+
requestedAt: new Date(nowMs - TRANSFER_REQUESTED_AT_SKEW_MS).toISOString(),
|
|
27029
|
+
executeBefore: new Date(nowMs + TRANSFER_EXECUTE_WINDOW_MS).toISOString(),
|
|
27030
|
+
inputHoldingCids,
|
|
27031
|
+
meta: { values: { [CANTON_MEMO_KEY]: memo } },
|
|
27032
|
+
},
|
|
27033
|
+
extraArgs: { context: { values: {} }, meta: { values: {} } },
|
|
27034
|
+
};
|
|
27035
|
+
}
|
|
27036
|
+
/**
|
|
27037
|
+
* Canton's Holding contracts follow Bitcoin-style UTXOs model.
|
|
27038
|
+
*
|
|
27039
|
+
* Pick Holding contract ids covering `amount` for the instrument: prefer a
|
|
27040
|
+
* single exact-amount holding, else accumulate largest-first.
|
|
27041
|
+
*/
|
|
27042
|
+
function selectInputHoldingCids(holdings, instrument, amount) {
|
|
27043
|
+
const spendableHoldings = holdings.filter((h) => !h.locked &&
|
|
27044
|
+
h.instrument.admin === instrument.admin &&
|
|
27045
|
+
h.instrument.id === instrument.id);
|
|
27046
|
+
const target = new BigNumber(amount);
|
|
27047
|
+
const exact = spendableHoldings.find((h) => new BigNumber(h.amount).eq(target));
|
|
27048
|
+
if (exact)
|
|
27049
|
+
return [exact.contractId];
|
|
27050
|
+
const sorted = [...spendableHoldings].sort((a, b) => new BigNumber(b.amount).comparedTo(new BigNumber(a.amount)));
|
|
27051
|
+
const selected = [];
|
|
27052
|
+
let sum = new BigNumber(0);
|
|
27053
|
+
for (const holding of sorted) {
|
|
27054
|
+
selected.push(holding.contractId);
|
|
27055
|
+
sum = sum.plus(holding.amount);
|
|
27056
|
+
if (sum.gte(target))
|
|
27057
|
+
break;
|
|
27058
|
+
}
|
|
27059
|
+
if (sum.lt(target)) {
|
|
27060
|
+
throw new Error("Insufficient Canton holdings to cover transfer amount");
|
|
27061
|
+
}
|
|
27062
|
+
return selected;
|
|
27063
|
+
}
|
|
27064
|
+
async function fetchInputHoldingCids(connector, party, instrument, amount) {
|
|
27065
|
+
const ledgerEnd = await connector.ledgerApi({
|
|
27066
|
+
requestMethod: "get",
|
|
27067
|
+
resource: "/v2/state/ledger-end",
|
|
27068
|
+
});
|
|
27069
|
+
const activeContracts = await connector.ledgerApi({
|
|
27070
|
+
requestMethod: "post",
|
|
27071
|
+
resource: "/v2/state/active-contracts",
|
|
27072
|
+
body: buildHoldingsAcsRequestBody(party, String(ledgerEnd.offset)),
|
|
27073
|
+
});
|
|
27074
|
+
return selectInputHoldingCids(parseCantonHoldings(activeContracts), instrument, amount);
|
|
27075
|
+
}
|
|
27076
|
+
/**
|
|
27077
|
+
* Ask the token registry for the transfer factory contract and extra context
|
|
27078
|
+
* required to exercise its transfer choice.
|
|
27079
|
+
*/
|
|
27080
|
+
async function fetchTransferFactory(registryUrl, choiceArgs) {
|
|
27081
|
+
const response = await fetch(`${registryUrl}/registry/transfer-instruction/v1/transfer-factory`, {
|
|
27082
|
+
method: "POST",
|
|
27083
|
+
headers: { "Content-Type": "application/json" },
|
|
27084
|
+
body: JSON.stringify({
|
|
27085
|
+
choiceArguments: choiceArgs,
|
|
27086
|
+
excludeDebugFields: true,
|
|
27087
|
+
}),
|
|
27088
|
+
});
|
|
27089
|
+
if (!response.ok) {
|
|
27090
|
+
throw new Error(`Canton transfer-factory request failed (${response.status})`);
|
|
27091
|
+
}
|
|
27092
|
+
return response.json();
|
|
27093
|
+
}
|
|
27094
|
+
/**
|
|
27095
|
+
* Convert transfer choice args into strict Daml Value JSON for the wallet's
|
|
27096
|
+
* prepareExecute ExerciseCommand.
|
|
27097
|
+
*/
|
|
27098
|
+
function buildPrepareChoiceArgument(choiceArgs) {
|
|
27099
|
+
const contextValues = Object.entries(choiceArgs.extraArgs.context?.values ?? {})
|
|
27100
|
+
.map(([key, value]) => {
|
|
27101
|
+
const normalized = normalizeChoiceContextValue(value);
|
|
27102
|
+
return normalized
|
|
27103
|
+
? { key, value: choiceContextValueToAnyValue(normalized) }
|
|
27104
|
+
: undefined;
|
|
27105
|
+
})
|
|
27106
|
+
.filter((entry) => Boolean(entry));
|
|
27107
|
+
return vRecord([
|
|
27108
|
+
["expectedAdmin", vParty(choiceArgs.expectedAdmin)],
|
|
27109
|
+
[
|
|
27110
|
+
"transfer",
|
|
27111
|
+
vRecord([
|
|
27112
|
+
["sender", vParty(choiceArgs.transfer.sender)],
|
|
27113
|
+
["receiver", vParty(choiceArgs.transfer.receiver)],
|
|
27114
|
+
["amount", vNumeric(choiceArgs.transfer.amount)],
|
|
27115
|
+
[
|
|
27116
|
+
"instrumentId",
|
|
27117
|
+
vRecord([
|
|
27118
|
+
["admin", vParty(choiceArgs.transfer.instrumentId.admin)],
|
|
27119
|
+
["id", vText(choiceArgs.transfer.instrumentId.id)],
|
|
27120
|
+
]),
|
|
27121
|
+
],
|
|
27122
|
+
["requestedAt", vTimestamp(choiceArgs.transfer.requestedAt)],
|
|
27123
|
+
["executeBefore", vTimestamp(choiceArgs.transfer.executeBefore)],
|
|
27124
|
+
[
|
|
27125
|
+
"inputHoldingCids",
|
|
27126
|
+
vList(choiceArgs.transfer.inputHoldingCids.map(vContractId)),
|
|
27127
|
+
],
|
|
27128
|
+
[
|
|
27129
|
+
"meta",
|
|
27130
|
+
vRecord([
|
|
27131
|
+
[
|
|
27132
|
+
"values",
|
|
27133
|
+
vTextMap(Object.entries(choiceArgs.transfer.meta.values).map(([key, value]) => ({ key, value: vText(value) }))),
|
|
27134
|
+
],
|
|
27135
|
+
]),
|
|
27136
|
+
],
|
|
27137
|
+
]),
|
|
27138
|
+
],
|
|
27139
|
+
[
|
|
27140
|
+
"extraArgs",
|
|
27141
|
+
vRecord([
|
|
27142
|
+
["context", vRecord([["values", vTextMap(contextValues)]])],
|
|
27143
|
+
["meta", vRecord([["values", vTextMap([])]])],
|
|
27144
|
+
]),
|
|
27145
|
+
],
|
|
27146
|
+
]);
|
|
27147
|
+
}
|
|
27148
|
+
/**
|
|
27149
|
+
* Convert a registry disclosed contract into the wallet submission shape,
|
|
27150
|
+
* dropping debug fields and entries without a usable createdEventBlob.
|
|
27151
|
+
*/
|
|
27152
|
+
function toDisclosedContract(raw) {
|
|
27153
|
+
if (typeof raw.createdEventBlob !== "string")
|
|
27154
|
+
return null;
|
|
27155
|
+
return {
|
|
27156
|
+
createdEventBlob: raw.createdEventBlob,
|
|
27157
|
+
contractId: typeof raw.contractId === "string" ? raw.contractId : undefined,
|
|
27158
|
+
synchronizerId: typeof raw.synchronizerId === "string" ? raw.synchronizerId : undefined,
|
|
27159
|
+
};
|
|
27160
|
+
}
|
|
27161
|
+
/**
|
|
27162
|
+
* Build a Splice token-standard transfer of `amount` of `token` from `sender`
|
|
27163
|
+
* to `receiver`, carrying `memo`, ready to hand to a wallet's prepareExecute.
|
|
27164
|
+
* Returns the full prepareExecute params (commandId + commands + actAs +
|
|
27165
|
+
* disclosedContracts).
|
|
27166
|
+
*/
|
|
27167
|
+
async function buildCantonTransfer(connector, { sender, receiver, amount, token, memo, nowMs, }) {
|
|
27168
|
+
if (!token.originalAddress) {
|
|
27169
|
+
throw new Error("Canton token originalAddress is required");
|
|
27170
|
+
}
|
|
27171
|
+
const instrument = parseCantonInstrument(token.originalAddress);
|
|
27172
|
+
if (!instrument) {
|
|
27173
|
+
throw new Error(`Invalid Canton token address: ${token.originalAddress}`);
|
|
27174
|
+
}
|
|
27175
|
+
const registryUrl = resolveCantonRegistryUrl(instrument);
|
|
27176
|
+
const inputHoldingCids = await fetchInputHoldingCids(connector, sender, instrument, amount);
|
|
27177
|
+
const choiceArgs = buildTransferChoiceArgs({
|
|
27178
|
+
sender,
|
|
27179
|
+
receiver,
|
|
27180
|
+
amount,
|
|
27181
|
+
instrument,
|
|
27182
|
+
inputHoldingCids,
|
|
27183
|
+
memo,
|
|
27184
|
+
nowMs,
|
|
27185
|
+
});
|
|
27186
|
+
const factory = await fetchTransferFactory(registryUrl, choiceArgs);
|
|
27187
|
+
const choiceContextData = factory.choiceContext.choiceContextData;
|
|
27188
|
+
if (!choiceContextData) {
|
|
27189
|
+
throw new Error("Transfer factory choice context is missing");
|
|
27190
|
+
}
|
|
27191
|
+
choiceArgs.extraArgs.context = choiceContextData;
|
|
27192
|
+
const disclosedContracts = (factory.choiceContext?.disclosedContracts ?? []).flatMap((raw) => {
|
|
27193
|
+
const disclosedContract = toDisclosedContract(raw);
|
|
27194
|
+
return disclosedContract ? [disclosedContract] : [];
|
|
27195
|
+
});
|
|
27196
|
+
// Command ID: client-supplied identifier used to deduplicate submissions
|
|
27197
|
+
const commandId = getCantonCommandId(memo);
|
|
27198
|
+
return {
|
|
27199
|
+
commandId,
|
|
27200
|
+
commands: [
|
|
27201
|
+
{
|
|
27202
|
+
ExerciseCommand: {
|
|
27203
|
+
templateId: TRANSFER_FACTORY_INTERFACE_ID,
|
|
27204
|
+
contractId: factory.factoryId,
|
|
27205
|
+
choice: TRANSFER_FACTORY_CHOICE,
|
|
27206
|
+
choiceArgument: buildPrepareChoiceArgument(choiceArgs),
|
|
27207
|
+
},
|
|
27208
|
+
},
|
|
27209
|
+
],
|
|
27210
|
+
actAs: [sender],
|
|
27211
|
+
disclosedContracts,
|
|
27212
|
+
};
|
|
27213
|
+
}
|
|
27214
|
+
|
|
27215
|
+
const REQUEST_TIMEOUT_MS$1 = 60_000;
|
|
26945
27216
|
function pickAddress(accounts) {
|
|
26946
27217
|
return (accounts.find((a) => a.primary) ?? accounts[0])?.partyId;
|
|
26947
27218
|
}
|
|
@@ -26950,6 +27221,7 @@ function pickAddress(accounts) {
|
|
|
26950
27221
|
* (bound to its announce `target`) and exposes the lifecycle methods.
|
|
26951
27222
|
*/
|
|
26952
27223
|
class CantonBrowserConnector {
|
|
27224
|
+
kind = "cip103";
|
|
26953
27225
|
providerId;
|
|
26954
27226
|
name;
|
|
26955
27227
|
target;
|
|
@@ -26964,7 +27236,7 @@ class CantonBrowserConnector {
|
|
|
26964
27236
|
client ??
|
|
26965
27237
|
createCantonClient(new WindowTransport(win, {
|
|
26966
27238
|
target: this.target,
|
|
26967
|
-
timeout: REQUEST_TIMEOUT_MS,
|
|
27239
|
+
timeout: REQUEST_TIMEOUT_MS$1,
|
|
26968
27240
|
}));
|
|
26969
27241
|
}
|
|
26970
27242
|
get accounts() {
|
|
@@ -27010,6 +27282,28 @@ class CantonBrowserConnector {
|
|
|
27010
27282
|
this._accounts = await this.client.listAccounts();
|
|
27011
27283
|
return this._accounts;
|
|
27012
27284
|
}
|
|
27285
|
+
async getBalances(partyId) {
|
|
27286
|
+
const { offset } = await this.ledgerApi({
|
|
27287
|
+
requestMethod: "get",
|
|
27288
|
+
resource: "/v2/state/ledger-end",
|
|
27289
|
+
});
|
|
27290
|
+
const contracts = await this.ledgerApi({
|
|
27291
|
+
requestMethod: "post",
|
|
27292
|
+
resource: "/v2/state/active-contracts",
|
|
27293
|
+
body: buildHoldingsAcsRequestBody(partyId, String(offset)),
|
|
27294
|
+
});
|
|
27295
|
+
return groupCantonHoldingBalances(contracts);
|
|
27296
|
+
}
|
|
27297
|
+
async transfer(params) {
|
|
27298
|
+
const payload = await buildCantonTransfer(this, params);
|
|
27299
|
+
const result = await this.prepareExecuteAndWait(payload);
|
|
27300
|
+
const updateId = result.tx?.payload?.updateId ?? "";
|
|
27301
|
+
return {
|
|
27302
|
+
status: "completed",
|
|
27303
|
+
commandId: getCantonCommandId(params.memo),
|
|
27304
|
+
updateId,
|
|
27305
|
+
};
|
|
27306
|
+
}
|
|
27013
27307
|
on(event, handler) {
|
|
27014
27308
|
this.listeners[event].add(handler);
|
|
27015
27309
|
}
|
|
@@ -27116,6 +27410,313 @@ function discoverInjectedCantonWallets(win) {
|
|
|
27116
27410
|
});
|
|
27117
27411
|
}
|
|
27118
27412
|
|
|
27413
|
+
class CantonConnectionCancelledError extends Error {
|
|
27414
|
+
code = 4001;
|
|
27415
|
+
constructor() {
|
|
27416
|
+
super("Wallet connection cancelled");
|
|
27417
|
+
}
|
|
27418
|
+
}
|
|
27419
|
+
|
|
27420
|
+
const REQUEST_TIMEOUT_MS = 5 * 60_000;
|
|
27421
|
+
const OVERLAY_ID = "loop-sdk-connect-overlay";
|
|
27422
|
+
class LoopSubmissionUnknownError extends Error {
|
|
27423
|
+
constructor(cause) {
|
|
27424
|
+
super("Loop transfer outcome is unknown. Check Loop before trying again.", {
|
|
27425
|
+
cause,
|
|
27426
|
+
});
|
|
27427
|
+
}
|
|
27428
|
+
}
|
|
27429
|
+
function parseLoopTransferUpdate(value, commandId) {
|
|
27430
|
+
if (!value || typeof value !== "object")
|
|
27431
|
+
return undefined;
|
|
27432
|
+
const response = value;
|
|
27433
|
+
if (response.command_id !== commandId)
|
|
27434
|
+
return undefined;
|
|
27435
|
+
if (response.status === "failed" || response.error) {
|
|
27436
|
+
const error = response.error;
|
|
27437
|
+
return {
|
|
27438
|
+
status: "failed",
|
|
27439
|
+
commandId,
|
|
27440
|
+
message: typeof error?.error_message === "string"
|
|
27441
|
+
? error.error_message
|
|
27442
|
+
: "Loop transaction failed",
|
|
27443
|
+
};
|
|
27444
|
+
}
|
|
27445
|
+
if (typeof response.update_id === "string" && response.update_id) {
|
|
27446
|
+
return { status: "completed", commandId, updateId: response.update_id };
|
|
27447
|
+
}
|
|
27448
|
+
return undefined;
|
|
27449
|
+
}
|
|
27450
|
+
class LoopCantonConnector {
|
|
27451
|
+
loadSdk;
|
|
27452
|
+
kind = "loop";
|
|
27453
|
+
providerId = "canton-loop";
|
|
27454
|
+
name = "Loop";
|
|
27455
|
+
target = undefined;
|
|
27456
|
+
module;
|
|
27457
|
+
initialization;
|
|
27458
|
+
provider;
|
|
27459
|
+
pendingConnection;
|
|
27460
|
+
connectionStart = Promise.resolve();
|
|
27461
|
+
transfers = new Map();
|
|
27462
|
+
listeners = { connect: new Set(), disconnect: new Set() };
|
|
27463
|
+
constructor(loadSdk = () => import('@fivenorth/loop-sdk')) {
|
|
27464
|
+
this.loadSdk = loadSdk;
|
|
27465
|
+
}
|
|
27466
|
+
get accounts() {
|
|
27467
|
+
return this.provider
|
|
27468
|
+
? [{ partyId: this.provider.party_id, primary: true }]
|
|
27469
|
+
: [];
|
|
27470
|
+
}
|
|
27471
|
+
get address() {
|
|
27472
|
+
return this.provider?.party_id;
|
|
27473
|
+
}
|
|
27474
|
+
connect() {
|
|
27475
|
+
return this.startConnection(true);
|
|
27476
|
+
}
|
|
27477
|
+
autoConnect() {
|
|
27478
|
+
return this.startConnection(false);
|
|
27479
|
+
}
|
|
27480
|
+
cancelConnect() {
|
|
27481
|
+
if (!this.pendingConnection)
|
|
27482
|
+
return;
|
|
27483
|
+
this.rejectConnection(new CantonConnectionCancelledError());
|
|
27484
|
+
this.module?.loop.logout();
|
|
27485
|
+
}
|
|
27486
|
+
async disconnect() {
|
|
27487
|
+
this.cancelConnect();
|
|
27488
|
+
this.module?.loop.logout();
|
|
27489
|
+
this.provider = undefined;
|
|
27490
|
+
this.emit("disconnect");
|
|
27491
|
+
}
|
|
27492
|
+
async getAccounts() {
|
|
27493
|
+
const provider = this.requireProvider();
|
|
27494
|
+
try {
|
|
27495
|
+
const account = await provider.getAccount();
|
|
27496
|
+
if (account.party_id !== provider.party_id) {
|
|
27497
|
+
await this.disconnect();
|
|
27498
|
+
throw new Error("Loop account changed. Connect again.");
|
|
27499
|
+
}
|
|
27500
|
+
return this.accounts;
|
|
27501
|
+
}
|
|
27502
|
+
catch (error) {
|
|
27503
|
+
if (this.module && error instanceof this.module.UnauthorizedError) {
|
|
27504
|
+
await this.disconnect();
|
|
27505
|
+
}
|
|
27506
|
+
throw error;
|
|
27507
|
+
}
|
|
27508
|
+
}
|
|
27509
|
+
async getBalances(partyId) {
|
|
27510
|
+
const provider = this.requireProvider();
|
|
27511
|
+
if (partyId !== provider.party_id) {
|
|
27512
|
+
throw new Error("Loop balances require the connected account");
|
|
27513
|
+
}
|
|
27514
|
+
const holdings = await provider.getHolding();
|
|
27515
|
+
return sumCantonHoldingBalances(holdings.map((holding) => {
|
|
27516
|
+
const amount = new BigNumber(holding.total_unlocked_coin);
|
|
27517
|
+
if (!amount.isFinite() || amount.isNegative()) {
|
|
27518
|
+
throw new Error("Loop returned an invalid balance");
|
|
27519
|
+
}
|
|
27520
|
+
return {
|
|
27521
|
+
instrument: holding.instrument_id,
|
|
27522
|
+
amount: holding.total_unlocked_coin,
|
|
27523
|
+
};
|
|
27524
|
+
}));
|
|
27525
|
+
}
|
|
27526
|
+
transfer(params) {
|
|
27527
|
+
const commandId = getCantonCommandId(params.memo);
|
|
27528
|
+
const existing = this.transfers.get(commandId);
|
|
27529
|
+
if (existing)
|
|
27530
|
+
return existing;
|
|
27531
|
+
const request = this.submitTransfer(params, commandId).catch((error) => {
|
|
27532
|
+
if (!(error instanceof LoopSubmissionUnknownError))
|
|
27533
|
+
this.transfers.delete(commandId);
|
|
27534
|
+
throw error;
|
|
27535
|
+
});
|
|
27536
|
+
this.transfers.set(commandId, request);
|
|
27537
|
+
return request;
|
|
27538
|
+
}
|
|
27539
|
+
on(event, handler) {
|
|
27540
|
+
this.listeners[event].add(handler);
|
|
27541
|
+
}
|
|
27542
|
+
off(event, handler) {
|
|
27543
|
+
this.listeners[event].delete(handler);
|
|
27544
|
+
}
|
|
27545
|
+
emit(event, ...args) {
|
|
27546
|
+
this.listeners[event].forEach((handler) => handler(...args));
|
|
27547
|
+
}
|
|
27548
|
+
initialize() {
|
|
27549
|
+
if (!this.initialization) {
|
|
27550
|
+
this.initialization = this.loadSdk()
|
|
27551
|
+
.then((module) => {
|
|
27552
|
+
module.loop.init({
|
|
27553
|
+
appName: SQUID_METADATA.name,
|
|
27554
|
+
network: "mainnet",
|
|
27555
|
+
onAccept: (provider) => this.acceptConnection(provider),
|
|
27556
|
+
onReject: () => {
|
|
27557
|
+
this.rejectConnection(new CantonConnectionCancelledError());
|
|
27558
|
+
this.provider = undefined;
|
|
27559
|
+
this.emit("disconnect");
|
|
27560
|
+
},
|
|
27561
|
+
});
|
|
27562
|
+
this.module = module;
|
|
27563
|
+
})
|
|
27564
|
+
.catch((error) => {
|
|
27565
|
+
this.initialization = undefined;
|
|
27566
|
+
throw error;
|
|
27567
|
+
});
|
|
27568
|
+
}
|
|
27569
|
+
return this.initialization;
|
|
27570
|
+
}
|
|
27571
|
+
startConnection(interactive) {
|
|
27572
|
+
if (this.pendingConnection)
|
|
27573
|
+
return this.pendingConnection.promise;
|
|
27574
|
+
let resolve;
|
|
27575
|
+
let reject;
|
|
27576
|
+
const promise = new Promise((accept, decline) => {
|
|
27577
|
+
resolve = accept;
|
|
27578
|
+
reject = decline;
|
|
27579
|
+
});
|
|
27580
|
+
const timer = setTimeout(() => {
|
|
27581
|
+
this.rejectConnection(new Error("Loop connection timed out"));
|
|
27582
|
+
this.module?.loop.logout();
|
|
27583
|
+
}, REQUEST_TIMEOUT_MS);
|
|
27584
|
+
let observer;
|
|
27585
|
+
const pending = {
|
|
27586
|
+
promise,
|
|
27587
|
+
resolve,
|
|
27588
|
+
reject,
|
|
27589
|
+
cleanup: () => {
|
|
27590
|
+
clearTimeout(timer);
|
|
27591
|
+
observer?.disconnect();
|
|
27592
|
+
},
|
|
27593
|
+
};
|
|
27594
|
+
this.pendingConnection = pending;
|
|
27595
|
+
this.connectionStart = this.connectionStart
|
|
27596
|
+
.then(async () => {
|
|
27597
|
+
await this.initialize();
|
|
27598
|
+
if (this.pendingConnection !== pending)
|
|
27599
|
+
return;
|
|
27600
|
+
if (interactive && typeof MutationObserver !== "undefined") {
|
|
27601
|
+
let sawOverlay = false;
|
|
27602
|
+
observer = new MutationObserver(() => {
|
|
27603
|
+
const overlay = document.getElementById(OVERLAY_ID);
|
|
27604
|
+
if (overlay)
|
|
27605
|
+
sawOverlay = true;
|
|
27606
|
+
if (sawOverlay && !overlay && this.pendingConnection === pending) {
|
|
27607
|
+
this.cancelConnect();
|
|
27608
|
+
}
|
|
27609
|
+
});
|
|
27610
|
+
observer.observe(document.body, { childList: true });
|
|
27611
|
+
}
|
|
27612
|
+
const sdk = this.module.loop;
|
|
27613
|
+
if (interactive)
|
|
27614
|
+
await sdk.connect();
|
|
27615
|
+
else
|
|
27616
|
+
await sdk.autoConnect();
|
|
27617
|
+
if (!interactive && this.pendingConnection === pending) {
|
|
27618
|
+
this.rejectConnection(new Error("No saved Loop connection"));
|
|
27619
|
+
}
|
|
27620
|
+
else if (interactive &&
|
|
27621
|
+
this.pendingConnection !== pending &&
|
|
27622
|
+
!this.provider) {
|
|
27623
|
+
sdk.logout();
|
|
27624
|
+
}
|
|
27625
|
+
})
|
|
27626
|
+
.catch((error) => {
|
|
27627
|
+
if (this.pendingConnection === pending)
|
|
27628
|
+
this.rejectConnection(error);
|
|
27629
|
+
});
|
|
27630
|
+
return promise;
|
|
27631
|
+
}
|
|
27632
|
+
acceptConnection(provider) {
|
|
27633
|
+
const pending = this.pendingConnection;
|
|
27634
|
+
if (!pending)
|
|
27635
|
+
return;
|
|
27636
|
+
if (!provider.party_id) {
|
|
27637
|
+
this.rejectConnection(new Error("Loop returned no account"));
|
|
27638
|
+
return;
|
|
27639
|
+
}
|
|
27640
|
+
this.provider = provider;
|
|
27641
|
+
this.pendingConnection = undefined;
|
|
27642
|
+
pending.cleanup();
|
|
27643
|
+
pending.resolve({ address: provider.party_id, accounts: this.accounts });
|
|
27644
|
+
this.emit("connect", provider.party_id);
|
|
27645
|
+
}
|
|
27646
|
+
rejectConnection(error) {
|
|
27647
|
+
const pending = this.pendingConnection;
|
|
27648
|
+
if (!pending)
|
|
27649
|
+
return;
|
|
27650
|
+
this.pendingConnection = undefined;
|
|
27651
|
+
pending.cleanup();
|
|
27652
|
+
pending.reject(error);
|
|
27653
|
+
}
|
|
27654
|
+
requireProvider() {
|
|
27655
|
+
if (!this.provider)
|
|
27656
|
+
throw new Error("Loop wallet is not connected");
|
|
27657
|
+
return this.provider;
|
|
27658
|
+
}
|
|
27659
|
+
async submitTransfer(params, commandId) {
|
|
27660
|
+
const provider = this.requireProvider();
|
|
27661
|
+
if (params.sender !== provider.party_id)
|
|
27662
|
+
throw new Error("Loop account does not match the sender");
|
|
27663
|
+
const instrument = params.token.originalAddress
|
|
27664
|
+
? parseCantonInstrument(params.token.originalAddress)
|
|
27665
|
+
: null;
|
|
27666
|
+
if (!instrument)
|
|
27667
|
+
throw new Error("Canton token originalAddress is required");
|
|
27668
|
+
if (!new BigNumber(params.amount).isFinite() ||
|
|
27669
|
+
!new BigNumber(params.amount).isPositive()) {
|
|
27670
|
+
throw new Error("Canton transfer amount must be positive");
|
|
27671
|
+
}
|
|
27672
|
+
const payload = await provider.connection.prepareTransfer(provider.getAuthToken(), {
|
|
27673
|
+
recipient: params.receiver,
|
|
27674
|
+
amount: params.amount,
|
|
27675
|
+
instrument: {
|
|
27676
|
+
instrument_admin: instrument.admin,
|
|
27677
|
+
instrument_id: instrument.id,
|
|
27678
|
+
},
|
|
27679
|
+
memo: params.memo,
|
|
27680
|
+
requested_at: new Date(params.nowMs - 60_000).toISOString(),
|
|
27681
|
+
execute_before: new Date(params.nowMs + 24 * 60 * 60_000).toISOString(),
|
|
27682
|
+
});
|
|
27683
|
+
if (this.provider !== provider)
|
|
27684
|
+
throw new Error("Loop account disconnected before submission");
|
|
27685
|
+
let response;
|
|
27686
|
+
let timer;
|
|
27687
|
+
try {
|
|
27688
|
+
const submission = { ...payload, commandId };
|
|
27689
|
+
const submitted = provider.submitAndWaitForTransaction(submission, {
|
|
27690
|
+
requestTimeout: REQUEST_TIMEOUT_MS,
|
|
27691
|
+
});
|
|
27692
|
+
response = await Promise.race([
|
|
27693
|
+
submitted,
|
|
27694
|
+
new Promise((_, reject) => {
|
|
27695
|
+
timer = setTimeout(() => reject(new Error("Loop submission timed out")), REQUEST_TIMEOUT_MS);
|
|
27696
|
+
}),
|
|
27697
|
+
]);
|
|
27698
|
+
}
|
|
27699
|
+
catch (error) {
|
|
27700
|
+
if (this.module && error instanceof this.module.RejectRequestError) {
|
|
27701
|
+
throw Object.assign(new Error("Loop transaction rejected"), {
|
|
27702
|
+
code: 4001,
|
|
27703
|
+
});
|
|
27704
|
+
}
|
|
27705
|
+
throw new LoopSubmissionUnknownError(error);
|
|
27706
|
+
}
|
|
27707
|
+
finally {
|
|
27708
|
+
clearTimeout(timer);
|
|
27709
|
+
}
|
|
27710
|
+
const update = parseLoopTransferUpdate(response, commandId);
|
|
27711
|
+
if (update?.status === "failed")
|
|
27712
|
+
throw new Error(update.message);
|
|
27713
|
+
if (!update)
|
|
27714
|
+
throw new LoopSubmissionUnknownError();
|
|
27715
|
+
return update;
|
|
27716
|
+
}
|
|
27717
|
+
}
|
|
27718
|
+
const loopCantonConnector = new LoopCantonConnector();
|
|
27719
|
+
|
|
27119
27720
|
/**
|
|
27120
27721
|
* Discovers Canton browser-extension wallets and maps each to a
|
|
27121
27722
|
* {@link CantonWallet}: CIP-103 announce-protocol wallets (on mount and on
|
|
@@ -27162,7 +27763,19 @@ function useCantonWallets() {
|
|
|
27162
27763
|
return;
|
|
27163
27764
|
const announced = (await discoverCantonProviders()).map(toAnnouncedWallet);
|
|
27164
27765
|
const injected = discoverInjectedCantonWallets().map(toInjectedWallet);
|
|
27165
|
-
|
|
27766
|
+
const loopWallet = {
|
|
27767
|
+
...formatCantonWallet({
|
|
27768
|
+
connectorId: "canton-loop",
|
|
27769
|
+
name: "Loop",
|
|
27770
|
+
icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/master/wallets/canton-loop.svg",
|
|
27771
|
+
connector: loopCantonConnector,
|
|
27772
|
+
}),
|
|
27773
|
+
links: { website: "https://cantonloop.com" },
|
|
27774
|
+
isInstalled: () => false,
|
|
27775
|
+
skipInstallCheck: true,
|
|
27776
|
+
isMobile: true,
|
|
27777
|
+
};
|
|
27778
|
+
setWallets(mergeInjectedCantonWallets(announced, [...injected, loopWallet]));
|
|
27166
27779
|
}, [toAnnouncedWallet, toInjectedWallet]);
|
|
27167
27780
|
useEffect(() => {
|
|
27168
27781
|
refresh();
|
|
@@ -27193,7 +27806,9 @@ function useCanton() {
|
|
|
27193
27806
|
// Try to reconnect silently first (existing session, no popup)
|
|
27194
27807
|
result = await wallet.connector.autoConnect();
|
|
27195
27808
|
}
|
|
27196
|
-
catch {
|
|
27809
|
+
catch (error) {
|
|
27810
|
+
if (error instanceof CantonConnectionCancelledError)
|
|
27811
|
+
throw error;
|
|
27197
27812
|
// Otherwise prompt the wallet to connect
|
|
27198
27813
|
result = await wallet.connector.connect();
|
|
27199
27814
|
}
|
|
@@ -27215,8 +27830,7 @@ function useCanton() {
|
|
|
27215
27830
|
wallet: recentCantonWallet,
|
|
27216
27831
|
address,
|
|
27217
27832
|
});
|
|
27218
|
-
|
|
27219
|
-
}, [wallets.map((w) => w.connectorId), recentCantonWalletId]);
|
|
27833
|
+
}, [wallets, recentCantonWalletId, setConnectedWallet]);
|
|
27220
27834
|
useEffect(() => {
|
|
27221
27835
|
const wallet = connectedCantonWallet.wallet;
|
|
27222
27836
|
if (!wallet) {
|
|
@@ -27473,7 +28087,7 @@ function useStellarWallets() {
|
|
|
27473
28087
|
try {
|
|
27474
28088
|
const { allowAllModules: initializeAllModules } = await import('@creit.tech/stellar-wallets-kit');
|
|
27475
28089
|
const { LedgerModule } = await import('@creit.tech/stellar-wallets-kit/modules/ledger.module.mjs');
|
|
27476
|
-
const { formatStellarWallet } = await import('./stellarService.client-
|
|
28090
|
+
const { formatStellarWallet } = await import('./stellarService.client-DAxgO3m3.js');
|
|
27477
28091
|
const modules = [...initializeAllModules(), new LedgerModule()];
|
|
27478
28092
|
const promises = modules.map(async (module) => {
|
|
27479
28093
|
const isAvailable = await module.isAvailable();
|
|
@@ -28052,6 +28666,11 @@ const useWallet = () => {
|
|
|
28052
28666
|
}
|
|
28053
28667
|
}
|
|
28054
28668
|
catch (error) {
|
|
28669
|
+
const connecting = useWalletStore.getState().connectingWalletState;
|
|
28670
|
+
if (connecting.wallet !== wallet ||
|
|
28671
|
+
connecting.status !== ConnectingWalletStatus.CONNECTING) {
|
|
28672
|
+
return false;
|
|
28673
|
+
}
|
|
28055
28674
|
console.error("Failed to connect wallet", error);
|
|
28056
28675
|
walletConnectionError(wallet, error);
|
|
28057
28676
|
return false;
|
|
@@ -28283,8 +28902,6 @@ const useMultiChainWallet = (chain) => {
|
|
|
28283
28902
|
chainType: chain.chainType,
|
|
28284
28903
|
}),
|
|
28285
28904
|
};
|
|
28286
|
-
case ChainType.DANAL:
|
|
28287
|
-
return {};
|
|
28288
28905
|
}
|
|
28289
28906
|
}, [
|
|
28290
28907
|
chain?.chainType,
|
|
@@ -29123,8 +29740,6 @@ async function createClient(chain) {
|
|
|
29123
29740
|
return new StellarRpcClient(chain.rpc);
|
|
29124
29741
|
case ChainType.CANTON:
|
|
29125
29742
|
return null;
|
|
29126
|
-
case ChainType.DANAL:
|
|
29127
|
-
return null;
|
|
29128
29743
|
}
|
|
29129
29744
|
}
|
|
29130
29745
|
|
|
@@ -30139,8 +30754,7 @@ const getAllStellarTokensBalance = async (userAddress, stellarTokens, stellarCha
|
|
|
30139
30754
|
};
|
|
30140
30755
|
/**
|
|
30141
30756
|
* Canton balances are private: they can only be read with a wallet-authorized
|
|
30142
|
-
*
|
|
30143
|
-
* party). So balance is fetchable only when a Canton wallet is connected, and
|
|
30757
|
+
* session. Balance is fetchable only when a Canton wallet is connected, and
|
|
30144
30758
|
* only for that wallet's party — there's no address-only read.
|
|
30145
30759
|
*/
|
|
30146
30760
|
const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
|
|
@@ -30149,30 +30763,14 @@ const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
|
|
|
30149
30763
|
const instrument = parseCantonInstrument(token.originalAddress);
|
|
30150
30764
|
if (!instrument)
|
|
30151
30765
|
return "0";
|
|
30152
|
-
const
|
|
30153
|
-
const activeContracts = await cantonConnector.ledgerApi({
|
|
30154
|
-
requestMethod: "post",
|
|
30155
|
-
resource: "/v2/state/active-contracts",
|
|
30156
|
-
body: buildHoldingsAcsRequestBody(userAddress, String(ledgerEnd.offset)),
|
|
30157
|
-
});
|
|
30158
|
-
const grouped = groupCantonHoldingBalances(activeContracts);
|
|
30766
|
+
const grouped = await cantonConnector.getBalances(userAddress);
|
|
30159
30767
|
return grouped[cantonInstrumentKey(instrument)] ?? "0";
|
|
30160
30768
|
};
|
|
30161
30769
|
/**
|
|
30162
|
-
* Fetch balances for all Canton tokens
|
|
30163
|
-
* private, so this requires a connected wallet's authorized `ledgerApi`.
|
|
30770
|
+
* Fetch balances for all Canton tokens through the connected wallet.
|
|
30164
30771
|
*/
|
|
30165
30772
|
async function getAllCantonTokensBalance({ connector, partyId, tokens, }) {
|
|
30166
|
-
const
|
|
30167
|
-
requestMethod: "get",
|
|
30168
|
-
resource: "/v2/state/ledger-end",
|
|
30169
|
-
});
|
|
30170
|
-
const activeContracts = await connector.ledgerApi({
|
|
30171
|
-
requestMethod: "post",
|
|
30172
|
-
resource: "/v2/state/active-contracts",
|
|
30173
|
-
body: buildHoldingsAcsRequestBody(partyId, String(ledgerEnd.offset)),
|
|
30174
|
-
});
|
|
30175
|
-
const grouped = groupCantonHoldingBalances(activeContracts);
|
|
30773
|
+
const grouped = await connector.getBalances(partyId);
|
|
30176
30774
|
return tokens.map((token) => {
|
|
30177
30775
|
const instrument = token.originalAddress
|
|
30178
30776
|
? parseCantonInstrument(token.originalAddress)
|
|
@@ -30391,7 +30989,7 @@ const useCantonBalance = ({ chain, token, userAddress, enabled = true, refreshIn
|
|
|
30391
30989
|
};
|
|
30392
30990
|
|
|
30393
30991
|
function useNativeTokenForChain(chain) {
|
|
30394
|
-
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens,
|
|
30992
|
+
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens();
|
|
30395
30993
|
const getTokensForChainType = () => {
|
|
30396
30994
|
if (!chain?.chainType)
|
|
30397
30995
|
return [];
|
|
@@ -30412,8 +31010,6 @@ function useNativeTokenForChain(chain) {
|
|
|
30412
31010
|
return stellarTokens;
|
|
30413
31011
|
case ChainType.CANTON:
|
|
30414
31012
|
return cantonTokens;
|
|
30415
|
-
case ChainType.DANAL:
|
|
30416
|
-
return danalTokens;
|
|
30417
31013
|
}
|
|
30418
31014
|
};
|
|
30419
31015
|
const nativeTokenForChainType = useMemo(() => {
|
|
@@ -30674,9 +31270,6 @@ const useNativeBalance = (chain) => {
|
|
|
30674
31270
|
case ChainType.CANTON:
|
|
30675
31271
|
balance = nativeCantonBalance;
|
|
30676
31272
|
break;
|
|
30677
|
-
case ChainType.DANAL:
|
|
30678
|
-
balance = undefined;
|
|
30679
|
-
break;
|
|
30680
31273
|
}
|
|
30681
31274
|
const balanceFormatted = !!balance
|
|
30682
31275
|
? formatBNToReadable(balance.value, balance.decimals)
|
|
@@ -30716,8 +31309,6 @@ const useNativeBalance = (chain) => {
|
|
|
30716
31309
|
return isStellarLoading;
|
|
30717
31310
|
case ChainType.CANTON:
|
|
30718
31311
|
return isCantonLoading;
|
|
30719
|
-
case ChainType.DANAL:
|
|
30720
|
-
return false;
|
|
30721
31312
|
}
|
|
30722
31313
|
}, [
|
|
30723
31314
|
chain?.chainType,
|
|
@@ -30943,7 +31534,7 @@ function hederaWalletConnect(parameters) {
|
|
|
30943
31534
|
const optionalChains = config.chains.map((x) => x.id);
|
|
30944
31535
|
if (!optionalChains.length)
|
|
30945
31536
|
return;
|
|
30946
|
-
const { EthereumProvider } = await import('./index.es-
|
|
31537
|
+
const { EthereumProvider } = await import('./index.es-09IsADsA.js');
|
|
30947
31538
|
const rawProvider = await EthereumProvider.init({
|
|
30948
31539
|
...restParameters,
|
|
30949
31540
|
disableProviderPing: true,
|
|
@@ -31177,8 +31768,6 @@ const useSigner = ({ chain }) => {
|
|
|
31177
31768
|
return isStellarSignerReady;
|
|
31178
31769
|
case ChainType.CANTON:
|
|
31179
31770
|
return isCantonSignerReady;
|
|
31180
|
-
case ChainType.DANAL:
|
|
31181
|
-
return false;
|
|
31182
31771
|
}
|
|
31183
31772
|
}, [
|
|
31184
31773
|
chain?.chainType,
|
|
@@ -31631,12 +32220,8 @@ const useSendTransactionStore = create((set, get) => ({
|
|
|
31631
32220
|
const useDepositAddressStore = create((set) => ({
|
|
31632
32221
|
deposit: null,
|
|
31633
32222
|
selectedPaymentMethod: "connectedWallet",
|
|
31634
|
-
userDepositTxId: null,
|
|
31635
32223
|
setDeposit: (data) => {
|
|
31636
|
-
set({ deposit: data
|
|
31637
|
-
},
|
|
31638
|
-
setUserDepositTxId: (txId) => {
|
|
31639
|
-
set({ userDepositTxId: txId });
|
|
32224
|
+
set({ deposit: data });
|
|
31640
32225
|
},
|
|
31641
32226
|
setPaymentMethod: (method) => {
|
|
31642
32227
|
set({ selectedPaymentMethod: method });
|
|
@@ -31648,7 +32233,7 @@ function useDepositAddress(squidRoute) {
|
|
|
31648
32233
|
selectedPaymentMethod: state.selectedPaymentMethod,
|
|
31649
32234
|
depositAddress: state.deposit?.depositAddress,
|
|
31650
32235
|
}));
|
|
31651
|
-
const { setDeposit, setPaymentMethod, deposit
|
|
32236
|
+
const { setDeposit, setPaymentMethod, deposit } = useDepositAddressStore();
|
|
31652
32237
|
const { squid } = useSquidStore();
|
|
31653
32238
|
const { fromChain } = useSwap();
|
|
31654
32239
|
const isAvailableAsPaymentMethod = useMemo(() => {
|
|
@@ -31658,13 +32243,11 @@ function useDepositAddress(squidRoute) {
|
|
|
31658
32243
|
CHAIN_IDS.BITCOIN,
|
|
31659
32244
|
CHAIN_IDS.SOLANA,
|
|
31660
32245
|
CHAIN_IDS.CANTON,
|
|
31661
|
-
CHAIN_IDS.DANAL_TESTNET,
|
|
31662
32246
|
];
|
|
31663
32247
|
return chainsSupportingDepositAddress.includes(fromChain.chainId);
|
|
31664
32248
|
}, [fromChain?.chainId]);
|
|
31665
|
-
const routeProvidesDirectDepositAddress =
|
|
31666
|
-
isDepositAddressDirectTransferRoute(squidRoute.transactionRequest)
|
|
31667
|
-
(!!squidRoute && isDanalDepositRoute(squidRoute));
|
|
32249
|
+
const routeProvidesDirectDepositAddress = !!squidRoute?.transactionRequest &&
|
|
32250
|
+
isDepositAddressDirectTransferRoute(squidRoute.transactionRequest);
|
|
31668
32251
|
const swapWillGenerateDepositAddress = isChainflipDepositRoute(squidRoute) || routeProvidesDirectDepositAddress;
|
|
31669
32252
|
const paymentMethod = isAvailableAsPaymentMethod
|
|
31670
32253
|
? selectedPaymentMethod
|
|
@@ -31690,19 +32273,6 @@ function useDepositAddress(squidRoute) {
|
|
|
31690
32273
|
});
|
|
31691
32274
|
return;
|
|
31692
32275
|
}
|
|
31693
|
-
else if (isDanalDepositRoute(route)) {
|
|
31694
|
-
const transferBody = parseDanalTransferBody(route.transactionRequest.data);
|
|
31695
|
-
if (!transferBody) {
|
|
31696
|
-
throw new Error("Could not parse Danal transfer request body");
|
|
31697
|
-
}
|
|
31698
|
-
setDeposit({
|
|
31699
|
-
amount: transferBody.amount,
|
|
31700
|
-
depositAddress: route.transactionRequest.target,
|
|
31701
|
-
statusTrackingId: transferBody.memo,
|
|
31702
|
-
memo: transferBody.memo,
|
|
31703
|
-
});
|
|
31704
|
-
return;
|
|
31705
|
-
}
|
|
31706
32276
|
else if (isDepositAddressDirectTransferRoute(route.transactionRequest)) {
|
|
31707
32277
|
// Canton case
|
|
31708
32278
|
const orderHash = route.transactionRequest.data;
|
|
@@ -31725,8 +32295,6 @@ function useDepositAddress(squidRoute) {
|
|
|
31725
32295
|
depositAddress,
|
|
31726
32296
|
closeDepositChannel,
|
|
31727
32297
|
depositData: deposit,
|
|
31728
|
-
userDepositTxId,
|
|
31729
|
-
setUserDepositTxId,
|
|
31730
32298
|
};
|
|
31731
32299
|
}
|
|
31732
32300
|
|
|
@@ -31754,7 +32322,7 @@ const useUrlSwapParams = () => {
|
|
|
31754
32322
|
|
|
31755
32323
|
const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, queryOptions, }) => {
|
|
31756
32324
|
const { evmChains, cosmosChains, suiChains, xrplChains, stellarChains } = useSquidChains(direction);
|
|
31757
|
-
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens,
|
|
32325
|
+
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens(direction);
|
|
31758
32326
|
const { keplrTypeWallet } = useCosmosContext();
|
|
31759
32327
|
const cantonConnector = useWalletStore((store) => store.connectedWalletsByChainType[ChainType.CANTON]?.wallet?.connector);
|
|
31760
32328
|
const placeholderData = useMemo(() => {
|
|
@@ -31767,7 +32335,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
|
|
|
31767
32335
|
[ChainType.XRPL]: xrplTokens.map((t) => ({ ...t, balance: "0" })),
|
|
31768
32336
|
[ChainType.STELLAR]: stellarTokens.map((t) => ({ ...t, balance: "0" })),
|
|
31769
32337
|
[ChainType.CANTON]: cantonTokens.map((t) => ({ ...t, balance: "0" })),
|
|
31770
|
-
[ChainType.DANAL]: danalTokens.map((t) => ({ ...t, balance: "0" })),
|
|
31771
32338
|
};
|
|
31772
32339
|
if (!chainType) {
|
|
31773
32340
|
// Return all tokens with zero balance
|
|
@@ -31790,7 +32357,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
|
|
|
31790
32357
|
xrplTokens,
|
|
31791
32358
|
stellarTokens,
|
|
31792
32359
|
cantonTokens,
|
|
31793
|
-
danalTokens,
|
|
31794
32360
|
]);
|
|
31795
32361
|
const isQueryEnabled = useMemo(() => {
|
|
31796
32362
|
// Respect the queryOptions.enabled override if provided
|
|
@@ -31815,8 +32381,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
|
|
|
31815
32381
|
return stellarTokens.length > 0;
|
|
31816
32382
|
case ChainType.CANTON:
|
|
31817
32383
|
return cantonTokens.length > 0;
|
|
31818
|
-
case ChainType.DANAL:
|
|
31819
|
-
return danalTokens.length > 0;
|
|
31820
32384
|
}
|
|
31821
32385
|
}, [
|
|
31822
32386
|
chainType,
|
|
@@ -31830,7 +32394,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
|
|
|
31830
32394
|
xrplTokens.length,
|
|
31831
32395
|
stellarTokens.length,
|
|
31832
32396
|
cantonTokens.length,
|
|
31833
|
-
danalTokens.length,
|
|
31834
32397
|
]);
|
|
31835
32398
|
const query = useQuery(keys().allTokensBalance(address, chainType, direction), async () => {
|
|
31836
32399
|
// Return zero balances if no address
|
|
@@ -31955,12 +32518,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
|
|
|
31955
32518
|
direction,
|
|
31956
32519
|
queryOptions,
|
|
31957
32520
|
});
|
|
31958
|
-
const danalBalancesQuery = useAllTokensWithBalanceForChainType({
|
|
31959
|
-
chainType: ChainType.DANAL,
|
|
31960
|
-
address: connectedAddresses?.[ChainType.DANAL],
|
|
31961
|
-
direction,
|
|
31962
|
-
queryOptions,
|
|
31963
|
-
});
|
|
31964
32521
|
// Create a map of chain type to balance query results
|
|
31965
32522
|
const balanceQueries = useMemo(() => ({
|
|
31966
32523
|
[ChainType.EVM]: evmBalancesQuery,
|
|
@@ -31971,7 +32528,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
|
|
|
31971
32528
|
[ChainType.XRPL]: xrplBalancesQuery,
|
|
31972
32529
|
[ChainType.STELLAR]: stellarBalancesQuery,
|
|
31973
32530
|
[ChainType.CANTON]: cantonBalancesQuery,
|
|
31974
|
-
[ChainType.DANAL]: danalBalancesQuery,
|
|
31975
32531
|
}), [
|
|
31976
32532
|
evmBalancesQuery,
|
|
31977
32533
|
cosmosBalancesQuery,
|
|
@@ -31981,7 +32537,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
|
|
|
31981
32537
|
xrplBalancesQuery,
|
|
31982
32538
|
stellarBalancesQuery,
|
|
31983
32539
|
cantonBalancesQuery,
|
|
31984
|
-
danalBalancesQuery,
|
|
31985
32540
|
]);
|
|
31986
32541
|
// Combine all tokens from different chains
|
|
31987
32542
|
const allTokens = useMemo(() => Object.values(balanceQueries).flatMap((query) => query.data?.tokens ?? []), [balanceQueries]);
|
|
@@ -32089,8 +32644,6 @@ const useMultiChainBalance = ({ chain, token, userAddress, enabled = true, }) =>
|
|
|
32089
32644
|
return stellarBalance;
|
|
32090
32645
|
case ChainType.CANTON:
|
|
32091
32646
|
return cantonBalance;
|
|
32092
|
-
case ChainType.DANAL:
|
|
32093
|
-
return "0";
|
|
32094
32647
|
}
|
|
32095
32648
|
}, [
|
|
32096
32649
|
chain?.chainType,
|
|
@@ -33200,9 +33753,8 @@ function useSendTransaction({ to, amount, token, chain, }) {
|
|
|
33200
33753
|
chain,
|
|
33201
33754
|
});
|
|
33202
33755
|
break;
|
|
33203
|
-
case ChainType.CANTON:
|
|
33204
|
-
|
|
33205
|
-
throw new Error(`Send not implemented for chain type ${chain.chainType}`);
|
|
33756
|
+
case ChainType.CANTON: {
|
|
33757
|
+
throw new Error("Not implemented");
|
|
33206
33758
|
}
|
|
33207
33759
|
}
|
|
33208
33760
|
return {
|
|
@@ -33408,8 +33960,7 @@ async function getSendTransactionStatus({ chain, txHash, }) {
|
|
|
33408
33960
|
chain,
|
|
33409
33961
|
});
|
|
33410
33962
|
case ChainType.CANTON:
|
|
33411
|
-
|
|
33412
|
-
throw new Error(`Send not implemented for chain type ${chain.chainType}`);
|
|
33963
|
+
throw new Error("Not implemented");
|
|
33413
33964
|
}
|
|
33414
33965
|
}
|
|
33415
33966
|
|
|
@@ -37030,342 +37581,6 @@ coin.DecProto = {
|
|
|
37030
37581
|
|
|
37031
37582
|
} (tx));
|
|
37032
37583
|
|
|
37033
|
-
const vParty = (party) => ({ party });
|
|
37034
|
-
const vText = (text) => ({ text });
|
|
37035
|
-
const vNumeric = (numeric) => ({ numeric });
|
|
37036
|
-
const vContractId = (contractId) => ({
|
|
37037
|
-
contractId,
|
|
37038
|
-
});
|
|
37039
|
-
const vList = (elements) => ({
|
|
37040
|
-
list: { elements },
|
|
37041
|
-
});
|
|
37042
|
-
const vRecord = (fields) => ({
|
|
37043
|
-
record: { fields: fields.map(([label, value]) => ({ label, value })) },
|
|
37044
|
-
});
|
|
37045
|
-
const vTextMap = (entries) => ({ textMap: { entries } });
|
|
37046
|
-
const vTimestamp = (isoString) => ({
|
|
37047
|
-
timestamp: String(Math.floor(new Date(isoString).getTime() * 1000)),
|
|
37048
|
-
});
|
|
37049
|
-
function isRecord(value) {
|
|
37050
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37051
|
-
}
|
|
37052
|
-
function normalizeChoiceContextValue(value) {
|
|
37053
|
-
if (typeof value === "string")
|
|
37054
|
-
return value;
|
|
37055
|
-
if (!isRecord(value) || typeof value.tag !== "string")
|
|
37056
|
-
return null;
|
|
37057
|
-
switch (value.tag) {
|
|
37058
|
-
case "AV_Text":
|
|
37059
|
-
case "AV_ContractId":
|
|
37060
|
-
return typeof value.value === "string"
|
|
37061
|
-
? { tag: value.tag, value: value.value }
|
|
37062
|
-
: null;
|
|
37063
|
-
case "AV_Bool":
|
|
37064
|
-
return typeof value.value === "boolean"
|
|
37065
|
-
? { tag: value.tag, value: value.value }
|
|
37066
|
-
: null;
|
|
37067
|
-
case "AV_List":
|
|
37068
|
-
if (!Array.isArray(value.value))
|
|
37069
|
-
return null;
|
|
37070
|
-
return {
|
|
37071
|
-
tag: value.tag,
|
|
37072
|
-
value: value.value.flatMap((entry) => {
|
|
37073
|
-
const normalized = normalizeChoiceContextValue(entry);
|
|
37074
|
-
return normalized ? [normalized] : [];
|
|
37075
|
-
}),
|
|
37076
|
-
};
|
|
37077
|
-
default: {
|
|
37078
|
-
const inner = value.value;
|
|
37079
|
-
if (typeof inner === "string" ||
|
|
37080
|
-
typeof inner === "number" ||
|
|
37081
|
-
typeof inner === "boolean" ||
|
|
37082
|
-
inner === null) {
|
|
37083
|
-
return { tag: value.tag, value: inner };
|
|
37084
|
-
}
|
|
37085
|
-
if (Array.isArray(inner)) {
|
|
37086
|
-
return {
|
|
37087
|
-
tag: value.tag,
|
|
37088
|
-
value: inner.flatMap((entry) => {
|
|
37089
|
-
const normalized = normalizeChoiceContextValue(entry);
|
|
37090
|
-
return normalized ? [normalized] : [];
|
|
37091
|
-
}),
|
|
37092
|
-
};
|
|
37093
|
-
}
|
|
37094
|
-
if (!isRecord(inner))
|
|
37095
|
-
return null;
|
|
37096
|
-
const normalizedObject = {};
|
|
37097
|
-
for (const [key, entry] of Object.entries(inner)) {
|
|
37098
|
-
if (typeof entry !== "string" &&
|
|
37099
|
-
typeof entry !== "number" &&
|
|
37100
|
-
typeof entry !== "boolean" &&
|
|
37101
|
-
entry !== null) {
|
|
37102
|
-
return null;
|
|
37103
|
-
}
|
|
37104
|
-
normalizedObject[key] = entry;
|
|
37105
|
-
}
|
|
37106
|
-
return { tag: value.tag, value: normalizedObject };
|
|
37107
|
-
}
|
|
37108
|
-
}
|
|
37109
|
-
}
|
|
37110
|
-
function stringifyChoiceContextValue(value) {
|
|
37111
|
-
return typeof value === "string" ? value : String(value);
|
|
37112
|
-
}
|
|
37113
|
-
function choiceContextValueToAnyValue(value) {
|
|
37114
|
-
if (typeof value === "string") {
|
|
37115
|
-
return {
|
|
37116
|
-
variant: { constructor: "AV_Text", value: vText(value) },
|
|
37117
|
-
};
|
|
37118
|
-
}
|
|
37119
|
-
switch (value.tag) {
|
|
37120
|
-
case "AV_ContractId":
|
|
37121
|
-
if (typeof value.value !== "string") {
|
|
37122
|
-
throw new TypeError("AV_ContractId values must be strings");
|
|
37123
|
-
}
|
|
37124
|
-
return {
|
|
37125
|
-
variant: { constructor: value.tag, value: vContractId(value.value) },
|
|
37126
|
-
};
|
|
37127
|
-
case "AV_Bool":
|
|
37128
|
-
if (typeof value.value !== "boolean") {
|
|
37129
|
-
throw new TypeError("AV_Bool values must be booleans");
|
|
37130
|
-
}
|
|
37131
|
-
return {
|
|
37132
|
-
variant: { constructor: value.tag, value: { bool: value.value } },
|
|
37133
|
-
};
|
|
37134
|
-
case "AV_List":
|
|
37135
|
-
if (!Array.isArray(value.value)) {
|
|
37136
|
-
throw new TypeError("AV_List values must be arrays");
|
|
37137
|
-
}
|
|
37138
|
-
return {
|
|
37139
|
-
variant: {
|
|
37140
|
-
constructor: value.tag,
|
|
37141
|
-
value: vList(value.value.map(choiceContextValueToAnyValue)),
|
|
37142
|
-
},
|
|
37143
|
-
};
|
|
37144
|
-
default:
|
|
37145
|
-
return {
|
|
37146
|
-
variant: {
|
|
37147
|
-
constructor: value.tag,
|
|
37148
|
-
value: vText(stringifyChoiceContextValue(value.value)),
|
|
37149
|
-
},
|
|
37150
|
-
};
|
|
37151
|
-
}
|
|
37152
|
-
}
|
|
37153
|
-
|
|
37154
|
-
/** Metadata key used by Splice token-standard transfers to carry our Squid orderhash memo. */
|
|
37155
|
-
const CANTON_MEMO_KEY = "splice.lfdecentralizedtrust.org/reason";
|
|
37156
|
-
/** Interface id for the transfer factory contract returned by the token registry. */
|
|
37157
|
-
const TRANSFER_FACTORY_INTERFACE_ID = "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory";
|
|
37158
|
-
/** Choice exercised on the transfer factory to create the token-standard transfer. */
|
|
37159
|
-
const TRANSFER_FACTORY_CHOICE = "TransferFactory_Transfer";
|
|
37160
|
-
/** CIP-056 (non-Amulet) tokens expose a registrar parameterized by admin party. */
|
|
37161
|
-
const CIP056_REGISTRAR_BASE = "https://api.utilities.digitalasset.com/api/token-standard/v0/registrars";
|
|
37162
|
-
const TRANSFER_REQUESTED_AT_SKEW_MS = 60_000;
|
|
37163
|
-
const TRANSFER_EXECUTE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
37164
|
-
function resolveCantonRegistryUrl(instrument) {
|
|
37165
|
-
if (instrument.id === CANTON_AMULET_INSTRUMENT_ID) {
|
|
37166
|
-
return CANTON_AMULET_REGISTRY_URL;
|
|
37167
|
-
}
|
|
37168
|
-
return `${CIP056_REGISTRAR_BASE}/${encodeURIComponent(instrument.admin)}`;
|
|
37169
|
-
}
|
|
37170
|
-
/**
|
|
37171
|
-
* Build the plain JSON arguments expected by the registry transfer-factory
|
|
37172
|
-
* endpoint before it adds choice context and disclosed contracts.
|
|
37173
|
-
*/
|
|
37174
|
-
function buildTransferChoiceArgs({ sender, receiver, amount, instrument, inputHoldingCids, memo, nowMs, }) {
|
|
37175
|
-
return {
|
|
37176
|
-
expectedAdmin: instrument.admin,
|
|
37177
|
-
transfer: {
|
|
37178
|
-
sender,
|
|
37179
|
-
receiver,
|
|
37180
|
-
amount,
|
|
37181
|
-
instrumentId: { admin: instrument.admin, id: instrument.id },
|
|
37182
|
-
requestedAt: new Date(nowMs - TRANSFER_REQUESTED_AT_SKEW_MS).toISOString(),
|
|
37183
|
-
executeBefore: new Date(nowMs + TRANSFER_EXECUTE_WINDOW_MS).toISOString(),
|
|
37184
|
-
inputHoldingCids,
|
|
37185
|
-
meta: { values: { [CANTON_MEMO_KEY]: memo } },
|
|
37186
|
-
},
|
|
37187
|
-
extraArgs: { context: { values: {} }, meta: { values: {} } },
|
|
37188
|
-
};
|
|
37189
|
-
}
|
|
37190
|
-
/**
|
|
37191
|
-
* Canton's Holding contracts follow Bitcoin-style UTXOs model.
|
|
37192
|
-
*
|
|
37193
|
-
* Pick Holding contract ids covering `amount` for the instrument: prefer a
|
|
37194
|
-
* single exact-amount holding, else accumulate largest-first.
|
|
37195
|
-
*/
|
|
37196
|
-
function selectInputHoldingCids(holdings, instrument, amount) {
|
|
37197
|
-
const spendableHoldings = holdings.filter((h) => !h.locked &&
|
|
37198
|
-
h.instrument.admin === instrument.admin &&
|
|
37199
|
-
h.instrument.id === instrument.id);
|
|
37200
|
-
const target = new BigNumber(amount);
|
|
37201
|
-
const exact = spendableHoldings.find((h) => new BigNumber(h.amount).eq(target));
|
|
37202
|
-
if (exact)
|
|
37203
|
-
return [exact.contractId];
|
|
37204
|
-
const sorted = [...spendableHoldings].sort((a, b) => new BigNumber(b.amount).comparedTo(new BigNumber(a.amount)));
|
|
37205
|
-
const selected = [];
|
|
37206
|
-
let sum = new BigNumber(0);
|
|
37207
|
-
for (const holding of sorted) {
|
|
37208
|
-
selected.push(holding.contractId);
|
|
37209
|
-
sum = sum.plus(holding.amount);
|
|
37210
|
-
if (sum.gte(target))
|
|
37211
|
-
break;
|
|
37212
|
-
}
|
|
37213
|
-
if (sum.lt(target)) {
|
|
37214
|
-
throw new Error("Insufficient Canton holdings to cover transfer amount");
|
|
37215
|
-
}
|
|
37216
|
-
return selected;
|
|
37217
|
-
}
|
|
37218
|
-
async function fetchInputHoldingCids(connector, party, instrument, amount) {
|
|
37219
|
-
const ledgerEnd = await connector.ledgerApi({
|
|
37220
|
-
requestMethod: "get",
|
|
37221
|
-
resource: "/v2/state/ledger-end",
|
|
37222
|
-
});
|
|
37223
|
-
const activeContracts = await connector.ledgerApi({
|
|
37224
|
-
requestMethod: "post",
|
|
37225
|
-
resource: "/v2/state/active-contracts",
|
|
37226
|
-
body: buildHoldingsAcsRequestBody(party, String(ledgerEnd.offset)),
|
|
37227
|
-
});
|
|
37228
|
-
return selectInputHoldingCids(parseCantonHoldings(activeContracts), instrument, amount);
|
|
37229
|
-
}
|
|
37230
|
-
/**
|
|
37231
|
-
* Ask the token registry for the transfer factory contract and extra context
|
|
37232
|
-
* required to exercise its transfer choice.
|
|
37233
|
-
*/
|
|
37234
|
-
async function fetchTransferFactory(registryUrl, choiceArgs) {
|
|
37235
|
-
const response = await fetch(`${registryUrl}/registry/transfer-instruction/v1/transfer-factory`, {
|
|
37236
|
-
method: "POST",
|
|
37237
|
-
headers: { "Content-Type": "application/json" },
|
|
37238
|
-
body: JSON.stringify({
|
|
37239
|
-
choiceArguments: choiceArgs,
|
|
37240
|
-
excludeDebugFields: true,
|
|
37241
|
-
}),
|
|
37242
|
-
});
|
|
37243
|
-
if (!response.ok) {
|
|
37244
|
-
throw new Error(`Canton transfer-factory request failed (${response.status})`);
|
|
37245
|
-
}
|
|
37246
|
-
return response.json();
|
|
37247
|
-
}
|
|
37248
|
-
/**
|
|
37249
|
-
* Convert transfer choice args into strict Daml Value JSON for the wallet's
|
|
37250
|
-
* prepareExecute ExerciseCommand.
|
|
37251
|
-
*/
|
|
37252
|
-
function buildPrepareChoiceArgument(choiceArgs) {
|
|
37253
|
-
const contextValues = Object.entries(choiceArgs.extraArgs.context?.values ?? {})
|
|
37254
|
-
.map(([key, value]) => {
|
|
37255
|
-
const normalized = normalizeChoiceContextValue(value);
|
|
37256
|
-
return normalized
|
|
37257
|
-
? { key, value: choiceContextValueToAnyValue(normalized) }
|
|
37258
|
-
: undefined;
|
|
37259
|
-
})
|
|
37260
|
-
.filter((entry) => Boolean(entry));
|
|
37261
|
-
return vRecord([
|
|
37262
|
-
["expectedAdmin", vParty(choiceArgs.expectedAdmin)],
|
|
37263
|
-
[
|
|
37264
|
-
"transfer",
|
|
37265
|
-
vRecord([
|
|
37266
|
-
["sender", vParty(choiceArgs.transfer.sender)],
|
|
37267
|
-
["receiver", vParty(choiceArgs.transfer.receiver)],
|
|
37268
|
-
["amount", vNumeric(choiceArgs.transfer.amount)],
|
|
37269
|
-
[
|
|
37270
|
-
"instrumentId",
|
|
37271
|
-
vRecord([
|
|
37272
|
-
["admin", vParty(choiceArgs.transfer.instrumentId.admin)],
|
|
37273
|
-
["id", vText(choiceArgs.transfer.instrumentId.id)],
|
|
37274
|
-
]),
|
|
37275
|
-
],
|
|
37276
|
-
["requestedAt", vTimestamp(choiceArgs.transfer.requestedAt)],
|
|
37277
|
-
["executeBefore", vTimestamp(choiceArgs.transfer.executeBefore)],
|
|
37278
|
-
[
|
|
37279
|
-
"inputHoldingCids",
|
|
37280
|
-
vList(choiceArgs.transfer.inputHoldingCids.map(vContractId)),
|
|
37281
|
-
],
|
|
37282
|
-
[
|
|
37283
|
-
"meta",
|
|
37284
|
-
vRecord([
|
|
37285
|
-
[
|
|
37286
|
-
"values",
|
|
37287
|
-
vTextMap(Object.entries(choiceArgs.transfer.meta.values).map(([key, value]) => ({ key, value: vText(value) }))),
|
|
37288
|
-
],
|
|
37289
|
-
]),
|
|
37290
|
-
],
|
|
37291
|
-
]),
|
|
37292
|
-
],
|
|
37293
|
-
[
|
|
37294
|
-
"extraArgs",
|
|
37295
|
-
vRecord([
|
|
37296
|
-
["context", vRecord([["values", vTextMap(contextValues)]])],
|
|
37297
|
-
["meta", vRecord([["values", vTextMap([])]])],
|
|
37298
|
-
]),
|
|
37299
|
-
],
|
|
37300
|
-
]);
|
|
37301
|
-
}
|
|
37302
|
-
/**
|
|
37303
|
-
* Convert a registry disclosed contract into the wallet submission shape,
|
|
37304
|
-
* dropping debug fields and entries without a usable createdEventBlob.
|
|
37305
|
-
*/
|
|
37306
|
-
function toDisclosedContract(raw) {
|
|
37307
|
-
if (typeof raw.createdEventBlob !== "string")
|
|
37308
|
-
return null;
|
|
37309
|
-
return {
|
|
37310
|
-
createdEventBlob: raw.createdEventBlob,
|
|
37311
|
-
contractId: typeof raw.contractId === "string" ? raw.contractId : undefined,
|
|
37312
|
-
synchronizerId: typeof raw.synchronizerId === "string" ? raw.synchronizerId : undefined,
|
|
37313
|
-
};
|
|
37314
|
-
}
|
|
37315
|
-
/**
|
|
37316
|
-
* Build a Splice token-standard transfer of `amount` of `token` from `sender`
|
|
37317
|
-
* to `receiver`, carrying `memo`, ready to hand to a wallet's prepareExecute.
|
|
37318
|
-
* Returns the full prepareExecute params (commandId + commands + actAs +
|
|
37319
|
-
* disclosedContracts).
|
|
37320
|
-
*/
|
|
37321
|
-
async function buildCantonTransfer(connector, { sender, receiver, amount, token, memo, nowMs, }) {
|
|
37322
|
-
if (!token.originalAddress) {
|
|
37323
|
-
throw new Error("Canton token originalAddress is required");
|
|
37324
|
-
}
|
|
37325
|
-
const instrument = parseCantonInstrument(token.originalAddress);
|
|
37326
|
-
if (!instrument) {
|
|
37327
|
-
throw new Error(`Invalid Canton token address: ${token.originalAddress}`);
|
|
37328
|
-
}
|
|
37329
|
-
const registryUrl = resolveCantonRegistryUrl(instrument);
|
|
37330
|
-
const inputHoldingCids = await fetchInputHoldingCids(connector, sender, instrument, amount);
|
|
37331
|
-
const choiceArgs = buildTransferChoiceArgs({
|
|
37332
|
-
sender,
|
|
37333
|
-
receiver,
|
|
37334
|
-
amount,
|
|
37335
|
-
instrument,
|
|
37336
|
-
inputHoldingCids,
|
|
37337
|
-
memo,
|
|
37338
|
-
nowMs,
|
|
37339
|
-
});
|
|
37340
|
-
const factory = await fetchTransferFactory(registryUrl, choiceArgs);
|
|
37341
|
-
const choiceContextData = factory.choiceContext.choiceContextData;
|
|
37342
|
-
if (!choiceContextData) {
|
|
37343
|
-
throw new Error("Transfer factory choice context is missing");
|
|
37344
|
-
}
|
|
37345
|
-
choiceArgs.extraArgs.context = choiceContextData;
|
|
37346
|
-
const disclosedContracts = (factory.choiceContext?.disclosedContracts ?? []).flatMap((raw) => {
|
|
37347
|
-
const disclosedContract = toDisclosedContract(raw);
|
|
37348
|
-
return disclosedContract ? [disclosedContract] : [];
|
|
37349
|
-
});
|
|
37350
|
-
// Command ID: client-supplied identifier used to deduplicate submissions
|
|
37351
|
-
const commandId = `canton-${memo}`;
|
|
37352
|
-
return {
|
|
37353
|
-
commandId,
|
|
37354
|
-
commands: [
|
|
37355
|
-
{
|
|
37356
|
-
ExerciseCommand: {
|
|
37357
|
-
templateId: TRANSFER_FACTORY_INTERFACE_ID,
|
|
37358
|
-
contractId: factory.factoryId,
|
|
37359
|
-
choice: TRANSFER_FACTORY_CHOICE,
|
|
37360
|
-
choiceArgument: buildPrepareChoiceArgument(choiceArgs),
|
|
37361
|
-
},
|
|
37362
|
-
},
|
|
37363
|
-
],
|
|
37364
|
-
actAs: [sender],
|
|
37365
|
-
disclosedContracts,
|
|
37366
|
-
};
|
|
37367
|
-
}
|
|
37368
|
-
|
|
37369
37584
|
const useExecuteTransaction = (squidRoute) => {
|
|
37370
37585
|
const { fromChain, toChain, fromToken, toToken, isSameChain } = useSwap();
|
|
37371
37586
|
// A route completes on source tx only if it's same-chain AND
|
|
@@ -37980,7 +38195,7 @@ const useExecuteTransaction = (squidRoute) => {
|
|
|
37980
38195
|
}
|
|
37981
38196
|
const amount = formatBNToReadable(fromAmount, token.decimals);
|
|
37982
38197
|
dispatchSignatureRequestEvent(route);
|
|
37983
|
-
const
|
|
38198
|
+
const result = await cantonSigner.transfer({
|
|
37984
38199
|
sender: sourceUserAddress,
|
|
37985
38200
|
receiver,
|
|
37986
38201
|
amount,
|
|
@@ -37988,15 +38203,14 @@ const useExecuteTransaction = (squidRoute) => {
|
|
|
37988
38203
|
memo: orderHash,
|
|
37989
38204
|
nowMs: Date.now(),
|
|
37990
38205
|
});
|
|
37991
|
-
const
|
|
37992
|
-
const txHash = result.tx?.payload?.updateId ?? "";
|
|
38206
|
+
const txHash = result.updateId;
|
|
37993
38207
|
if (txHash) {
|
|
37994
38208
|
resetQueriesAfterTxSigned();
|
|
37995
38209
|
}
|
|
37996
38210
|
WidgetEvents.getInstance().dispatchSwapExecuteCall(route, txHash);
|
|
37997
38211
|
const txParams = setTransactionState({
|
|
37998
38212
|
route,
|
|
37999
|
-
txHash
|
|
38213
|
+
txHash,
|
|
38000
38214
|
userAddress: sourceUserAddress,
|
|
38001
38215
|
status: TransactionStatus.INITIAL_LOADING,
|
|
38002
38216
|
sourceStatus: TransactionStatus.ONGOING,
|
|
@@ -38279,7 +38493,7 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
|
|
|
38279
38493
|
//
|
|
38280
38494
|
// If deposit address is not selected, we use the connected address as the source address instead
|
|
38281
38495
|
const sourceUserAddress = isDepositAddressActive
|
|
38282
|
-
? depositRefundAddress ?? sourceConnectedAddress
|
|
38496
|
+
? (depositRefundAddress ?? sourceConnectedAddress)
|
|
38283
38497
|
: sourceConnectedAddress;
|
|
38284
38498
|
const squidRouteQueryKeys = 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), [
|
|
38285
38499
|
fromChain?.chainId,
|
|
@@ -38571,6 +38785,10 @@ const useAutoConnect = () => {
|
|
|
38571
38785
|
const { chains } = useSquidChains();
|
|
38572
38786
|
const { isInitialized } = useConfigStore();
|
|
38573
38787
|
const hasAttemptedReconnect = useRef(false);
|
|
38788
|
+
const hasAttemptedCantonReconnect = useRef(false);
|
|
38789
|
+
const { autoConnectCanton, wallets: cantonWallets } = useCantonContext();
|
|
38790
|
+
const recentCantonWalletId = useWalletStore((state) => state.recentConnectorIds[ChainType.CANTON]);
|
|
38791
|
+
const hasRecentCantonWallet = cantonWallets.some((wallet) => wallet.connectorId === recentCantonWalletId);
|
|
38574
38792
|
const { autoConnectEvm } = useEvmContext();
|
|
38575
38793
|
const { autoConnectSolana } = useSolanaContext();
|
|
38576
38794
|
const { autoConnectSui } = useSuiContext();
|
|
@@ -38638,6 +38856,15 @@ const useAutoConnect = () => {
|
|
|
38638
38856
|
useEffect(() => {
|
|
38639
38857
|
autoConnect();
|
|
38640
38858
|
}, [autoConnect]);
|
|
38859
|
+
useEffect(() => {
|
|
38860
|
+
if (!isInitialized ||
|
|
38861
|
+
chains.length === 0 ||
|
|
38862
|
+
!hasRecentCantonWallet ||
|
|
38863
|
+
hasAttemptedCantonReconnect.current)
|
|
38864
|
+
return;
|
|
38865
|
+
hasAttemptedCantonReconnect.current = true;
|
|
38866
|
+
autoConnectCanton().catch(() => undefined);
|
|
38867
|
+
}, [autoConnectCanton, hasRecentCantonWallet, isInitialized, chains.length]);
|
|
38641
38868
|
return { autoConnect };
|
|
38642
38869
|
};
|
|
38643
38870
|
|
|
@@ -38946,5 +39173,5 @@ const SquidProvider = ({ children, config, placeholder, }) => {
|
|
|
38946
39173
|
React.createElement(CosmosProvider, null, children))))))))))) : (placeholder);
|
|
38947
39174
|
};
|
|
38948
39175
|
|
|
38949
|
-
export { useWalletStore as $, AxelarStatusResponseType as A, useHederaAccountActivation as B, CHAIN_IDS as C, DEFAULT_LOCALE as D, useHederaTokenAssociations as E, useKeyboardNavigation as F, useSquidQueryClient as G, HistoryTxType as H, useSquid as I, useStellarAccountActivation as J, useStellarTrustLine as K, useAddressBookStore as L, useAssetsColorsStore as M, Nr as N, useFavoriteTokensStore as O, useHistoryStore as P, QueryKeys as Q, useSendTransactionStore as R, SquidStatusErrorType as S, TransactionErrorType as T, useConfigStore as U, useSquidStore as V, Wo as W, XamanXrplNetwork as X, useSwapRoutePersistStore as Y, useTransactionStore as Z, ConnectingWalletStatus as _, WindowWalletFlag as a, useAvailableQuotes as a$, useDepositAddress as a0, useSwap as a1, buildUrlSearchParamsFromSwapEvent as a2, parseInitialAssetsFromUrl as a3, useUrlSwapParams as a4, useAllConnectedWalletBalances as a5, useAllTokensWithBalanceForChainType as a6, useCosmosBalance as a7, useEvmBalance as a8, useMultiChainBalance as a9, useRouteWarnings as aA, useSendTransactionStatus as aB, useSwapTransactionStatus as aC, useAvatar as aD, useHistory as aE, useDebouncedValue as aF, useAddToken as aG, useAutoConnect as aH, useEnsDataForAddress as aI, useEnsSearch as aJ, useGnosisContext as aK, useIsSameAddressAndGnosisContext as aL, useIntegratorContext as aM, useMultiChainWallet as aN, useSigner as aO, useWallet as aP, useWallets as aQ, useXrplTrustLine as aR, TX_STATUS_CONSTANTS as aS, FINAL_TRANSACTION_STATUSES as aT, useGetFiatQuote as aU, useGetOnRampConfig as aV, useExecuteFiatQuote as aW, useFiatOnRampTxStatus as aX, useFiatTransactions as aY, useCurrencyDetails as aZ, useCountryDetails as a_, useMultipleTokenPrices as aa, useBitcoinNativeBalance as ab, useCosmosNativeBalance as ac, useEvmNativeBalance as ad, useNativeBalance as ae, useSolanaNativeBalance as af, useStellarNativeBalance as ag, useSuiNativeBalance as ah, useXrplNativeBalance as ai, useNativeTokenForChain as aj, useSingleTokenPrice as ak, useSourceChainGasToken as al, useSquidTokens as am, useHistoricalData as an, useTokensData as ao, useEstimateSendTransaction as ap, useSendTransaction as aq, useSendTransactionGas as ar, useAllTransactionsStatus as as, useApproval as at, useDepositTransactionStatus as au, useEstimate as av, useEstimatePriceImpact as aw, useExecuteTransaction as ax, useGetRoute as ay, useGetRouteWrapper as az, DEFAULT_ROUTE_REFETCH_INTERVAL as b,
|
|
38950
|
-
//# sourceMappingURL=index-
|
|
39176
|
+
export { useWalletStore as $, AxelarStatusResponseType as A, useHederaAccountActivation as B, CHAIN_IDS as C, DEFAULT_LOCALE as D, useHederaTokenAssociations as E, useKeyboardNavigation as F, useSquidQueryClient as G, HistoryTxType as H, useSquid as I, useStellarAccountActivation as J, useStellarTrustLine as K, useAddressBookStore as L, useAssetsColorsStore as M, Nr as N, useFavoriteTokensStore as O, useHistoryStore as P, QueryKeys as Q, useSendTransactionStore as R, SquidStatusErrorType as S, TransactionErrorType as T, useConfigStore as U, useSquidStore as V, Wo as W, XamanXrplNetwork as X, useSwapRoutePersistStore as Y, useTransactionStore as Z, ConnectingWalletStatus as _, WindowWalletFlag as a, useAvailableQuotes as a$, useDepositAddress as a0, useSwap as a1, buildUrlSearchParamsFromSwapEvent as a2, parseInitialAssetsFromUrl as a3, useUrlSwapParams as a4, useAllConnectedWalletBalances as a5, useAllTokensWithBalanceForChainType as a6, useCosmosBalance as a7, useEvmBalance as a8, useMultiChainBalance as a9, useRouteWarnings as aA, useSendTransactionStatus as aB, useSwapTransactionStatus as aC, useAvatar as aD, useHistory as aE, useDebouncedValue as aF, useAddToken as aG, useAutoConnect as aH, useEnsDataForAddress as aI, useEnsSearch as aJ, useGnosisContext as aK, useIsSameAddressAndGnosisContext as aL, useIntegratorContext as aM, useMultiChainWallet as aN, useSigner as aO, useWallet as aP, useWallets as aQ, useXrplTrustLine as aR, TX_STATUS_CONSTANTS as aS, FINAL_TRANSACTION_STATUSES as aT, useGetFiatQuote as aU, useGetOnRampConfig as aV, useExecuteFiatQuote as aW, useFiatOnRampTxStatus as aX, useFiatTransactions as aY, useCurrencyDetails as aZ, useCountryDetails as a_, useMultipleTokenPrices as aa, useBitcoinNativeBalance as ab, useCosmosNativeBalance as ac, useEvmNativeBalance as ad, useNativeBalance as ae, useSolanaNativeBalance as af, useStellarNativeBalance as ag, useSuiNativeBalance as ah, useXrplNativeBalance as ai, useNativeTokenForChain as aj, useSingleTokenPrice as ak, useSourceChainGasToken as al, useSquidTokens as am, useHistoricalData as an, useTokensData as ao, useEstimateSendTransaction as ap, useSendTransaction as aq, useSendTransactionGas as ar, useAllTransactionsStatus as as, useApproval as at, useDepositTransactionStatus as au, useEstimate as av, useEstimatePriceImpact as aw, useExecuteTransaction as ax, useGetRoute as ay, useGetRouteWrapper as az, DEFAULT_ROUTE_REFETCH_INTERVAL as b, addEthereumChain as b$, useRecommendedQuote as b0, useGetOnrampPaymentTypes as b1, useSuggestedFiatAmounts as b2, SquidProvider as b3, EnsService as b4, getXummClient as b5, isXamanXAppContext as b6, getQueryHeaders as b7, getStatusCode as b8, is404Error as b9, randomIntFromInterval as bA, getTokensForChain as bB, getFirstAvailableChainId as bC, fetchHighestBalanceToken as bD, getInitialOrDefaultTokenAddressForChain as bE, getInitialTokenAddressForChain as bF, filterTokensForDestination as bG, getInitialChainIdFromConfig as bH, getCosmosKey as bI, getKeysSettled as bJ, getAllKeysForSupportedCosmosChains as bK, isCosmosAddressValid as bL, getCosmosSigningClient as bM, getCosmosChainInfosObject as bN, connectCosmosWallet as bO, isFallbackAddressNeeded as bP, suggestChainOrThrow as bQ, normalizeError as bR, transactionErrorCode as bS, isUserRejectionError as bT, getTransactionError as bU, handleTransactionErrorEvents as bV, isSwapRouteError as bW, isStatusError as bX, createQuoteRequestParamsHash as bY, WidgetEvents as bZ, EvmNetworkNotSupportedErrorCode as b_, assetsBaseUrl as ba, shareSubgraphId as bb, sortTokensBySharedSubgraphIds as bc, getSupportedChainIdsForDirection as bd, filterChains as be, filterTokens as bf, getTokenImage as bg, getNewSwapParamsFromInput as bh, sortAllTokens as bi, findToken as bj, findNativeToken as bk, normalizeIbcAddress as bl, groupTokensBySymbol as bm, groupTokensByChainId as bn, filterViewableTokens as bo, getSecretNetworkBalances as bp, getTokenAssetsKey as bq, fetchAssetsColors as br, isSupportedChainType as bs, initializeSquidData as bt, isEmptyObject as bu, normalizeTokenSymbol as bv, areTokenSymbolsCompatible as bw, isEvmosChain as bx, resolveChainIdFromAsset as by, getConfigWithDefaults as bz, chainTypeToNativeTokenAddressMap as c, chainflipMultihopBridgeType as c$, parseEvmAddress as c0, formatEvmWallet as c1, filterWagmiConnector as c2, waitForReceiptWithRetry as c3, getUserCountry as c4, getCountryData as c5, getCurrencyData as c6, adaptiveRound as c7, getSuggestedAmountsForCurrency as c8, HederaExtensionHelper as c9, isStellarIssuedToken as cA, getStellarHorizonApiUrl as cB, isValidIssuedAsset as cC, isValidHorizonAsset as cD, formatTransactionHistoryDate as cE, getAxelarExplorerTxUrl as cF, getSourceExplorerTxUrl as cG, getMainExplorerUrl as cH, formatDistance as cI, formatSeconds as cJ, formatSwapTxStatusResponseForStorage as cK, simplifyRouteAction as cL, fetchSwapTransactionStatus as cM, compareTransactionIds as cN, isCoralBridgeAction as cO, isActionCompletedOnSourceTx as cP, sleep as cQ, isChainflipDepositRoute as cR, isChainflipBridgeTransaction as cS, isOnChainTxData as cT, isDepositAddressDirectTransferRoute as cU, getHistoryTransactionId as cV, getStepStatuses as cW, getHalfSuccessState as cX, getStepsInfos as cY, getSwapTxStatusRefetchInterval as cZ, getSendTxStatusRefetchInterval as c_, convertHederaAccountIdToEvmAddress as ca, convertEvmAddressToHederaAccountId as cb, scaleHbarToWei as cc, scaleWeiToHbar as cd, parseToBigInt as ce, roundNumericValue as cf, formatUnitsRounded as cg, formatTokenAmount as ch, formatUsdAmount as ci, trimExtraDecimals as cj, getNumericValue as ck, cleanAmount as cl, convertTokenAmountToUSD as cm, convertUSDToTokenAmount as cn, calculateTotal24hChange as co, getRouteExpiry as cp, searchTokens as cq, filterSolanaWallets as cr, isSolanaAddressValid as cs, executeSolanaSwap as ct, executeSolanaTransfer as cu, isStellarAddressValid as cv, getStellarNetwork as cw, stellarAddressToScVal as cx, getStellarTrustLineAsset as cy, isStellarToken as cz, definedInWindow as d, getBridgeType as d0, getTransactionStatus as d1, getTransactionEndStatus as d2, isHistoryTransactionPending as d3, isHistoryTransactionFailed as d4, isHistoryTransactionWarning as d5, isHistoryTransactionEnded as d6, formatHash as d7, isWalletAddressValid as d8, redirectToExtensionsStore as d9, populateWallets as da, buildFallbackWallets as db, getDefaultChain as dc, sortWallets as dd, areSameAddress as de, sortAddressBook as df, calculateTotalUsdBalanceUSD as dg, addTokenToWallet as dh, isEvmChainNotSupportedError as di, getWalletSupportedChainTypes as dj, getConnectorForChainType as dk, walletSupportsChainType as dl, connectWallet as dm, cancelConnectWallet as dn, isProblematicConnector as dp, mergeWallets as dq, isXionSmartContractAddress as dr, isXrplAddressValid as ds, buildXrplTrustSetTx as dt, getXrplNetwork as du, parseXrplPaymentTx as dv, parseXrplTokenAddress as dw, er as e, formatBNToReadable as f, chainTypeToZeroAddressMap as g, destinationAddressResetValue as h, fallbackAddressResetValue as i, nativeCantonTokenAddress as j, nativeCosmosTokenAddress as k, nativeEvmTokenAddress as l, nativeSolanaTokenAddress as m, nativeBitcoinTokenAddress as n, nativeStellarTokenAddress as o, nativeSuiTokenAddress as p, nativeXrplTokenAddress as q, CosmosProvider as r, SendTransactionStatus as s, TransactionStatus as t, useCosmosContext as u, useTrackSearchEmpty as v, walletIconBaseUrl as w, useSquidChains as x, useClient as y, useCosmosForChain as z };
|
|
39177
|
+
//# sourceMappingURL=index-C3owE9lb.js.map
|