@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
|
@@ -80,7 +80,6 @@ const suiZeroAddress = "0x0000000000000000000000000000000000000000";
|
|
|
80
80
|
const xrplZeroAddress = "rrrrrrrrrrrrrrrrrrrrrhoLvTp";
|
|
81
81
|
const stellarZeroAddress = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF";
|
|
82
82
|
const cantonZeroAddress = "none::12200000000000000000000000000000000000000000000000000000000000000000";
|
|
83
|
-
const danalZeroAddress = "PCI00000000000000000000000000000000000000000000000000";
|
|
84
83
|
const chainTypeToZeroAddressMap = {
|
|
85
84
|
[squidTypes.ChainType.EVM]: viem.zeroAddress,
|
|
86
85
|
[squidTypes.ChainType.COSMOS]: cosmosZeroAddress,
|
|
@@ -90,7 +89,6 @@ const chainTypeToZeroAddressMap = {
|
|
|
90
89
|
[squidTypes.ChainType.XRPL]: xrplZeroAddress,
|
|
91
90
|
[squidTypes.ChainType.STELLAR]: stellarZeroAddress,
|
|
92
91
|
[squidTypes.ChainType.CANTON]: cantonZeroAddress,
|
|
93
|
-
[squidTypes.ChainType.DANAL]: danalZeroAddress,
|
|
94
92
|
};
|
|
95
93
|
const nativeEvmTokenAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
|
96
94
|
const nativeCosmosTokenAddress = "uosmo";
|
|
@@ -100,7 +98,6 @@ const nativeSuiTokenAddress = "0x00000000000000000000000000000000000000000000000
|
|
|
100
98
|
const nativeXrplTokenAddress = "xrp";
|
|
101
99
|
const nativeStellarTokenAddress = "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA";
|
|
102
100
|
const nativeCantonTokenAddress = "DSO::1220b1431ef217342db44d516bb9befde802be7d8899637d290895fa58880f19accc";
|
|
103
|
-
const nativeDanalTokenAddress = "pci";
|
|
104
101
|
// by setting slippage to undefined, it's set to "auto"
|
|
105
102
|
const defaultSlippage = undefined;
|
|
106
103
|
const destinationAddressResetValue = "null";
|
|
@@ -166,7 +163,6 @@ const CHAIN_IDS = {
|
|
|
166
163
|
STELLAR: "stellar-mainnet",
|
|
167
164
|
STELLAR_TESTNET: "stellar-testnet",
|
|
168
165
|
CANTON: "canton",
|
|
169
|
-
DANAL_TESTNET: "danal-testnet",
|
|
170
166
|
};
|
|
171
167
|
const chainTypeToDefaultChainIdMap = {
|
|
172
168
|
[squidTypes.ChainType.EVM]: CHAIN_IDS.ETHEREUM,
|
|
@@ -177,7 +173,6 @@ const chainTypeToDefaultChainIdMap = {
|
|
|
177
173
|
[squidTypes.ChainType.XRPL]: CHAIN_IDS.XRPL,
|
|
178
174
|
[squidTypes.ChainType.STELLAR]: CHAIN_IDS.STELLAR,
|
|
179
175
|
[squidTypes.ChainType.CANTON]: CHAIN_IDS.CANTON,
|
|
180
|
-
[squidTypes.ChainType.DANAL]: CHAIN_IDS.DANAL_TESTNET,
|
|
181
176
|
};
|
|
182
177
|
const chainTypeToNativeTokenAddressMap = {
|
|
183
178
|
[squidTypes.ChainType.EVM]: nativeEvmTokenAddress,
|
|
@@ -188,7 +183,6 @@ const chainTypeToNativeTokenAddressMap = {
|
|
|
188
183
|
[squidTypes.ChainType.XRPL]: nativeXrplTokenAddress,
|
|
189
184
|
[squidTypes.ChainType.STELLAR]: nativeStellarTokenAddress,
|
|
190
185
|
[squidTypes.ChainType.CANTON]: nativeCantonTokenAddress,
|
|
191
|
-
[squidTypes.ChainType.DANAL]: nativeDanalTokenAddress,
|
|
192
186
|
};
|
|
193
187
|
const defaultConfigValues = {
|
|
194
188
|
integratorId: INTEGRATOR_ID,
|
|
@@ -20970,6 +20964,9 @@ const isBitcoinAddressValid = (address) => {
|
|
|
20970
20964
|
}
|
|
20971
20965
|
};
|
|
20972
20966
|
|
|
20967
|
+
function getCantonCommandId(memo) {
|
|
20968
|
+
return `canton-${memo}`;
|
|
20969
|
+
}
|
|
20973
20970
|
/**
|
|
20974
20971
|
* Format a Canton connector into a Squid {@link CantonWallet}. Shared by the
|
|
20975
20972
|
* announce-discovery and injected-provider paths.
|
|
@@ -21120,51 +21117,16 @@ function cantonInstrumentKey(instrument) {
|
|
|
21120
21117
|
* is already a Decimal, not base units).
|
|
21121
21118
|
*/
|
|
21122
21119
|
function groupCantonHoldingBalances(acsResponse) {
|
|
21120
|
+
return sumCantonHoldingBalances(parseCantonHoldings(acsResponse).filter((holding) => !holding.locked));
|
|
21121
|
+
}
|
|
21122
|
+
function sumCantonHoldingBalances(holdings) {
|
|
21123
21123
|
const totals = {};
|
|
21124
|
-
for (const holding of
|
|
21125
|
-
// Locked holdings are not spendable, skip them
|
|
21126
|
-
if (holding.locked)
|
|
21127
|
-
continue;
|
|
21124
|
+
for (const holding of holdings) {
|
|
21128
21125
|
const key = cantonInstrumentKey(holding.instrument);
|
|
21129
21126
|
const total = totals[key] ?? new BigNumber(0);
|
|
21130
21127
|
totals[key] = total.plus(new BigNumber(holding.amount));
|
|
21131
21128
|
}
|
|
21132
|
-
return Object.fromEntries(Object.entries(totals).map(([key, total]) => [key, total.
|
|
21133
|
-
}
|
|
21134
|
-
|
|
21135
|
-
// Matches the backend validation: "PCI" + 50 uppercase-hex chars, case-sensitive.
|
|
21136
|
-
const DANAL_ADDRESS_REGEX = /^PCI[0-9A-F]{50}$/;
|
|
21137
|
-
function isDanalAddressValid(address) {
|
|
21138
|
-
if (!address)
|
|
21139
|
-
return false;
|
|
21140
|
-
return DANAL_ADDRESS_REGEX.test(address);
|
|
21141
|
-
}
|
|
21142
|
-
// Fabric transaction id: 64 hex chars, no 0x prefix
|
|
21143
|
-
const DANAL_TX_HASH_REGEX = /^[0-9a-fA-F]{64}$/;
|
|
21144
|
-
function isDanalTxHashValid(hash) {
|
|
21145
|
-
if (!hash)
|
|
21146
|
-
return false;
|
|
21147
|
-
return DANAL_TX_HASH_REGEX.test(hash);
|
|
21148
|
-
}
|
|
21149
|
-
function isDanalTransferRequestBody(value) {
|
|
21150
|
-
if (typeof value !== "object" || value === null)
|
|
21151
|
-
return false;
|
|
21152
|
-
const { receiver, amount, memo } = value;
|
|
21153
|
-
return (typeof receiver === "string" &&
|
|
21154
|
-
receiver.length > 0 &&
|
|
21155
|
-
typeof amount === "string" &&
|
|
21156
|
-
amount.length > 0 &&
|
|
21157
|
-
typeof memo === "string" &&
|
|
21158
|
-
memo.length > 0);
|
|
21159
|
-
}
|
|
21160
|
-
function parseDanalTransferBody(data) {
|
|
21161
|
-
try {
|
|
21162
|
-
const parsed = JSON.parse(data);
|
|
21163
|
-
return isDanalTransferRequestBody(parsed) ? parsed : null;
|
|
21164
|
-
}
|
|
21165
|
-
catch {
|
|
21166
|
-
return null;
|
|
21167
|
-
}
|
|
21129
|
+
return Object.fromEntries(Object.entries(totals).map(([key, total]) => [key, total.toFixed()]));
|
|
21168
21130
|
}
|
|
21169
21131
|
|
|
21170
21132
|
const solanaTokenProgramIdByAddress = {
|
|
@@ -21503,15 +21465,6 @@ function isOnChainTxData(squidData) {
|
|
|
21503
21465
|
function isDepositAddressDirectTransferRoute(squidData) {
|
|
21504
21466
|
return squidData.type === squidTypes.SquidDataType.DepositAddressDirectTransfer;
|
|
21505
21467
|
}
|
|
21506
|
-
/**
|
|
21507
|
-
* Danal keeps SquidDataType.DepositAddressWithMemo, which XRPL/Stellar use for
|
|
21508
|
-
* wallet-signed transactions — so the deposit treatment is gated on the chain,
|
|
21509
|
-
* not just the type
|
|
21510
|
-
*/
|
|
21511
|
-
function isDanalDepositRoute(route) {
|
|
21512
|
-
return (route.params.fromChain === CHAIN_IDS.DANAL_TESTNET &&
|
|
21513
|
-
route.transactionRequest?.type === squidTypes.SquidDataType.DepositAddressWithMemo);
|
|
21514
|
-
}
|
|
21515
21468
|
function getHistoryTransactionId(tx) {
|
|
21516
21469
|
switch (tx.txType) {
|
|
21517
21470
|
case exports.HistoryTxType.SWAP:
|
|
@@ -22036,11 +21989,6 @@ const chainTypeToTrimLength = {
|
|
|
22036
21989
|
start: 3,
|
|
22037
21990
|
end: 3,
|
|
22038
21991
|
},
|
|
22039
|
-
// PCI0180...E77E
|
|
22040
|
-
[squidTypes.ChainType.DANAL]: {
|
|
22041
|
-
start: 7,
|
|
22042
|
-
end: 4,
|
|
22043
|
-
},
|
|
22044
21992
|
};
|
|
22045
21993
|
const truncateWithEllipsis = (value, startLength, endLength) => value.length <= startLength + endLength
|
|
22046
21994
|
? value
|
|
@@ -22089,8 +22037,6 @@ const isWalletAddressValid = (chainData, address) => {
|
|
|
22089
22037
|
return isStellarAddressValid(address);
|
|
22090
22038
|
case squidTypes.ChainType.CANTON:
|
|
22091
22039
|
return isCantonAddressValid(address);
|
|
22092
|
-
case squidTypes.ChainType.DANAL:
|
|
22093
|
-
return isDanalAddressValid(address);
|
|
22094
22040
|
}
|
|
22095
22041
|
};
|
|
22096
22042
|
const redirectToExtensionsStore = (wallet) => {
|
|
@@ -22375,8 +22321,6 @@ const connectByChainType = async (chainType, wallet, defaultChain, params) => {
|
|
|
22375
22321
|
connector,
|
|
22376
22322
|
},
|
|
22377
22323
|
});
|
|
22378
|
-
case squidTypes.ChainType.DANAL:
|
|
22379
|
-
return null;
|
|
22380
22324
|
}
|
|
22381
22325
|
};
|
|
22382
22326
|
const getChainTypesToConnect = (selectedChainTypes, defaultChain, supportedChains) => {
|
|
@@ -22501,6 +22445,8 @@ const cancelConnectWallet = (wallet) => {
|
|
|
22501
22445
|
return;
|
|
22502
22446
|
}
|
|
22503
22447
|
switch (wallet.type) {
|
|
22448
|
+
case squidTypes.ChainType.CANTON:
|
|
22449
|
+
return wallet.connector.cancelConnect?.();
|
|
22504
22450
|
case squidTypes.ChainType.XRPL:
|
|
22505
22451
|
return wallet.connector.cancelConnect?.();
|
|
22506
22452
|
default:
|
|
@@ -23361,7 +23307,7 @@ const handleDestinationAddressOnSwapChange = ({ fromChainId, fromTokenAddress, t
|
|
|
23361
23307
|
});
|
|
23362
23308
|
let destinationTokenAddress = toChainId && !toTokenAddress
|
|
23363
23309
|
? defaultToAddress
|
|
23364
|
-
: toTokenAddress ?? swapRoute?.toTokenAddress;
|
|
23310
|
+
: (toTokenAddress ?? swapRoute?.toTokenAddress);
|
|
23365
23311
|
const lowerCaseDestinationTokenAddress = destinationTokenAddress?.toLowerCase();
|
|
23366
23312
|
if (destinationTokensFiltered.find((t) => t.address === lowerCaseDestinationTokenAddress) === undefined) {
|
|
23367
23313
|
destinationTokenAddress = defaultToAddress;
|
|
@@ -23413,7 +23359,7 @@ const getNewSwapParamsFromInput = ({ inputParams, initialSwapRoute, tokens, chai
|
|
|
23413
23359
|
toTokenAddress === initialSwapRoute?.fromTokenAddress;
|
|
23414
23360
|
const sourceTokenAddress = sameNewDestAndInitialSource || onlySourceChainIdChanged
|
|
23415
23361
|
? defaultFromAddress
|
|
23416
|
-
: fromTokenAddress ?? initialSwapRoute?.fromTokenAddress;
|
|
23362
|
+
: (fromTokenAddress ?? initialSwapRoute?.fromTokenAddress);
|
|
23417
23363
|
let destinationTokenAddress = undefined;
|
|
23418
23364
|
if (!sameNewSourceAndInitialDest) {
|
|
23419
23365
|
if (onlySourceChainIdChanged) {
|
|
@@ -23735,7 +23681,7 @@ const filterViewableTokens = (tokens, config, direction) => {
|
|
|
23735
23681
|
};
|
|
23736
23682
|
const getSecretNetworkBalances = async (chainData, cosmosAddress, squidTokens, keplrTypeWallet) => {
|
|
23737
23683
|
const squidSecretTokens = squidTokens.filter((t) => t.chainId === CHAIN_IDS.SECRET);
|
|
23738
|
-
const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-
|
|
23684
|
+
const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-GHdReJBs.js'); });
|
|
23739
23685
|
return fetchAllSecretBalances(chainData, cosmosAddress, squidSecretTokens, keplrTypeWallet);
|
|
23740
23686
|
};
|
|
23741
23687
|
function getTokenAssetsKey(token) {
|
|
@@ -25729,7 +25675,7 @@ const useSquidTokens = (direction) => {
|
|
|
25729
25675
|
config.availableChains,
|
|
25730
25676
|
direction,
|
|
25731
25677
|
]);
|
|
25732
|
-
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens,
|
|
25678
|
+
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = React.useMemo(() => {
|
|
25733
25679
|
return tokens?.reduce((acc, token) => {
|
|
25734
25680
|
switch (token.type) {
|
|
25735
25681
|
case squidTypes.ChainType.EVM:
|
|
@@ -25756,9 +25702,6 @@ const useSquidTokens = (direction) => {
|
|
|
25756
25702
|
case squidTypes.ChainType.CANTON:
|
|
25757
25703
|
acc.cantonTokens.push(token);
|
|
25758
25704
|
break;
|
|
25759
|
-
case squidTypes.ChainType.DANAL:
|
|
25760
|
-
acc.danalTokens.push(token);
|
|
25761
|
-
break;
|
|
25762
25705
|
}
|
|
25763
25706
|
return acc;
|
|
25764
25707
|
}, {
|
|
@@ -25770,7 +25713,6 @@ const useSquidTokens = (direction) => {
|
|
|
25770
25713
|
xrplTokens: [],
|
|
25771
25714
|
stellarTokens: [],
|
|
25772
25715
|
cantonTokens: [],
|
|
25773
|
-
danalTokens: [],
|
|
25774
25716
|
});
|
|
25775
25717
|
}, [tokens]);
|
|
25776
25718
|
const findToken = React.useCallback((address, chainId) => {
|
|
@@ -25789,7 +25731,6 @@ const useSquidTokens = (direction) => {
|
|
|
25789
25731
|
xrplTokens,
|
|
25790
25732
|
stellarTokens,
|
|
25791
25733
|
cantonTokens,
|
|
25792
|
-
danalTokens,
|
|
25793
25734
|
};
|
|
25794
25735
|
};
|
|
25795
25736
|
|
|
@@ -26326,7 +26267,6 @@ const chainTypeToRefetchInterval = {
|
|
|
26326
26267
|
[squidTypes.ChainType.XRPL]: 1_000,
|
|
26327
26268
|
[squidTypes.ChainType.STELLAR]: 1_000,
|
|
26328
26269
|
[squidTypes.ChainType.CANTON]: 1_000,
|
|
26329
|
-
[squidTypes.ChainType.DANAL]: 1_000,
|
|
26330
26270
|
};
|
|
26331
26271
|
/**
|
|
26332
26272
|
* Returns the status refetch interval of a Send transaction
|
|
@@ -26593,7 +26533,7 @@ const useSquidChains = (direction) => {
|
|
|
26593
26533
|
const chain = findChain(chainId);
|
|
26594
26534
|
return chain?.chainType;
|
|
26595
26535
|
}, [findChain]);
|
|
26596
|
-
const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains,
|
|
26536
|
+
const { evmChains, cosmosChains, suiChains, stellarChains, xrplChains, cantonChains, } = React.useMemo(() => {
|
|
26597
26537
|
return chains.reduce((acc, chain) => {
|
|
26598
26538
|
switch (chain.chainType) {
|
|
26599
26539
|
case squidTypes.ChainType.EVM:
|
|
@@ -26614,9 +26554,6 @@ const useSquidChains = (direction) => {
|
|
|
26614
26554
|
case squidTypes.ChainType.CANTON:
|
|
26615
26555
|
acc.cantonChains.push(chain);
|
|
26616
26556
|
break;
|
|
26617
|
-
case squidTypes.ChainType.DANAL:
|
|
26618
|
-
acc.danalChains.push(chain);
|
|
26619
|
-
break;
|
|
26620
26557
|
}
|
|
26621
26558
|
return acc;
|
|
26622
26559
|
}, {
|
|
@@ -26626,7 +26563,6 @@ const useSquidChains = (direction) => {
|
|
|
26626
26563
|
xrplChains: [],
|
|
26627
26564
|
stellarChains: [],
|
|
26628
26565
|
cantonChains: [],
|
|
26629
|
-
danalChains: [],
|
|
26630
26566
|
});
|
|
26631
26567
|
}, [chains]);
|
|
26632
26568
|
const { supportedSourceChains, supportedDestinationChains } = React.useMemo(() => {
|
|
@@ -26648,7 +26584,6 @@ const useSquidChains = (direction) => {
|
|
|
26648
26584
|
xrplChains,
|
|
26649
26585
|
stellarChains,
|
|
26650
26586
|
cantonChains,
|
|
26651
|
-
danalChains,
|
|
26652
26587
|
getChainType,
|
|
26653
26588
|
findChain,
|
|
26654
26589
|
};
|
|
@@ -26962,7 +26897,343 @@ const useBitcoinContext = () => {
|
|
|
26962
26897
|
return context;
|
|
26963
26898
|
};
|
|
26964
26899
|
|
|
26965
|
-
const
|
|
26900
|
+
const vParty = (party) => ({ party });
|
|
26901
|
+
const vText = (text) => ({ text });
|
|
26902
|
+
const vNumeric = (numeric) => ({ numeric });
|
|
26903
|
+
const vContractId = (contractId) => ({
|
|
26904
|
+
contractId,
|
|
26905
|
+
});
|
|
26906
|
+
const vList = (elements) => ({
|
|
26907
|
+
list: { elements },
|
|
26908
|
+
});
|
|
26909
|
+
const vRecord = (fields) => ({
|
|
26910
|
+
record: { fields: fields.map(([label, value]) => ({ label, value })) },
|
|
26911
|
+
});
|
|
26912
|
+
const vTextMap = (entries) => ({ textMap: { entries } });
|
|
26913
|
+
const vTimestamp = (isoString) => ({
|
|
26914
|
+
timestamp: String(Math.floor(new Date(isoString).getTime() * 1000)),
|
|
26915
|
+
});
|
|
26916
|
+
function isRecord(value) {
|
|
26917
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26918
|
+
}
|
|
26919
|
+
function normalizeChoiceContextValue(value) {
|
|
26920
|
+
if (typeof value === "string")
|
|
26921
|
+
return value;
|
|
26922
|
+
if (!isRecord(value) || typeof value.tag !== "string")
|
|
26923
|
+
return null;
|
|
26924
|
+
switch (value.tag) {
|
|
26925
|
+
case "AV_Text":
|
|
26926
|
+
case "AV_ContractId":
|
|
26927
|
+
return typeof value.value === "string"
|
|
26928
|
+
? { tag: value.tag, value: value.value }
|
|
26929
|
+
: null;
|
|
26930
|
+
case "AV_Bool":
|
|
26931
|
+
return typeof value.value === "boolean"
|
|
26932
|
+
? { tag: value.tag, value: value.value }
|
|
26933
|
+
: null;
|
|
26934
|
+
case "AV_List":
|
|
26935
|
+
if (!Array.isArray(value.value))
|
|
26936
|
+
return null;
|
|
26937
|
+
return {
|
|
26938
|
+
tag: value.tag,
|
|
26939
|
+
value: value.value.flatMap((entry) => {
|
|
26940
|
+
const normalized = normalizeChoiceContextValue(entry);
|
|
26941
|
+
return normalized ? [normalized] : [];
|
|
26942
|
+
}),
|
|
26943
|
+
};
|
|
26944
|
+
default: {
|
|
26945
|
+
const inner = value.value;
|
|
26946
|
+
if (typeof inner === "string" ||
|
|
26947
|
+
typeof inner === "number" ||
|
|
26948
|
+
typeof inner === "boolean" ||
|
|
26949
|
+
inner === null) {
|
|
26950
|
+
return { tag: value.tag, value: inner };
|
|
26951
|
+
}
|
|
26952
|
+
if (Array.isArray(inner)) {
|
|
26953
|
+
return {
|
|
26954
|
+
tag: value.tag,
|
|
26955
|
+
value: inner.flatMap((entry) => {
|
|
26956
|
+
const normalized = normalizeChoiceContextValue(entry);
|
|
26957
|
+
return normalized ? [normalized] : [];
|
|
26958
|
+
}),
|
|
26959
|
+
};
|
|
26960
|
+
}
|
|
26961
|
+
if (!isRecord(inner))
|
|
26962
|
+
return null;
|
|
26963
|
+
const normalizedObject = {};
|
|
26964
|
+
for (const [key, entry] of Object.entries(inner)) {
|
|
26965
|
+
if (typeof entry !== "string" &&
|
|
26966
|
+
typeof entry !== "number" &&
|
|
26967
|
+
typeof entry !== "boolean" &&
|
|
26968
|
+
entry !== null) {
|
|
26969
|
+
return null;
|
|
26970
|
+
}
|
|
26971
|
+
normalizedObject[key] = entry;
|
|
26972
|
+
}
|
|
26973
|
+
return { tag: value.tag, value: normalizedObject };
|
|
26974
|
+
}
|
|
26975
|
+
}
|
|
26976
|
+
}
|
|
26977
|
+
function stringifyChoiceContextValue(value) {
|
|
26978
|
+
return typeof value === "string" ? value : String(value);
|
|
26979
|
+
}
|
|
26980
|
+
function choiceContextValueToAnyValue(value) {
|
|
26981
|
+
if (typeof value === "string") {
|
|
26982
|
+
return {
|
|
26983
|
+
variant: { constructor: "AV_Text", value: vText(value) },
|
|
26984
|
+
};
|
|
26985
|
+
}
|
|
26986
|
+
switch (value.tag) {
|
|
26987
|
+
case "AV_ContractId":
|
|
26988
|
+
if (typeof value.value !== "string") {
|
|
26989
|
+
throw new TypeError("AV_ContractId values must be strings");
|
|
26990
|
+
}
|
|
26991
|
+
return {
|
|
26992
|
+
variant: { constructor: value.tag, value: vContractId(value.value) },
|
|
26993
|
+
};
|
|
26994
|
+
case "AV_Bool":
|
|
26995
|
+
if (typeof value.value !== "boolean") {
|
|
26996
|
+
throw new TypeError("AV_Bool values must be booleans");
|
|
26997
|
+
}
|
|
26998
|
+
return {
|
|
26999
|
+
variant: { constructor: value.tag, value: { bool: value.value } },
|
|
27000
|
+
};
|
|
27001
|
+
case "AV_List":
|
|
27002
|
+
if (!Array.isArray(value.value)) {
|
|
27003
|
+
throw new TypeError("AV_List values must be arrays");
|
|
27004
|
+
}
|
|
27005
|
+
return {
|
|
27006
|
+
variant: {
|
|
27007
|
+
constructor: value.tag,
|
|
27008
|
+
value: vList(value.value.map(choiceContextValueToAnyValue)),
|
|
27009
|
+
},
|
|
27010
|
+
};
|
|
27011
|
+
default:
|
|
27012
|
+
return {
|
|
27013
|
+
variant: {
|
|
27014
|
+
constructor: value.tag,
|
|
27015
|
+
value: vText(stringifyChoiceContextValue(value.value)),
|
|
27016
|
+
},
|
|
27017
|
+
};
|
|
27018
|
+
}
|
|
27019
|
+
}
|
|
27020
|
+
|
|
27021
|
+
/** Metadata key used by Splice token-standard transfers to carry our Squid orderhash memo. */
|
|
27022
|
+
const CANTON_MEMO_KEY = "splice.lfdecentralizedtrust.org/reason";
|
|
27023
|
+
/** Interface id for the transfer factory contract returned by the token registry. */
|
|
27024
|
+
const TRANSFER_FACTORY_INTERFACE_ID = "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory";
|
|
27025
|
+
/** Choice exercised on the transfer factory to create the token-standard transfer. */
|
|
27026
|
+
const TRANSFER_FACTORY_CHOICE = "TransferFactory_Transfer";
|
|
27027
|
+
/** CIP-056 (non-Amulet) tokens expose a registrar parameterized by admin party. */
|
|
27028
|
+
const CIP056_REGISTRAR_BASE = "https://api.utilities.digitalasset.com/api/token-standard/v0/registrars";
|
|
27029
|
+
const TRANSFER_REQUESTED_AT_SKEW_MS = 60_000;
|
|
27030
|
+
const TRANSFER_EXECUTE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
27031
|
+
function resolveCantonRegistryUrl(instrument) {
|
|
27032
|
+
if (instrument.id === CANTON_AMULET_INSTRUMENT_ID) {
|
|
27033
|
+
return CANTON_AMULET_REGISTRY_URL;
|
|
27034
|
+
}
|
|
27035
|
+
return `${CIP056_REGISTRAR_BASE}/${encodeURIComponent(instrument.admin)}`;
|
|
27036
|
+
}
|
|
27037
|
+
/**
|
|
27038
|
+
* Build the plain JSON arguments expected by the registry transfer-factory
|
|
27039
|
+
* endpoint before it adds choice context and disclosed contracts.
|
|
27040
|
+
*/
|
|
27041
|
+
function buildTransferChoiceArgs({ sender, receiver, amount, instrument, inputHoldingCids, memo, nowMs, }) {
|
|
27042
|
+
return {
|
|
27043
|
+
expectedAdmin: instrument.admin,
|
|
27044
|
+
transfer: {
|
|
27045
|
+
sender,
|
|
27046
|
+
receiver,
|
|
27047
|
+
amount,
|
|
27048
|
+
instrumentId: { admin: instrument.admin, id: instrument.id },
|
|
27049
|
+
requestedAt: new Date(nowMs - TRANSFER_REQUESTED_AT_SKEW_MS).toISOString(),
|
|
27050
|
+
executeBefore: new Date(nowMs + TRANSFER_EXECUTE_WINDOW_MS).toISOString(),
|
|
27051
|
+
inputHoldingCids,
|
|
27052
|
+
meta: { values: { [CANTON_MEMO_KEY]: memo } },
|
|
27053
|
+
},
|
|
27054
|
+
extraArgs: { context: { values: {} }, meta: { values: {} } },
|
|
27055
|
+
};
|
|
27056
|
+
}
|
|
27057
|
+
/**
|
|
27058
|
+
* Canton's Holding contracts follow Bitcoin-style UTXOs model.
|
|
27059
|
+
*
|
|
27060
|
+
* Pick Holding contract ids covering `amount` for the instrument: prefer a
|
|
27061
|
+
* single exact-amount holding, else accumulate largest-first.
|
|
27062
|
+
*/
|
|
27063
|
+
function selectInputHoldingCids(holdings, instrument, amount) {
|
|
27064
|
+
const spendableHoldings = holdings.filter((h) => !h.locked &&
|
|
27065
|
+
h.instrument.admin === instrument.admin &&
|
|
27066
|
+
h.instrument.id === instrument.id);
|
|
27067
|
+
const target = new BigNumber(amount);
|
|
27068
|
+
const exact = spendableHoldings.find((h) => new BigNumber(h.amount).eq(target));
|
|
27069
|
+
if (exact)
|
|
27070
|
+
return [exact.contractId];
|
|
27071
|
+
const sorted = [...spendableHoldings].sort((a, b) => new BigNumber(b.amount).comparedTo(new BigNumber(a.amount)));
|
|
27072
|
+
const selected = [];
|
|
27073
|
+
let sum = new BigNumber(0);
|
|
27074
|
+
for (const holding of sorted) {
|
|
27075
|
+
selected.push(holding.contractId);
|
|
27076
|
+
sum = sum.plus(holding.amount);
|
|
27077
|
+
if (sum.gte(target))
|
|
27078
|
+
break;
|
|
27079
|
+
}
|
|
27080
|
+
if (sum.lt(target)) {
|
|
27081
|
+
throw new Error("Insufficient Canton holdings to cover transfer amount");
|
|
27082
|
+
}
|
|
27083
|
+
return selected;
|
|
27084
|
+
}
|
|
27085
|
+
async function fetchInputHoldingCids(connector, party, instrument, amount) {
|
|
27086
|
+
const ledgerEnd = await connector.ledgerApi({
|
|
27087
|
+
requestMethod: "get",
|
|
27088
|
+
resource: "/v2/state/ledger-end",
|
|
27089
|
+
});
|
|
27090
|
+
const activeContracts = await connector.ledgerApi({
|
|
27091
|
+
requestMethod: "post",
|
|
27092
|
+
resource: "/v2/state/active-contracts",
|
|
27093
|
+
body: buildHoldingsAcsRequestBody(party, String(ledgerEnd.offset)),
|
|
27094
|
+
});
|
|
27095
|
+
return selectInputHoldingCids(parseCantonHoldings(activeContracts), instrument, amount);
|
|
27096
|
+
}
|
|
27097
|
+
/**
|
|
27098
|
+
* Ask the token registry for the transfer factory contract and extra context
|
|
27099
|
+
* required to exercise its transfer choice.
|
|
27100
|
+
*/
|
|
27101
|
+
async function fetchTransferFactory(registryUrl, choiceArgs) {
|
|
27102
|
+
const response = await fetch(`${registryUrl}/registry/transfer-instruction/v1/transfer-factory`, {
|
|
27103
|
+
method: "POST",
|
|
27104
|
+
headers: { "Content-Type": "application/json" },
|
|
27105
|
+
body: JSON.stringify({
|
|
27106
|
+
choiceArguments: choiceArgs,
|
|
27107
|
+
excludeDebugFields: true,
|
|
27108
|
+
}),
|
|
27109
|
+
});
|
|
27110
|
+
if (!response.ok) {
|
|
27111
|
+
throw new Error(`Canton transfer-factory request failed (${response.status})`);
|
|
27112
|
+
}
|
|
27113
|
+
return response.json();
|
|
27114
|
+
}
|
|
27115
|
+
/**
|
|
27116
|
+
* Convert transfer choice args into strict Daml Value JSON for the wallet's
|
|
27117
|
+
* prepareExecute ExerciseCommand.
|
|
27118
|
+
*/
|
|
27119
|
+
function buildPrepareChoiceArgument(choiceArgs) {
|
|
27120
|
+
const contextValues = Object.entries(choiceArgs.extraArgs.context?.values ?? {})
|
|
27121
|
+
.map(([key, value]) => {
|
|
27122
|
+
const normalized = normalizeChoiceContextValue(value);
|
|
27123
|
+
return normalized
|
|
27124
|
+
? { key, value: choiceContextValueToAnyValue(normalized) }
|
|
27125
|
+
: undefined;
|
|
27126
|
+
})
|
|
27127
|
+
.filter((entry) => Boolean(entry));
|
|
27128
|
+
return vRecord([
|
|
27129
|
+
["expectedAdmin", vParty(choiceArgs.expectedAdmin)],
|
|
27130
|
+
[
|
|
27131
|
+
"transfer",
|
|
27132
|
+
vRecord([
|
|
27133
|
+
["sender", vParty(choiceArgs.transfer.sender)],
|
|
27134
|
+
["receiver", vParty(choiceArgs.transfer.receiver)],
|
|
27135
|
+
["amount", vNumeric(choiceArgs.transfer.amount)],
|
|
27136
|
+
[
|
|
27137
|
+
"instrumentId",
|
|
27138
|
+
vRecord([
|
|
27139
|
+
["admin", vParty(choiceArgs.transfer.instrumentId.admin)],
|
|
27140
|
+
["id", vText(choiceArgs.transfer.instrumentId.id)],
|
|
27141
|
+
]),
|
|
27142
|
+
],
|
|
27143
|
+
["requestedAt", vTimestamp(choiceArgs.transfer.requestedAt)],
|
|
27144
|
+
["executeBefore", vTimestamp(choiceArgs.transfer.executeBefore)],
|
|
27145
|
+
[
|
|
27146
|
+
"inputHoldingCids",
|
|
27147
|
+
vList(choiceArgs.transfer.inputHoldingCids.map(vContractId)),
|
|
27148
|
+
],
|
|
27149
|
+
[
|
|
27150
|
+
"meta",
|
|
27151
|
+
vRecord([
|
|
27152
|
+
[
|
|
27153
|
+
"values",
|
|
27154
|
+
vTextMap(Object.entries(choiceArgs.transfer.meta.values).map(([key, value]) => ({ key, value: vText(value) }))),
|
|
27155
|
+
],
|
|
27156
|
+
]),
|
|
27157
|
+
],
|
|
27158
|
+
]),
|
|
27159
|
+
],
|
|
27160
|
+
[
|
|
27161
|
+
"extraArgs",
|
|
27162
|
+
vRecord([
|
|
27163
|
+
["context", vRecord([["values", vTextMap(contextValues)]])],
|
|
27164
|
+
["meta", vRecord([["values", vTextMap([])]])],
|
|
27165
|
+
]),
|
|
27166
|
+
],
|
|
27167
|
+
]);
|
|
27168
|
+
}
|
|
27169
|
+
/**
|
|
27170
|
+
* Convert a registry disclosed contract into the wallet submission shape,
|
|
27171
|
+
* dropping debug fields and entries without a usable createdEventBlob.
|
|
27172
|
+
*/
|
|
27173
|
+
function toDisclosedContract(raw) {
|
|
27174
|
+
if (typeof raw.createdEventBlob !== "string")
|
|
27175
|
+
return null;
|
|
27176
|
+
return {
|
|
27177
|
+
createdEventBlob: raw.createdEventBlob,
|
|
27178
|
+
contractId: typeof raw.contractId === "string" ? raw.contractId : undefined,
|
|
27179
|
+
synchronizerId: typeof raw.synchronizerId === "string" ? raw.synchronizerId : undefined,
|
|
27180
|
+
};
|
|
27181
|
+
}
|
|
27182
|
+
/**
|
|
27183
|
+
* Build a Splice token-standard transfer of `amount` of `token` from `sender`
|
|
27184
|
+
* to `receiver`, carrying `memo`, ready to hand to a wallet's prepareExecute.
|
|
27185
|
+
* Returns the full prepareExecute params (commandId + commands + actAs +
|
|
27186
|
+
* disclosedContracts).
|
|
27187
|
+
*/
|
|
27188
|
+
async function buildCantonTransfer(connector, { sender, receiver, amount, token, memo, nowMs, }) {
|
|
27189
|
+
if (!token.originalAddress) {
|
|
27190
|
+
throw new Error("Canton token originalAddress is required");
|
|
27191
|
+
}
|
|
27192
|
+
const instrument = parseCantonInstrument(token.originalAddress);
|
|
27193
|
+
if (!instrument) {
|
|
27194
|
+
throw new Error(`Invalid Canton token address: ${token.originalAddress}`);
|
|
27195
|
+
}
|
|
27196
|
+
const registryUrl = resolveCantonRegistryUrl(instrument);
|
|
27197
|
+
const inputHoldingCids = await fetchInputHoldingCids(connector, sender, instrument, amount);
|
|
27198
|
+
const choiceArgs = buildTransferChoiceArgs({
|
|
27199
|
+
sender,
|
|
27200
|
+
receiver,
|
|
27201
|
+
amount,
|
|
27202
|
+
instrument,
|
|
27203
|
+
inputHoldingCids,
|
|
27204
|
+
memo,
|
|
27205
|
+
nowMs,
|
|
27206
|
+
});
|
|
27207
|
+
const factory = await fetchTransferFactory(registryUrl, choiceArgs);
|
|
27208
|
+
const choiceContextData = factory.choiceContext.choiceContextData;
|
|
27209
|
+
if (!choiceContextData) {
|
|
27210
|
+
throw new Error("Transfer factory choice context is missing");
|
|
27211
|
+
}
|
|
27212
|
+
choiceArgs.extraArgs.context = choiceContextData;
|
|
27213
|
+
const disclosedContracts = (factory.choiceContext?.disclosedContracts ?? []).flatMap((raw) => {
|
|
27214
|
+
const disclosedContract = toDisclosedContract(raw);
|
|
27215
|
+
return disclosedContract ? [disclosedContract] : [];
|
|
27216
|
+
});
|
|
27217
|
+
// Command ID: client-supplied identifier used to deduplicate submissions
|
|
27218
|
+
const commandId = getCantonCommandId(memo);
|
|
27219
|
+
return {
|
|
27220
|
+
commandId,
|
|
27221
|
+
commands: [
|
|
27222
|
+
{
|
|
27223
|
+
ExerciseCommand: {
|
|
27224
|
+
templateId: TRANSFER_FACTORY_INTERFACE_ID,
|
|
27225
|
+
contractId: factory.factoryId,
|
|
27226
|
+
choice: TRANSFER_FACTORY_CHOICE,
|
|
27227
|
+
choiceArgument: buildPrepareChoiceArgument(choiceArgs),
|
|
27228
|
+
},
|
|
27229
|
+
},
|
|
27230
|
+
],
|
|
27231
|
+
actAs: [sender],
|
|
27232
|
+
disclosedContracts,
|
|
27233
|
+
};
|
|
27234
|
+
}
|
|
27235
|
+
|
|
27236
|
+
const REQUEST_TIMEOUT_MS$1 = 60_000;
|
|
26966
27237
|
function pickAddress(accounts) {
|
|
26967
27238
|
return (accounts.find((a) => a.primary) ?? accounts[0])?.partyId;
|
|
26968
27239
|
}
|
|
@@ -26971,6 +27242,7 @@ function pickAddress(accounts) {
|
|
|
26971
27242
|
* (bound to its announce `target`) and exposes the lifecycle methods.
|
|
26972
27243
|
*/
|
|
26973
27244
|
class CantonBrowserConnector {
|
|
27245
|
+
kind = "cip103";
|
|
26974
27246
|
providerId;
|
|
26975
27247
|
name;
|
|
26976
27248
|
target;
|
|
@@ -26985,7 +27257,7 @@ class CantonBrowserConnector {
|
|
|
26985
27257
|
client ??
|
|
26986
27258
|
dapp.createCantonClient(new dapp.WindowTransport(win, {
|
|
26987
27259
|
target: this.target,
|
|
26988
|
-
timeout: REQUEST_TIMEOUT_MS,
|
|
27260
|
+
timeout: REQUEST_TIMEOUT_MS$1,
|
|
26989
27261
|
}));
|
|
26990
27262
|
}
|
|
26991
27263
|
get accounts() {
|
|
@@ -27031,6 +27303,28 @@ class CantonBrowserConnector {
|
|
|
27031
27303
|
this._accounts = await this.client.listAccounts();
|
|
27032
27304
|
return this._accounts;
|
|
27033
27305
|
}
|
|
27306
|
+
async getBalances(partyId) {
|
|
27307
|
+
const { offset } = await this.ledgerApi({
|
|
27308
|
+
requestMethod: "get",
|
|
27309
|
+
resource: "/v2/state/ledger-end",
|
|
27310
|
+
});
|
|
27311
|
+
const contracts = await this.ledgerApi({
|
|
27312
|
+
requestMethod: "post",
|
|
27313
|
+
resource: "/v2/state/active-contracts",
|
|
27314
|
+
body: buildHoldingsAcsRequestBody(partyId, String(offset)),
|
|
27315
|
+
});
|
|
27316
|
+
return groupCantonHoldingBalances(contracts);
|
|
27317
|
+
}
|
|
27318
|
+
async transfer(params) {
|
|
27319
|
+
const payload = await buildCantonTransfer(this, params);
|
|
27320
|
+
const result = await this.prepareExecuteAndWait(payload);
|
|
27321
|
+
const updateId = result.tx?.payload?.updateId ?? "";
|
|
27322
|
+
return {
|
|
27323
|
+
status: "completed",
|
|
27324
|
+
commandId: getCantonCommandId(params.memo),
|
|
27325
|
+
updateId,
|
|
27326
|
+
};
|
|
27327
|
+
}
|
|
27034
27328
|
on(event, handler) {
|
|
27035
27329
|
this.listeners[event].add(handler);
|
|
27036
27330
|
}
|
|
@@ -27137,6 +27431,313 @@ function discoverInjectedCantonWallets(win) {
|
|
|
27137
27431
|
});
|
|
27138
27432
|
}
|
|
27139
27433
|
|
|
27434
|
+
class CantonConnectionCancelledError extends Error {
|
|
27435
|
+
code = 4001;
|
|
27436
|
+
constructor() {
|
|
27437
|
+
super("Wallet connection cancelled");
|
|
27438
|
+
}
|
|
27439
|
+
}
|
|
27440
|
+
|
|
27441
|
+
const REQUEST_TIMEOUT_MS = 5 * 60_000;
|
|
27442
|
+
const OVERLAY_ID = "loop-sdk-connect-overlay";
|
|
27443
|
+
class LoopSubmissionUnknownError extends Error {
|
|
27444
|
+
constructor(cause) {
|
|
27445
|
+
super("Loop transfer outcome is unknown. Check Loop before trying again.", {
|
|
27446
|
+
cause,
|
|
27447
|
+
});
|
|
27448
|
+
}
|
|
27449
|
+
}
|
|
27450
|
+
function parseLoopTransferUpdate(value, commandId) {
|
|
27451
|
+
if (!value || typeof value !== "object")
|
|
27452
|
+
return undefined;
|
|
27453
|
+
const response = value;
|
|
27454
|
+
if (response.command_id !== commandId)
|
|
27455
|
+
return undefined;
|
|
27456
|
+
if (response.status === "failed" || response.error) {
|
|
27457
|
+
const error = response.error;
|
|
27458
|
+
return {
|
|
27459
|
+
status: "failed",
|
|
27460
|
+
commandId,
|
|
27461
|
+
message: typeof error?.error_message === "string"
|
|
27462
|
+
? error.error_message
|
|
27463
|
+
: "Loop transaction failed",
|
|
27464
|
+
};
|
|
27465
|
+
}
|
|
27466
|
+
if (typeof response.update_id === "string" && response.update_id) {
|
|
27467
|
+
return { status: "completed", commandId, updateId: response.update_id };
|
|
27468
|
+
}
|
|
27469
|
+
return undefined;
|
|
27470
|
+
}
|
|
27471
|
+
class LoopCantonConnector {
|
|
27472
|
+
loadSdk;
|
|
27473
|
+
kind = "loop";
|
|
27474
|
+
providerId = "canton-loop";
|
|
27475
|
+
name = "Loop";
|
|
27476
|
+
target = undefined;
|
|
27477
|
+
module;
|
|
27478
|
+
initialization;
|
|
27479
|
+
provider;
|
|
27480
|
+
pendingConnection;
|
|
27481
|
+
connectionStart = Promise.resolve();
|
|
27482
|
+
transfers = new Map();
|
|
27483
|
+
listeners = { connect: new Set(), disconnect: new Set() };
|
|
27484
|
+
constructor(loadSdk = () => import('@fivenorth/loop-sdk')) {
|
|
27485
|
+
this.loadSdk = loadSdk;
|
|
27486
|
+
}
|
|
27487
|
+
get accounts() {
|
|
27488
|
+
return this.provider
|
|
27489
|
+
? [{ partyId: this.provider.party_id, primary: true }]
|
|
27490
|
+
: [];
|
|
27491
|
+
}
|
|
27492
|
+
get address() {
|
|
27493
|
+
return this.provider?.party_id;
|
|
27494
|
+
}
|
|
27495
|
+
connect() {
|
|
27496
|
+
return this.startConnection(true);
|
|
27497
|
+
}
|
|
27498
|
+
autoConnect() {
|
|
27499
|
+
return this.startConnection(false);
|
|
27500
|
+
}
|
|
27501
|
+
cancelConnect() {
|
|
27502
|
+
if (!this.pendingConnection)
|
|
27503
|
+
return;
|
|
27504
|
+
this.rejectConnection(new CantonConnectionCancelledError());
|
|
27505
|
+
this.module?.loop.logout();
|
|
27506
|
+
}
|
|
27507
|
+
async disconnect() {
|
|
27508
|
+
this.cancelConnect();
|
|
27509
|
+
this.module?.loop.logout();
|
|
27510
|
+
this.provider = undefined;
|
|
27511
|
+
this.emit("disconnect");
|
|
27512
|
+
}
|
|
27513
|
+
async getAccounts() {
|
|
27514
|
+
const provider = this.requireProvider();
|
|
27515
|
+
try {
|
|
27516
|
+
const account = await provider.getAccount();
|
|
27517
|
+
if (account.party_id !== provider.party_id) {
|
|
27518
|
+
await this.disconnect();
|
|
27519
|
+
throw new Error("Loop account changed. Connect again.");
|
|
27520
|
+
}
|
|
27521
|
+
return this.accounts;
|
|
27522
|
+
}
|
|
27523
|
+
catch (error) {
|
|
27524
|
+
if (this.module && error instanceof this.module.UnauthorizedError) {
|
|
27525
|
+
await this.disconnect();
|
|
27526
|
+
}
|
|
27527
|
+
throw error;
|
|
27528
|
+
}
|
|
27529
|
+
}
|
|
27530
|
+
async getBalances(partyId) {
|
|
27531
|
+
const provider = this.requireProvider();
|
|
27532
|
+
if (partyId !== provider.party_id) {
|
|
27533
|
+
throw new Error("Loop balances require the connected account");
|
|
27534
|
+
}
|
|
27535
|
+
const holdings = await provider.getHolding();
|
|
27536
|
+
return sumCantonHoldingBalances(holdings.map((holding) => {
|
|
27537
|
+
const amount = new BigNumber(holding.total_unlocked_coin);
|
|
27538
|
+
if (!amount.isFinite() || amount.isNegative()) {
|
|
27539
|
+
throw new Error("Loop returned an invalid balance");
|
|
27540
|
+
}
|
|
27541
|
+
return {
|
|
27542
|
+
instrument: holding.instrument_id,
|
|
27543
|
+
amount: holding.total_unlocked_coin,
|
|
27544
|
+
};
|
|
27545
|
+
}));
|
|
27546
|
+
}
|
|
27547
|
+
transfer(params) {
|
|
27548
|
+
const commandId = getCantonCommandId(params.memo);
|
|
27549
|
+
const existing = this.transfers.get(commandId);
|
|
27550
|
+
if (existing)
|
|
27551
|
+
return existing;
|
|
27552
|
+
const request = this.submitTransfer(params, commandId).catch((error) => {
|
|
27553
|
+
if (!(error instanceof LoopSubmissionUnknownError))
|
|
27554
|
+
this.transfers.delete(commandId);
|
|
27555
|
+
throw error;
|
|
27556
|
+
});
|
|
27557
|
+
this.transfers.set(commandId, request);
|
|
27558
|
+
return request;
|
|
27559
|
+
}
|
|
27560
|
+
on(event, handler) {
|
|
27561
|
+
this.listeners[event].add(handler);
|
|
27562
|
+
}
|
|
27563
|
+
off(event, handler) {
|
|
27564
|
+
this.listeners[event].delete(handler);
|
|
27565
|
+
}
|
|
27566
|
+
emit(event, ...args) {
|
|
27567
|
+
this.listeners[event].forEach((handler) => handler(...args));
|
|
27568
|
+
}
|
|
27569
|
+
initialize() {
|
|
27570
|
+
if (!this.initialization) {
|
|
27571
|
+
this.initialization = this.loadSdk()
|
|
27572
|
+
.then((module) => {
|
|
27573
|
+
module.loop.init({
|
|
27574
|
+
appName: SQUID_METADATA.name,
|
|
27575
|
+
network: "mainnet",
|
|
27576
|
+
onAccept: (provider) => this.acceptConnection(provider),
|
|
27577
|
+
onReject: () => {
|
|
27578
|
+
this.rejectConnection(new CantonConnectionCancelledError());
|
|
27579
|
+
this.provider = undefined;
|
|
27580
|
+
this.emit("disconnect");
|
|
27581
|
+
},
|
|
27582
|
+
});
|
|
27583
|
+
this.module = module;
|
|
27584
|
+
})
|
|
27585
|
+
.catch((error) => {
|
|
27586
|
+
this.initialization = undefined;
|
|
27587
|
+
throw error;
|
|
27588
|
+
});
|
|
27589
|
+
}
|
|
27590
|
+
return this.initialization;
|
|
27591
|
+
}
|
|
27592
|
+
startConnection(interactive) {
|
|
27593
|
+
if (this.pendingConnection)
|
|
27594
|
+
return this.pendingConnection.promise;
|
|
27595
|
+
let resolve;
|
|
27596
|
+
let reject;
|
|
27597
|
+
const promise = new Promise((accept, decline) => {
|
|
27598
|
+
resolve = accept;
|
|
27599
|
+
reject = decline;
|
|
27600
|
+
});
|
|
27601
|
+
const timer = setTimeout(() => {
|
|
27602
|
+
this.rejectConnection(new Error("Loop connection timed out"));
|
|
27603
|
+
this.module?.loop.logout();
|
|
27604
|
+
}, REQUEST_TIMEOUT_MS);
|
|
27605
|
+
let observer;
|
|
27606
|
+
const pending = {
|
|
27607
|
+
promise,
|
|
27608
|
+
resolve,
|
|
27609
|
+
reject,
|
|
27610
|
+
cleanup: () => {
|
|
27611
|
+
clearTimeout(timer);
|
|
27612
|
+
observer?.disconnect();
|
|
27613
|
+
},
|
|
27614
|
+
};
|
|
27615
|
+
this.pendingConnection = pending;
|
|
27616
|
+
this.connectionStart = this.connectionStart
|
|
27617
|
+
.then(async () => {
|
|
27618
|
+
await this.initialize();
|
|
27619
|
+
if (this.pendingConnection !== pending)
|
|
27620
|
+
return;
|
|
27621
|
+
if (interactive && typeof MutationObserver !== "undefined") {
|
|
27622
|
+
let sawOverlay = false;
|
|
27623
|
+
observer = new MutationObserver(() => {
|
|
27624
|
+
const overlay = document.getElementById(OVERLAY_ID);
|
|
27625
|
+
if (overlay)
|
|
27626
|
+
sawOverlay = true;
|
|
27627
|
+
if (sawOverlay && !overlay && this.pendingConnection === pending) {
|
|
27628
|
+
this.cancelConnect();
|
|
27629
|
+
}
|
|
27630
|
+
});
|
|
27631
|
+
observer.observe(document.body, { childList: true });
|
|
27632
|
+
}
|
|
27633
|
+
const sdk = this.module.loop;
|
|
27634
|
+
if (interactive)
|
|
27635
|
+
await sdk.connect();
|
|
27636
|
+
else
|
|
27637
|
+
await sdk.autoConnect();
|
|
27638
|
+
if (!interactive && this.pendingConnection === pending) {
|
|
27639
|
+
this.rejectConnection(new Error("No saved Loop connection"));
|
|
27640
|
+
}
|
|
27641
|
+
else if (interactive &&
|
|
27642
|
+
this.pendingConnection !== pending &&
|
|
27643
|
+
!this.provider) {
|
|
27644
|
+
sdk.logout();
|
|
27645
|
+
}
|
|
27646
|
+
})
|
|
27647
|
+
.catch((error) => {
|
|
27648
|
+
if (this.pendingConnection === pending)
|
|
27649
|
+
this.rejectConnection(error);
|
|
27650
|
+
});
|
|
27651
|
+
return promise;
|
|
27652
|
+
}
|
|
27653
|
+
acceptConnection(provider) {
|
|
27654
|
+
const pending = this.pendingConnection;
|
|
27655
|
+
if (!pending)
|
|
27656
|
+
return;
|
|
27657
|
+
if (!provider.party_id) {
|
|
27658
|
+
this.rejectConnection(new Error("Loop returned no account"));
|
|
27659
|
+
return;
|
|
27660
|
+
}
|
|
27661
|
+
this.provider = provider;
|
|
27662
|
+
this.pendingConnection = undefined;
|
|
27663
|
+
pending.cleanup();
|
|
27664
|
+
pending.resolve({ address: provider.party_id, accounts: this.accounts });
|
|
27665
|
+
this.emit("connect", provider.party_id);
|
|
27666
|
+
}
|
|
27667
|
+
rejectConnection(error) {
|
|
27668
|
+
const pending = this.pendingConnection;
|
|
27669
|
+
if (!pending)
|
|
27670
|
+
return;
|
|
27671
|
+
this.pendingConnection = undefined;
|
|
27672
|
+
pending.cleanup();
|
|
27673
|
+
pending.reject(error);
|
|
27674
|
+
}
|
|
27675
|
+
requireProvider() {
|
|
27676
|
+
if (!this.provider)
|
|
27677
|
+
throw new Error("Loop wallet is not connected");
|
|
27678
|
+
return this.provider;
|
|
27679
|
+
}
|
|
27680
|
+
async submitTransfer(params, commandId) {
|
|
27681
|
+
const provider = this.requireProvider();
|
|
27682
|
+
if (params.sender !== provider.party_id)
|
|
27683
|
+
throw new Error("Loop account does not match the sender");
|
|
27684
|
+
const instrument = params.token.originalAddress
|
|
27685
|
+
? parseCantonInstrument(params.token.originalAddress)
|
|
27686
|
+
: null;
|
|
27687
|
+
if (!instrument)
|
|
27688
|
+
throw new Error("Canton token originalAddress is required");
|
|
27689
|
+
if (!new BigNumber(params.amount).isFinite() ||
|
|
27690
|
+
!new BigNumber(params.amount).isPositive()) {
|
|
27691
|
+
throw new Error("Canton transfer amount must be positive");
|
|
27692
|
+
}
|
|
27693
|
+
const payload = await provider.connection.prepareTransfer(provider.getAuthToken(), {
|
|
27694
|
+
recipient: params.receiver,
|
|
27695
|
+
amount: params.amount,
|
|
27696
|
+
instrument: {
|
|
27697
|
+
instrument_admin: instrument.admin,
|
|
27698
|
+
instrument_id: instrument.id,
|
|
27699
|
+
},
|
|
27700
|
+
memo: params.memo,
|
|
27701
|
+
requested_at: new Date(params.nowMs - 60_000).toISOString(),
|
|
27702
|
+
execute_before: new Date(params.nowMs + 24 * 60 * 60_000).toISOString(),
|
|
27703
|
+
});
|
|
27704
|
+
if (this.provider !== provider)
|
|
27705
|
+
throw new Error("Loop account disconnected before submission");
|
|
27706
|
+
let response;
|
|
27707
|
+
let timer;
|
|
27708
|
+
try {
|
|
27709
|
+
const submission = { ...payload, commandId };
|
|
27710
|
+
const submitted = provider.submitAndWaitForTransaction(submission, {
|
|
27711
|
+
requestTimeout: REQUEST_TIMEOUT_MS,
|
|
27712
|
+
});
|
|
27713
|
+
response = await Promise.race([
|
|
27714
|
+
submitted,
|
|
27715
|
+
new Promise((_, reject) => {
|
|
27716
|
+
timer = setTimeout(() => reject(new Error("Loop submission timed out")), REQUEST_TIMEOUT_MS);
|
|
27717
|
+
}),
|
|
27718
|
+
]);
|
|
27719
|
+
}
|
|
27720
|
+
catch (error) {
|
|
27721
|
+
if (this.module && error instanceof this.module.RejectRequestError) {
|
|
27722
|
+
throw Object.assign(new Error("Loop transaction rejected"), {
|
|
27723
|
+
code: 4001,
|
|
27724
|
+
});
|
|
27725
|
+
}
|
|
27726
|
+
throw new LoopSubmissionUnknownError(error);
|
|
27727
|
+
}
|
|
27728
|
+
finally {
|
|
27729
|
+
clearTimeout(timer);
|
|
27730
|
+
}
|
|
27731
|
+
const update = parseLoopTransferUpdate(response, commandId);
|
|
27732
|
+
if (update?.status === "failed")
|
|
27733
|
+
throw new Error(update.message);
|
|
27734
|
+
if (!update)
|
|
27735
|
+
throw new LoopSubmissionUnknownError();
|
|
27736
|
+
return update;
|
|
27737
|
+
}
|
|
27738
|
+
}
|
|
27739
|
+
const loopCantonConnector = new LoopCantonConnector();
|
|
27740
|
+
|
|
27140
27741
|
/**
|
|
27141
27742
|
* Discovers Canton browser-extension wallets and maps each to a
|
|
27142
27743
|
* {@link CantonWallet}: CIP-103 announce-protocol wallets (on mount and on
|
|
@@ -27183,7 +27784,19 @@ function useCantonWallets() {
|
|
|
27183
27784
|
return;
|
|
27184
27785
|
const announced = (await discoverCantonProviders()).map(toAnnouncedWallet);
|
|
27185
27786
|
const injected = discoverInjectedCantonWallets().map(toInjectedWallet);
|
|
27186
|
-
|
|
27787
|
+
const loopWallet = {
|
|
27788
|
+
...formatCantonWallet({
|
|
27789
|
+
connectorId: "canton-loop",
|
|
27790
|
+
name: "Loop",
|
|
27791
|
+
icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/master/wallets/canton-loop.svg",
|
|
27792
|
+
connector: loopCantonConnector,
|
|
27793
|
+
}),
|
|
27794
|
+
links: { website: "https://cantonloop.com" },
|
|
27795
|
+
isInstalled: () => false,
|
|
27796
|
+
skipInstallCheck: true,
|
|
27797
|
+
isMobile: true,
|
|
27798
|
+
};
|
|
27799
|
+
setWallets(mergeInjectedCantonWallets(announced, [...injected, loopWallet]));
|
|
27187
27800
|
}, [toAnnouncedWallet, toInjectedWallet]);
|
|
27188
27801
|
React.useEffect(() => {
|
|
27189
27802
|
refresh();
|
|
@@ -27214,7 +27827,9 @@ function useCanton() {
|
|
|
27214
27827
|
// Try to reconnect silently first (existing session, no popup)
|
|
27215
27828
|
result = await wallet.connector.autoConnect();
|
|
27216
27829
|
}
|
|
27217
|
-
catch {
|
|
27830
|
+
catch (error) {
|
|
27831
|
+
if (error instanceof CantonConnectionCancelledError)
|
|
27832
|
+
throw error;
|
|
27218
27833
|
// Otherwise prompt the wallet to connect
|
|
27219
27834
|
result = await wallet.connector.connect();
|
|
27220
27835
|
}
|
|
@@ -27236,8 +27851,7 @@ function useCanton() {
|
|
|
27236
27851
|
wallet: recentCantonWallet,
|
|
27237
27852
|
address,
|
|
27238
27853
|
});
|
|
27239
|
-
|
|
27240
|
-
}, [wallets.map((w) => w.connectorId), recentCantonWalletId]);
|
|
27854
|
+
}, [wallets, recentCantonWalletId, setConnectedWallet]);
|
|
27241
27855
|
React.useEffect(() => {
|
|
27242
27856
|
const wallet = connectedCantonWallet.wallet;
|
|
27243
27857
|
if (!wallet) {
|
|
@@ -27494,7 +28108,7 @@ function useStellarWallets() {
|
|
|
27494
28108
|
try {
|
|
27495
28109
|
const { allowAllModules: initializeAllModules } = await import('@creit.tech/stellar-wallets-kit');
|
|
27496
28110
|
const { LedgerModule } = await import('@creit.tech/stellar-wallets-kit/modules/ledger.module.mjs');
|
|
27497
|
-
const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-
|
|
28111
|
+
const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-DvA-2pXq.js'); });
|
|
27498
28112
|
const modules = [...initializeAllModules(), new LedgerModule()];
|
|
27499
28113
|
const promises = modules.map(async (module) => {
|
|
27500
28114
|
const isAvailable = await module.isAvailable();
|
|
@@ -28073,6 +28687,11 @@ const useWallet = () => {
|
|
|
28073
28687
|
}
|
|
28074
28688
|
}
|
|
28075
28689
|
catch (error) {
|
|
28690
|
+
const connecting = useWalletStore.getState().connectingWalletState;
|
|
28691
|
+
if (connecting.wallet !== wallet ||
|
|
28692
|
+
connecting.status !== exports.ConnectingWalletStatus.CONNECTING) {
|
|
28693
|
+
return false;
|
|
28694
|
+
}
|
|
28076
28695
|
console.error("Failed to connect wallet", error);
|
|
28077
28696
|
walletConnectionError(wallet, error);
|
|
28078
28697
|
return false;
|
|
@@ -28304,8 +28923,6 @@ const useMultiChainWallet = (chain) => {
|
|
|
28304
28923
|
chainType: chain.chainType,
|
|
28305
28924
|
}),
|
|
28306
28925
|
};
|
|
28307
|
-
case squidTypes.ChainType.DANAL:
|
|
28308
|
-
return {};
|
|
28309
28926
|
}
|
|
28310
28927
|
}, [
|
|
28311
28928
|
chain?.chainType,
|
|
@@ -29144,8 +29761,6 @@ async function createClient(chain) {
|
|
|
29144
29761
|
return new StellarRpcClient(chain.rpc);
|
|
29145
29762
|
case squidTypes.ChainType.CANTON:
|
|
29146
29763
|
return null;
|
|
29147
|
-
case squidTypes.ChainType.DANAL:
|
|
29148
|
-
return null;
|
|
29149
29764
|
}
|
|
29150
29765
|
}
|
|
29151
29766
|
|
|
@@ -30160,8 +30775,7 @@ const getAllStellarTokensBalance = async (userAddress, stellarTokens, stellarCha
|
|
|
30160
30775
|
};
|
|
30161
30776
|
/**
|
|
30162
30777
|
* Canton balances are private: they can only be read with a wallet-authorized
|
|
30163
|
-
*
|
|
30164
|
-
* party). So balance is fetchable only when a Canton wallet is connected, and
|
|
30778
|
+
* session. Balance is fetchable only when a Canton wallet is connected, and
|
|
30165
30779
|
* only for that wallet's party — there's no address-only read.
|
|
30166
30780
|
*/
|
|
30167
30781
|
const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
|
|
@@ -30170,30 +30784,14 @@ const getCantonTokenBalance = async (userAddress, token, cantonConnector) => {
|
|
|
30170
30784
|
const instrument = parseCantonInstrument(token.originalAddress);
|
|
30171
30785
|
if (!instrument)
|
|
30172
30786
|
return "0";
|
|
30173
|
-
const
|
|
30174
|
-
const activeContracts = await cantonConnector.ledgerApi({
|
|
30175
|
-
requestMethod: "post",
|
|
30176
|
-
resource: "/v2/state/active-contracts",
|
|
30177
|
-
body: buildHoldingsAcsRequestBody(userAddress, String(ledgerEnd.offset)),
|
|
30178
|
-
});
|
|
30179
|
-
const grouped = groupCantonHoldingBalances(activeContracts);
|
|
30787
|
+
const grouped = await cantonConnector.getBalances(userAddress);
|
|
30180
30788
|
return grouped[cantonInstrumentKey(instrument)] ?? "0";
|
|
30181
30789
|
};
|
|
30182
30790
|
/**
|
|
30183
|
-
* Fetch balances for all Canton tokens
|
|
30184
|
-
* private, so this requires a connected wallet's authorized `ledgerApi`.
|
|
30791
|
+
* Fetch balances for all Canton tokens through the connected wallet.
|
|
30185
30792
|
*/
|
|
30186
30793
|
async function getAllCantonTokensBalance({ connector, partyId, tokens, }) {
|
|
30187
|
-
const
|
|
30188
|
-
requestMethod: "get",
|
|
30189
|
-
resource: "/v2/state/ledger-end",
|
|
30190
|
-
});
|
|
30191
|
-
const activeContracts = await connector.ledgerApi({
|
|
30192
|
-
requestMethod: "post",
|
|
30193
|
-
resource: "/v2/state/active-contracts",
|
|
30194
|
-
body: buildHoldingsAcsRequestBody(partyId, String(ledgerEnd.offset)),
|
|
30195
|
-
});
|
|
30196
|
-
const grouped = groupCantonHoldingBalances(activeContracts);
|
|
30794
|
+
const grouped = await connector.getBalances(partyId);
|
|
30197
30795
|
return tokens.map((token) => {
|
|
30198
30796
|
const instrument = token.originalAddress
|
|
30199
30797
|
? parseCantonInstrument(token.originalAddress)
|
|
@@ -30412,7 +31010,7 @@ const useCantonBalance = ({ chain, token, userAddress, enabled = true, refreshIn
|
|
|
30412
31010
|
};
|
|
30413
31011
|
|
|
30414
31012
|
function useNativeTokenForChain(chain) {
|
|
30415
|
-
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens,
|
|
31013
|
+
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens();
|
|
30416
31014
|
const getTokensForChainType = () => {
|
|
30417
31015
|
if (!chain?.chainType)
|
|
30418
31016
|
return [];
|
|
@@ -30433,8 +31031,6 @@ function useNativeTokenForChain(chain) {
|
|
|
30433
31031
|
return stellarTokens;
|
|
30434
31032
|
case squidTypes.ChainType.CANTON:
|
|
30435
31033
|
return cantonTokens;
|
|
30436
|
-
case squidTypes.ChainType.DANAL:
|
|
30437
|
-
return danalTokens;
|
|
30438
31034
|
}
|
|
30439
31035
|
};
|
|
30440
31036
|
const nativeTokenForChainType = React.useMemo(() => {
|
|
@@ -30695,9 +31291,6 @@ const useNativeBalance = (chain) => {
|
|
|
30695
31291
|
case squidTypes.ChainType.CANTON:
|
|
30696
31292
|
balance = nativeCantonBalance;
|
|
30697
31293
|
break;
|
|
30698
|
-
case squidTypes.ChainType.DANAL:
|
|
30699
|
-
balance = undefined;
|
|
30700
|
-
break;
|
|
30701
31294
|
}
|
|
30702
31295
|
const balanceFormatted = !!balance
|
|
30703
31296
|
? formatBNToReadable(balance.value, balance.decimals)
|
|
@@ -30737,8 +31330,6 @@ const useNativeBalance = (chain) => {
|
|
|
30737
31330
|
return isStellarLoading;
|
|
30738
31331
|
case squidTypes.ChainType.CANTON:
|
|
30739
31332
|
return isCantonLoading;
|
|
30740
|
-
case squidTypes.ChainType.DANAL:
|
|
30741
|
-
return false;
|
|
30742
31333
|
}
|
|
30743
31334
|
}, [
|
|
30744
31335
|
chain?.chainType,
|
|
@@ -30964,7 +31555,7 @@ function hederaWalletConnect(parameters) {
|
|
|
30964
31555
|
const optionalChains = config.chains.map((x) => x.id);
|
|
30965
31556
|
if (!optionalChains.length)
|
|
30966
31557
|
return;
|
|
30967
|
-
const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-
|
|
31558
|
+
const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-CTKuR0LC.js'); });
|
|
30968
31559
|
const rawProvider = await EthereumProvider.init({
|
|
30969
31560
|
...restParameters,
|
|
30970
31561
|
disableProviderPing: true,
|
|
@@ -31198,8 +31789,6 @@ const useSigner = ({ chain }) => {
|
|
|
31198
31789
|
return isStellarSignerReady;
|
|
31199
31790
|
case squidTypes.ChainType.CANTON:
|
|
31200
31791
|
return isCantonSignerReady;
|
|
31201
|
-
case squidTypes.ChainType.DANAL:
|
|
31202
|
-
return false;
|
|
31203
31792
|
}
|
|
31204
31793
|
}, [
|
|
31205
31794
|
chain?.chainType,
|
|
@@ -31652,12 +32241,8 @@ const useSendTransactionStore = zustand.create((set, get) => ({
|
|
|
31652
32241
|
const useDepositAddressStore = zustand.create((set) => ({
|
|
31653
32242
|
deposit: null,
|
|
31654
32243
|
selectedPaymentMethod: "connectedWallet",
|
|
31655
|
-
userDepositTxId: null,
|
|
31656
32244
|
setDeposit: (data) => {
|
|
31657
|
-
set({ deposit: data
|
|
31658
|
-
},
|
|
31659
|
-
setUserDepositTxId: (txId) => {
|
|
31660
|
-
set({ userDepositTxId: txId });
|
|
32245
|
+
set({ deposit: data });
|
|
31661
32246
|
},
|
|
31662
32247
|
setPaymentMethod: (method) => {
|
|
31663
32248
|
set({ selectedPaymentMethod: method });
|
|
@@ -31669,7 +32254,7 @@ function useDepositAddress(squidRoute) {
|
|
|
31669
32254
|
selectedPaymentMethod: state.selectedPaymentMethod,
|
|
31670
32255
|
depositAddress: state.deposit?.depositAddress,
|
|
31671
32256
|
}));
|
|
31672
|
-
const { setDeposit, setPaymentMethod, deposit
|
|
32257
|
+
const { setDeposit, setPaymentMethod, deposit } = useDepositAddressStore();
|
|
31673
32258
|
const { squid } = useSquidStore();
|
|
31674
32259
|
const { fromChain } = useSwap();
|
|
31675
32260
|
const isAvailableAsPaymentMethod = React.useMemo(() => {
|
|
@@ -31679,13 +32264,11 @@ function useDepositAddress(squidRoute) {
|
|
|
31679
32264
|
CHAIN_IDS.BITCOIN,
|
|
31680
32265
|
CHAIN_IDS.SOLANA,
|
|
31681
32266
|
CHAIN_IDS.CANTON,
|
|
31682
|
-
CHAIN_IDS.DANAL_TESTNET,
|
|
31683
32267
|
];
|
|
31684
32268
|
return chainsSupportingDepositAddress.includes(fromChain.chainId);
|
|
31685
32269
|
}, [fromChain?.chainId]);
|
|
31686
|
-
const routeProvidesDirectDepositAddress =
|
|
31687
|
-
isDepositAddressDirectTransferRoute(squidRoute.transactionRequest)
|
|
31688
|
-
(!!squidRoute && isDanalDepositRoute(squidRoute));
|
|
32270
|
+
const routeProvidesDirectDepositAddress = !!squidRoute?.transactionRequest &&
|
|
32271
|
+
isDepositAddressDirectTransferRoute(squidRoute.transactionRequest);
|
|
31689
32272
|
const swapWillGenerateDepositAddress = isChainflipDepositRoute(squidRoute) || routeProvidesDirectDepositAddress;
|
|
31690
32273
|
const paymentMethod = isAvailableAsPaymentMethod
|
|
31691
32274
|
? selectedPaymentMethod
|
|
@@ -31711,19 +32294,6 @@ function useDepositAddress(squidRoute) {
|
|
|
31711
32294
|
});
|
|
31712
32295
|
return;
|
|
31713
32296
|
}
|
|
31714
|
-
else if (isDanalDepositRoute(route)) {
|
|
31715
|
-
const transferBody = parseDanalTransferBody(route.transactionRequest.data);
|
|
31716
|
-
if (!transferBody) {
|
|
31717
|
-
throw new Error("Could not parse Danal transfer request body");
|
|
31718
|
-
}
|
|
31719
|
-
setDeposit({
|
|
31720
|
-
amount: transferBody.amount,
|
|
31721
|
-
depositAddress: route.transactionRequest.target,
|
|
31722
|
-
statusTrackingId: transferBody.memo,
|
|
31723
|
-
memo: transferBody.memo,
|
|
31724
|
-
});
|
|
31725
|
-
return;
|
|
31726
|
-
}
|
|
31727
32297
|
else if (isDepositAddressDirectTransferRoute(route.transactionRequest)) {
|
|
31728
32298
|
// Canton case
|
|
31729
32299
|
const orderHash = route.transactionRequest.data;
|
|
@@ -31746,8 +32316,6 @@ function useDepositAddress(squidRoute) {
|
|
|
31746
32316
|
depositAddress,
|
|
31747
32317
|
closeDepositChannel,
|
|
31748
32318
|
depositData: deposit,
|
|
31749
|
-
userDepositTxId,
|
|
31750
|
-
setUserDepositTxId,
|
|
31751
32319
|
};
|
|
31752
32320
|
}
|
|
31753
32321
|
|
|
@@ -31775,7 +32343,7 @@ const useUrlSwapParams = () => {
|
|
|
31775
32343
|
|
|
31776
32344
|
const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, queryOptions, }) => {
|
|
31777
32345
|
const { evmChains, cosmosChains, suiChains, xrplChains, stellarChains } = useSquidChains(direction);
|
|
31778
|
-
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens,
|
|
32346
|
+
const { evmTokens, cosmosTokens, solanaTokens, bitcoinTokens, suiTokens, xrplTokens, stellarTokens, cantonTokens, } = useSquidTokens(direction);
|
|
31779
32347
|
const { keplrTypeWallet } = useCosmosContext();
|
|
31780
32348
|
const cantonConnector = useWalletStore((store) => store.connectedWalletsByChainType[squidTypes.ChainType.CANTON]?.wallet?.connector);
|
|
31781
32349
|
const placeholderData = React.useMemo(() => {
|
|
@@ -31788,7 +32356,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
|
|
|
31788
32356
|
[squidTypes.ChainType.XRPL]: xrplTokens.map((t) => ({ ...t, balance: "0" })),
|
|
31789
32357
|
[squidTypes.ChainType.STELLAR]: stellarTokens.map((t) => ({ ...t, balance: "0" })),
|
|
31790
32358
|
[squidTypes.ChainType.CANTON]: cantonTokens.map((t) => ({ ...t, balance: "0" })),
|
|
31791
|
-
[squidTypes.ChainType.DANAL]: danalTokens.map((t) => ({ ...t, balance: "0" })),
|
|
31792
32359
|
};
|
|
31793
32360
|
if (!chainType) {
|
|
31794
32361
|
// Return all tokens with zero balance
|
|
@@ -31811,7 +32378,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
|
|
|
31811
32378
|
xrplTokens,
|
|
31812
32379
|
stellarTokens,
|
|
31813
32380
|
cantonTokens,
|
|
31814
|
-
danalTokens,
|
|
31815
32381
|
]);
|
|
31816
32382
|
const isQueryEnabled = React.useMemo(() => {
|
|
31817
32383
|
// Respect the queryOptions.enabled override if provided
|
|
@@ -31836,8 +32402,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
|
|
|
31836
32402
|
return stellarTokens.length > 0;
|
|
31837
32403
|
case squidTypes.ChainType.CANTON:
|
|
31838
32404
|
return cantonTokens.length > 0;
|
|
31839
|
-
case squidTypes.ChainType.DANAL:
|
|
31840
|
-
return danalTokens.length > 0;
|
|
31841
32405
|
}
|
|
31842
32406
|
}, [
|
|
31843
32407
|
chainType,
|
|
@@ -31851,7 +32415,6 @@ const useAllTokensWithBalanceForChainType = ({ chainType, address, direction, qu
|
|
|
31851
32415
|
xrplTokens.length,
|
|
31852
32416
|
stellarTokens.length,
|
|
31853
32417
|
cantonTokens.length,
|
|
31854
|
-
danalTokens.length,
|
|
31855
32418
|
]);
|
|
31856
32419
|
const query = reactQuery.useQuery(keys().allTokensBalance(address, chainType, direction), async () => {
|
|
31857
32420
|
// Return zero balances if no address
|
|
@@ -31976,12 +32539,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
|
|
|
31976
32539
|
direction,
|
|
31977
32540
|
queryOptions,
|
|
31978
32541
|
});
|
|
31979
|
-
const danalBalancesQuery = useAllTokensWithBalanceForChainType({
|
|
31980
|
-
chainType: squidTypes.ChainType.DANAL,
|
|
31981
|
-
address: connectedAddresses?.[squidTypes.ChainType.DANAL],
|
|
31982
|
-
direction,
|
|
31983
|
-
queryOptions,
|
|
31984
|
-
});
|
|
31985
32542
|
// Create a map of chain type to balance query results
|
|
31986
32543
|
const balanceQueries = React.useMemo(() => ({
|
|
31987
32544
|
[squidTypes.ChainType.EVM]: evmBalancesQuery,
|
|
@@ -31992,7 +32549,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
|
|
|
31992
32549
|
[squidTypes.ChainType.XRPL]: xrplBalancesQuery,
|
|
31993
32550
|
[squidTypes.ChainType.STELLAR]: stellarBalancesQuery,
|
|
31994
32551
|
[squidTypes.ChainType.CANTON]: cantonBalancesQuery,
|
|
31995
|
-
[squidTypes.ChainType.DANAL]: danalBalancesQuery,
|
|
31996
32552
|
}), [
|
|
31997
32553
|
evmBalancesQuery,
|
|
31998
32554
|
cosmosBalancesQuery,
|
|
@@ -32002,7 +32558,6 @@ const useAllConnectedWalletBalances = ({ direction, queryOptions = {
|
|
|
32002
32558
|
xrplBalancesQuery,
|
|
32003
32559
|
stellarBalancesQuery,
|
|
32004
32560
|
cantonBalancesQuery,
|
|
32005
|
-
danalBalancesQuery,
|
|
32006
32561
|
]);
|
|
32007
32562
|
// Combine all tokens from different chains
|
|
32008
32563
|
const allTokens = React.useMemo(() => Object.values(balanceQueries).flatMap((query) => query.data?.tokens ?? []), [balanceQueries]);
|
|
@@ -32110,8 +32665,6 @@ const useMultiChainBalance = ({ chain, token, userAddress, enabled = true, }) =>
|
|
|
32110
32665
|
return stellarBalance;
|
|
32111
32666
|
case squidTypes.ChainType.CANTON:
|
|
32112
32667
|
return cantonBalance;
|
|
32113
|
-
case squidTypes.ChainType.DANAL:
|
|
32114
|
-
return "0";
|
|
32115
32668
|
}
|
|
32116
32669
|
}, [
|
|
32117
32670
|
chain?.chainType,
|
|
@@ -33221,9 +33774,8 @@ function useSendTransaction({ to, amount, token, chain, }) {
|
|
|
33221
33774
|
chain,
|
|
33222
33775
|
});
|
|
33223
33776
|
break;
|
|
33224
|
-
case squidTypes.ChainType.CANTON:
|
|
33225
|
-
|
|
33226
|
-
throw new Error(`Send not implemented for chain type ${chain.chainType}`);
|
|
33777
|
+
case squidTypes.ChainType.CANTON: {
|
|
33778
|
+
throw new Error("Not implemented");
|
|
33227
33779
|
}
|
|
33228
33780
|
}
|
|
33229
33781
|
return {
|
|
@@ -33429,8 +33981,7 @@ async function getSendTransactionStatus({ chain, txHash, }) {
|
|
|
33429
33981
|
chain,
|
|
33430
33982
|
});
|
|
33431
33983
|
case squidTypes.ChainType.CANTON:
|
|
33432
|
-
|
|
33433
|
-
throw new Error(`Send not implemented for chain type ${chain.chainType}`);
|
|
33984
|
+
throw new Error("Not implemented");
|
|
33434
33985
|
}
|
|
33435
33986
|
}
|
|
33436
33987
|
|
|
@@ -37051,342 +37602,6 @@ coin.DecProto = {
|
|
|
37051
37602
|
|
|
37052
37603
|
} (tx));
|
|
37053
37604
|
|
|
37054
|
-
const vParty = (party) => ({ party });
|
|
37055
|
-
const vText = (text) => ({ text });
|
|
37056
|
-
const vNumeric = (numeric) => ({ numeric });
|
|
37057
|
-
const vContractId = (contractId) => ({
|
|
37058
|
-
contractId,
|
|
37059
|
-
});
|
|
37060
|
-
const vList = (elements) => ({
|
|
37061
|
-
list: { elements },
|
|
37062
|
-
});
|
|
37063
|
-
const vRecord = (fields) => ({
|
|
37064
|
-
record: { fields: fields.map(([label, value]) => ({ label, value })) },
|
|
37065
|
-
});
|
|
37066
|
-
const vTextMap = (entries) => ({ textMap: { entries } });
|
|
37067
|
-
const vTimestamp = (isoString) => ({
|
|
37068
|
-
timestamp: String(Math.floor(new Date(isoString).getTime() * 1000)),
|
|
37069
|
-
});
|
|
37070
|
-
function isRecord(value) {
|
|
37071
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37072
|
-
}
|
|
37073
|
-
function normalizeChoiceContextValue(value) {
|
|
37074
|
-
if (typeof value === "string")
|
|
37075
|
-
return value;
|
|
37076
|
-
if (!isRecord(value) || typeof value.tag !== "string")
|
|
37077
|
-
return null;
|
|
37078
|
-
switch (value.tag) {
|
|
37079
|
-
case "AV_Text":
|
|
37080
|
-
case "AV_ContractId":
|
|
37081
|
-
return typeof value.value === "string"
|
|
37082
|
-
? { tag: value.tag, value: value.value }
|
|
37083
|
-
: null;
|
|
37084
|
-
case "AV_Bool":
|
|
37085
|
-
return typeof value.value === "boolean"
|
|
37086
|
-
? { tag: value.tag, value: value.value }
|
|
37087
|
-
: null;
|
|
37088
|
-
case "AV_List":
|
|
37089
|
-
if (!Array.isArray(value.value))
|
|
37090
|
-
return null;
|
|
37091
|
-
return {
|
|
37092
|
-
tag: value.tag,
|
|
37093
|
-
value: value.value.flatMap((entry) => {
|
|
37094
|
-
const normalized = normalizeChoiceContextValue(entry);
|
|
37095
|
-
return normalized ? [normalized] : [];
|
|
37096
|
-
}),
|
|
37097
|
-
};
|
|
37098
|
-
default: {
|
|
37099
|
-
const inner = value.value;
|
|
37100
|
-
if (typeof inner === "string" ||
|
|
37101
|
-
typeof inner === "number" ||
|
|
37102
|
-
typeof inner === "boolean" ||
|
|
37103
|
-
inner === null) {
|
|
37104
|
-
return { tag: value.tag, value: inner };
|
|
37105
|
-
}
|
|
37106
|
-
if (Array.isArray(inner)) {
|
|
37107
|
-
return {
|
|
37108
|
-
tag: value.tag,
|
|
37109
|
-
value: inner.flatMap((entry) => {
|
|
37110
|
-
const normalized = normalizeChoiceContextValue(entry);
|
|
37111
|
-
return normalized ? [normalized] : [];
|
|
37112
|
-
}),
|
|
37113
|
-
};
|
|
37114
|
-
}
|
|
37115
|
-
if (!isRecord(inner))
|
|
37116
|
-
return null;
|
|
37117
|
-
const normalizedObject = {};
|
|
37118
|
-
for (const [key, entry] of Object.entries(inner)) {
|
|
37119
|
-
if (typeof entry !== "string" &&
|
|
37120
|
-
typeof entry !== "number" &&
|
|
37121
|
-
typeof entry !== "boolean" &&
|
|
37122
|
-
entry !== null) {
|
|
37123
|
-
return null;
|
|
37124
|
-
}
|
|
37125
|
-
normalizedObject[key] = entry;
|
|
37126
|
-
}
|
|
37127
|
-
return { tag: value.tag, value: normalizedObject };
|
|
37128
|
-
}
|
|
37129
|
-
}
|
|
37130
|
-
}
|
|
37131
|
-
function stringifyChoiceContextValue(value) {
|
|
37132
|
-
return typeof value === "string" ? value : String(value);
|
|
37133
|
-
}
|
|
37134
|
-
function choiceContextValueToAnyValue(value) {
|
|
37135
|
-
if (typeof value === "string") {
|
|
37136
|
-
return {
|
|
37137
|
-
variant: { constructor: "AV_Text", value: vText(value) },
|
|
37138
|
-
};
|
|
37139
|
-
}
|
|
37140
|
-
switch (value.tag) {
|
|
37141
|
-
case "AV_ContractId":
|
|
37142
|
-
if (typeof value.value !== "string") {
|
|
37143
|
-
throw new TypeError("AV_ContractId values must be strings");
|
|
37144
|
-
}
|
|
37145
|
-
return {
|
|
37146
|
-
variant: { constructor: value.tag, value: vContractId(value.value) },
|
|
37147
|
-
};
|
|
37148
|
-
case "AV_Bool":
|
|
37149
|
-
if (typeof value.value !== "boolean") {
|
|
37150
|
-
throw new TypeError("AV_Bool values must be booleans");
|
|
37151
|
-
}
|
|
37152
|
-
return {
|
|
37153
|
-
variant: { constructor: value.tag, value: { bool: value.value } },
|
|
37154
|
-
};
|
|
37155
|
-
case "AV_List":
|
|
37156
|
-
if (!Array.isArray(value.value)) {
|
|
37157
|
-
throw new TypeError("AV_List values must be arrays");
|
|
37158
|
-
}
|
|
37159
|
-
return {
|
|
37160
|
-
variant: {
|
|
37161
|
-
constructor: value.tag,
|
|
37162
|
-
value: vList(value.value.map(choiceContextValueToAnyValue)),
|
|
37163
|
-
},
|
|
37164
|
-
};
|
|
37165
|
-
default:
|
|
37166
|
-
return {
|
|
37167
|
-
variant: {
|
|
37168
|
-
constructor: value.tag,
|
|
37169
|
-
value: vText(stringifyChoiceContextValue(value.value)),
|
|
37170
|
-
},
|
|
37171
|
-
};
|
|
37172
|
-
}
|
|
37173
|
-
}
|
|
37174
|
-
|
|
37175
|
-
/** Metadata key used by Splice token-standard transfers to carry our Squid orderhash memo. */
|
|
37176
|
-
const CANTON_MEMO_KEY = "splice.lfdecentralizedtrust.org/reason";
|
|
37177
|
-
/** Interface id for the transfer factory contract returned by the token registry. */
|
|
37178
|
-
const TRANSFER_FACTORY_INTERFACE_ID = "#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory";
|
|
37179
|
-
/** Choice exercised on the transfer factory to create the token-standard transfer. */
|
|
37180
|
-
const TRANSFER_FACTORY_CHOICE = "TransferFactory_Transfer";
|
|
37181
|
-
/** CIP-056 (non-Amulet) tokens expose a registrar parameterized by admin party. */
|
|
37182
|
-
const CIP056_REGISTRAR_BASE = "https://api.utilities.digitalasset.com/api/token-standard/v0/registrars";
|
|
37183
|
-
const TRANSFER_REQUESTED_AT_SKEW_MS = 60_000;
|
|
37184
|
-
const TRANSFER_EXECUTE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
37185
|
-
function resolveCantonRegistryUrl(instrument) {
|
|
37186
|
-
if (instrument.id === CANTON_AMULET_INSTRUMENT_ID) {
|
|
37187
|
-
return CANTON_AMULET_REGISTRY_URL;
|
|
37188
|
-
}
|
|
37189
|
-
return `${CIP056_REGISTRAR_BASE}/${encodeURIComponent(instrument.admin)}`;
|
|
37190
|
-
}
|
|
37191
|
-
/**
|
|
37192
|
-
* Build the plain JSON arguments expected by the registry transfer-factory
|
|
37193
|
-
* endpoint before it adds choice context and disclosed contracts.
|
|
37194
|
-
*/
|
|
37195
|
-
function buildTransferChoiceArgs({ sender, receiver, amount, instrument, inputHoldingCids, memo, nowMs, }) {
|
|
37196
|
-
return {
|
|
37197
|
-
expectedAdmin: instrument.admin,
|
|
37198
|
-
transfer: {
|
|
37199
|
-
sender,
|
|
37200
|
-
receiver,
|
|
37201
|
-
amount,
|
|
37202
|
-
instrumentId: { admin: instrument.admin, id: instrument.id },
|
|
37203
|
-
requestedAt: new Date(nowMs - TRANSFER_REQUESTED_AT_SKEW_MS).toISOString(),
|
|
37204
|
-
executeBefore: new Date(nowMs + TRANSFER_EXECUTE_WINDOW_MS).toISOString(),
|
|
37205
|
-
inputHoldingCids,
|
|
37206
|
-
meta: { values: { [CANTON_MEMO_KEY]: memo } },
|
|
37207
|
-
},
|
|
37208
|
-
extraArgs: { context: { values: {} }, meta: { values: {} } },
|
|
37209
|
-
};
|
|
37210
|
-
}
|
|
37211
|
-
/**
|
|
37212
|
-
* Canton's Holding contracts follow Bitcoin-style UTXOs model.
|
|
37213
|
-
*
|
|
37214
|
-
* Pick Holding contract ids covering `amount` for the instrument: prefer a
|
|
37215
|
-
* single exact-amount holding, else accumulate largest-first.
|
|
37216
|
-
*/
|
|
37217
|
-
function selectInputHoldingCids(holdings, instrument, amount) {
|
|
37218
|
-
const spendableHoldings = holdings.filter((h) => !h.locked &&
|
|
37219
|
-
h.instrument.admin === instrument.admin &&
|
|
37220
|
-
h.instrument.id === instrument.id);
|
|
37221
|
-
const target = new BigNumber(amount);
|
|
37222
|
-
const exact = spendableHoldings.find((h) => new BigNumber(h.amount).eq(target));
|
|
37223
|
-
if (exact)
|
|
37224
|
-
return [exact.contractId];
|
|
37225
|
-
const sorted = [...spendableHoldings].sort((a, b) => new BigNumber(b.amount).comparedTo(new BigNumber(a.amount)));
|
|
37226
|
-
const selected = [];
|
|
37227
|
-
let sum = new BigNumber(0);
|
|
37228
|
-
for (const holding of sorted) {
|
|
37229
|
-
selected.push(holding.contractId);
|
|
37230
|
-
sum = sum.plus(holding.amount);
|
|
37231
|
-
if (sum.gte(target))
|
|
37232
|
-
break;
|
|
37233
|
-
}
|
|
37234
|
-
if (sum.lt(target)) {
|
|
37235
|
-
throw new Error("Insufficient Canton holdings to cover transfer amount");
|
|
37236
|
-
}
|
|
37237
|
-
return selected;
|
|
37238
|
-
}
|
|
37239
|
-
async function fetchInputHoldingCids(connector, party, instrument, amount) {
|
|
37240
|
-
const ledgerEnd = await connector.ledgerApi({
|
|
37241
|
-
requestMethod: "get",
|
|
37242
|
-
resource: "/v2/state/ledger-end",
|
|
37243
|
-
});
|
|
37244
|
-
const activeContracts = await connector.ledgerApi({
|
|
37245
|
-
requestMethod: "post",
|
|
37246
|
-
resource: "/v2/state/active-contracts",
|
|
37247
|
-
body: buildHoldingsAcsRequestBody(party, String(ledgerEnd.offset)),
|
|
37248
|
-
});
|
|
37249
|
-
return selectInputHoldingCids(parseCantonHoldings(activeContracts), instrument, amount);
|
|
37250
|
-
}
|
|
37251
|
-
/**
|
|
37252
|
-
* Ask the token registry for the transfer factory contract and extra context
|
|
37253
|
-
* required to exercise its transfer choice.
|
|
37254
|
-
*/
|
|
37255
|
-
async function fetchTransferFactory(registryUrl, choiceArgs) {
|
|
37256
|
-
const response = await fetch(`${registryUrl}/registry/transfer-instruction/v1/transfer-factory`, {
|
|
37257
|
-
method: "POST",
|
|
37258
|
-
headers: { "Content-Type": "application/json" },
|
|
37259
|
-
body: JSON.stringify({
|
|
37260
|
-
choiceArguments: choiceArgs,
|
|
37261
|
-
excludeDebugFields: true,
|
|
37262
|
-
}),
|
|
37263
|
-
});
|
|
37264
|
-
if (!response.ok) {
|
|
37265
|
-
throw new Error(`Canton transfer-factory request failed (${response.status})`);
|
|
37266
|
-
}
|
|
37267
|
-
return response.json();
|
|
37268
|
-
}
|
|
37269
|
-
/**
|
|
37270
|
-
* Convert transfer choice args into strict Daml Value JSON for the wallet's
|
|
37271
|
-
* prepareExecute ExerciseCommand.
|
|
37272
|
-
*/
|
|
37273
|
-
function buildPrepareChoiceArgument(choiceArgs) {
|
|
37274
|
-
const contextValues = Object.entries(choiceArgs.extraArgs.context?.values ?? {})
|
|
37275
|
-
.map(([key, value]) => {
|
|
37276
|
-
const normalized = normalizeChoiceContextValue(value);
|
|
37277
|
-
return normalized
|
|
37278
|
-
? { key, value: choiceContextValueToAnyValue(normalized) }
|
|
37279
|
-
: undefined;
|
|
37280
|
-
})
|
|
37281
|
-
.filter((entry) => Boolean(entry));
|
|
37282
|
-
return vRecord([
|
|
37283
|
-
["expectedAdmin", vParty(choiceArgs.expectedAdmin)],
|
|
37284
|
-
[
|
|
37285
|
-
"transfer",
|
|
37286
|
-
vRecord([
|
|
37287
|
-
["sender", vParty(choiceArgs.transfer.sender)],
|
|
37288
|
-
["receiver", vParty(choiceArgs.transfer.receiver)],
|
|
37289
|
-
["amount", vNumeric(choiceArgs.transfer.amount)],
|
|
37290
|
-
[
|
|
37291
|
-
"instrumentId",
|
|
37292
|
-
vRecord([
|
|
37293
|
-
["admin", vParty(choiceArgs.transfer.instrumentId.admin)],
|
|
37294
|
-
["id", vText(choiceArgs.transfer.instrumentId.id)],
|
|
37295
|
-
]),
|
|
37296
|
-
],
|
|
37297
|
-
["requestedAt", vTimestamp(choiceArgs.transfer.requestedAt)],
|
|
37298
|
-
["executeBefore", vTimestamp(choiceArgs.transfer.executeBefore)],
|
|
37299
|
-
[
|
|
37300
|
-
"inputHoldingCids",
|
|
37301
|
-
vList(choiceArgs.transfer.inputHoldingCids.map(vContractId)),
|
|
37302
|
-
],
|
|
37303
|
-
[
|
|
37304
|
-
"meta",
|
|
37305
|
-
vRecord([
|
|
37306
|
-
[
|
|
37307
|
-
"values",
|
|
37308
|
-
vTextMap(Object.entries(choiceArgs.transfer.meta.values).map(([key, value]) => ({ key, value: vText(value) }))),
|
|
37309
|
-
],
|
|
37310
|
-
]),
|
|
37311
|
-
],
|
|
37312
|
-
]),
|
|
37313
|
-
],
|
|
37314
|
-
[
|
|
37315
|
-
"extraArgs",
|
|
37316
|
-
vRecord([
|
|
37317
|
-
["context", vRecord([["values", vTextMap(contextValues)]])],
|
|
37318
|
-
["meta", vRecord([["values", vTextMap([])]])],
|
|
37319
|
-
]),
|
|
37320
|
-
],
|
|
37321
|
-
]);
|
|
37322
|
-
}
|
|
37323
|
-
/**
|
|
37324
|
-
* Convert a registry disclosed contract into the wallet submission shape,
|
|
37325
|
-
* dropping debug fields and entries without a usable createdEventBlob.
|
|
37326
|
-
*/
|
|
37327
|
-
function toDisclosedContract(raw) {
|
|
37328
|
-
if (typeof raw.createdEventBlob !== "string")
|
|
37329
|
-
return null;
|
|
37330
|
-
return {
|
|
37331
|
-
createdEventBlob: raw.createdEventBlob,
|
|
37332
|
-
contractId: typeof raw.contractId === "string" ? raw.contractId : undefined,
|
|
37333
|
-
synchronizerId: typeof raw.synchronizerId === "string" ? raw.synchronizerId : undefined,
|
|
37334
|
-
};
|
|
37335
|
-
}
|
|
37336
|
-
/**
|
|
37337
|
-
* Build a Splice token-standard transfer of `amount` of `token` from `sender`
|
|
37338
|
-
* to `receiver`, carrying `memo`, ready to hand to a wallet's prepareExecute.
|
|
37339
|
-
* Returns the full prepareExecute params (commandId + commands + actAs +
|
|
37340
|
-
* disclosedContracts).
|
|
37341
|
-
*/
|
|
37342
|
-
async function buildCantonTransfer(connector, { sender, receiver, amount, token, memo, nowMs, }) {
|
|
37343
|
-
if (!token.originalAddress) {
|
|
37344
|
-
throw new Error("Canton token originalAddress is required");
|
|
37345
|
-
}
|
|
37346
|
-
const instrument = parseCantonInstrument(token.originalAddress);
|
|
37347
|
-
if (!instrument) {
|
|
37348
|
-
throw new Error(`Invalid Canton token address: ${token.originalAddress}`);
|
|
37349
|
-
}
|
|
37350
|
-
const registryUrl = resolveCantonRegistryUrl(instrument);
|
|
37351
|
-
const inputHoldingCids = await fetchInputHoldingCids(connector, sender, instrument, amount);
|
|
37352
|
-
const choiceArgs = buildTransferChoiceArgs({
|
|
37353
|
-
sender,
|
|
37354
|
-
receiver,
|
|
37355
|
-
amount,
|
|
37356
|
-
instrument,
|
|
37357
|
-
inputHoldingCids,
|
|
37358
|
-
memo,
|
|
37359
|
-
nowMs,
|
|
37360
|
-
});
|
|
37361
|
-
const factory = await fetchTransferFactory(registryUrl, choiceArgs);
|
|
37362
|
-
const choiceContextData = factory.choiceContext.choiceContextData;
|
|
37363
|
-
if (!choiceContextData) {
|
|
37364
|
-
throw new Error("Transfer factory choice context is missing");
|
|
37365
|
-
}
|
|
37366
|
-
choiceArgs.extraArgs.context = choiceContextData;
|
|
37367
|
-
const disclosedContracts = (factory.choiceContext?.disclosedContracts ?? []).flatMap((raw) => {
|
|
37368
|
-
const disclosedContract = toDisclosedContract(raw);
|
|
37369
|
-
return disclosedContract ? [disclosedContract] : [];
|
|
37370
|
-
});
|
|
37371
|
-
// Command ID: client-supplied identifier used to deduplicate submissions
|
|
37372
|
-
const commandId = `canton-${memo}`;
|
|
37373
|
-
return {
|
|
37374
|
-
commandId,
|
|
37375
|
-
commands: [
|
|
37376
|
-
{
|
|
37377
|
-
ExerciseCommand: {
|
|
37378
|
-
templateId: TRANSFER_FACTORY_INTERFACE_ID,
|
|
37379
|
-
contractId: factory.factoryId,
|
|
37380
|
-
choice: TRANSFER_FACTORY_CHOICE,
|
|
37381
|
-
choiceArgument: buildPrepareChoiceArgument(choiceArgs),
|
|
37382
|
-
},
|
|
37383
|
-
},
|
|
37384
|
-
],
|
|
37385
|
-
actAs: [sender],
|
|
37386
|
-
disclosedContracts,
|
|
37387
|
-
};
|
|
37388
|
-
}
|
|
37389
|
-
|
|
37390
37605
|
const useExecuteTransaction = (squidRoute) => {
|
|
37391
37606
|
const { fromChain, toChain, fromToken, toToken, isSameChain } = useSwap();
|
|
37392
37607
|
// A route completes on source tx only if it's same-chain AND
|
|
@@ -38001,7 +38216,7 @@ const useExecuteTransaction = (squidRoute) => {
|
|
|
38001
38216
|
}
|
|
38002
38217
|
const amount = formatBNToReadable(fromAmount, token.decimals);
|
|
38003
38218
|
dispatchSignatureRequestEvent(route);
|
|
38004
|
-
const
|
|
38219
|
+
const result = await cantonSigner.transfer({
|
|
38005
38220
|
sender: sourceUserAddress,
|
|
38006
38221
|
receiver,
|
|
38007
38222
|
amount,
|
|
@@ -38009,15 +38224,14 @@ const useExecuteTransaction = (squidRoute) => {
|
|
|
38009
38224
|
memo: orderHash,
|
|
38010
38225
|
nowMs: Date.now(),
|
|
38011
38226
|
});
|
|
38012
|
-
const
|
|
38013
|
-
const txHash = result.tx?.payload?.updateId ?? "";
|
|
38227
|
+
const txHash = result.updateId;
|
|
38014
38228
|
if (txHash) {
|
|
38015
38229
|
resetQueriesAfterTxSigned();
|
|
38016
38230
|
}
|
|
38017
38231
|
WidgetEvents.getInstance().dispatchSwapExecuteCall(route, txHash);
|
|
38018
38232
|
const txParams = setTransactionState({
|
|
38019
38233
|
route,
|
|
38020
|
-
txHash
|
|
38234
|
+
txHash,
|
|
38021
38235
|
userAddress: sourceUserAddress,
|
|
38022
38236
|
status: exports.TransactionStatus.INITIAL_LOADING,
|
|
38023
38237
|
sourceStatus: exports.TransactionStatus.ONGOING,
|
|
@@ -38300,7 +38514,7 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
|
|
|
38300
38514
|
//
|
|
38301
38515
|
// If deposit address is not selected, we use the connected address as the source address instead
|
|
38302
38516
|
const sourceUserAddress = isDepositAddressActive
|
|
38303
|
-
? depositRefundAddress ?? sourceConnectedAddress
|
|
38517
|
+
? (depositRefundAddress ?? sourceConnectedAddress)
|
|
38304
38518
|
: sourceConnectedAddress;
|
|
38305
38519
|
const squidRouteQueryKeys = React.useMemo(() => keys().transaction(fromChain?.chainId, toChain?.chainId, toToken?.address, fromToken?.address, fromPrice, config.slippage, sourceUserAddress, config.degenMode, destinationAddress, fallbackAddress, quoteOnly, fromChain?.chainType, config.preHook, config.postHook, config.overrideGasRefundAddress), [
|
|
38306
38520
|
fromChain?.chainId,
|
|
@@ -38592,6 +38806,10 @@ const useAutoConnect = () => {
|
|
|
38592
38806
|
const { chains } = useSquidChains();
|
|
38593
38807
|
const { isInitialized } = useConfigStore();
|
|
38594
38808
|
const hasAttemptedReconnect = React.useRef(false);
|
|
38809
|
+
const hasAttemptedCantonReconnect = React.useRef(false);
|
|
38810
|
+
const { autoConnectCanton, wallets: cantonWallets } = useCantonContext();
|
|
38811
|
+
const recentCantonWalletId = useWalletStore((state) => state.recentConnectorIds[squidTypes.ChainType.CANTON]);
|
|
38812
|
+
const hasRecentCantonWallet = cantonWallets.some((wallet) => wallet.connectorId === recentCantonWalletId);
|
|
38595
38813
|
const { autoConnectEvm } = useEvmContext();
|
|
38596
38814
|
const { autoConnectSolana } = useSolanaContext();
|
|
38597
38815
|
const { autoConnectSui } = useSuiContext();
|
|
@@ -38659,6 +38877,15 @@ const useAutoConnect = () => {
|
|
|
38659
38877
|
React.useEffect(() => {
|
|
38660
38878
|
autoConnect();
|
|
38661
38879
|
}, [autoConnect]);
|
|
38880
|
+
React.useEffect(() => {
|
|
38881
|
+
if (!isInitialized ||
|
|
38882
|
+
chains.length === 0 ||
|
|
38883
|
+
!hasRecentCantonWallet ||
|
|
38884
|
+
hasAttemptedCantonReconnect.current)
|
|
38885
|
+
return;
|
|
38886
|
+
hasAttemptedCantonReconnect.current = true;
|
|
38887
|
+
autoConnectCanton().catch(() => undefined);
|
|
38888
|
+
}, [autoConnectCanton, hasRecentCantonWallet, isInitialized, chains.length]);
|
|
38662
38889
|
return { autoConnect };
|
|
38663
38890
|
};
|
|
38664
38891
|
|
|
@@ -39086,9 +39313,6 @@ exports.isChainflipBridgeTransaction = isChainflipBridgeTransaction;
|
|
|
39086
39313
|
exports.isChainflipDepositRoute = isChainflipDepositRoute;
|
|
39087
39314
|
exports.isCoralBridgeAction = isCoralBridgeAction;
|
|
39088
39315
|
exports.isCosmosAddressValid = isCosmosAddressValid;
|
|
39089
|
-
exports.isDanalAddressValid = isDanalAddressValid;
|
|
39090
|
-
exports.isDanalDepositRoute = isDanalDepositRoute;
|
|
39091
|
-
exports.isDanalTxHashValid = isDanalTxHashValid;
|
|
39092
39316
|
exports.isDepositAddressDirectTransferRoute = isDepositAddressDirectTransferRoute;
|
|
39093
39317
|
exports.isEmptyObject = isEmptyObject;
|
|
39094
39318
|
exports.isEvmChainNotSupportedError = isEvmChainNotSupportedError;
|
|
@@ -39126,7 +39350,6 @@ exports.nativeXrplTokenAddress = nativeXrplTokenAddress;
|
|
|
39126
39350
|
exports.normalizeError = normalizeError;
|
|
39127
39351
|
exports.normalizeIbcAddress = normalizeIbcAddress;
|
|
39128
39352
|
exports.normalizeTokenSymbol = normalizeTokenSymbol;
|
|
39129
|
-
exports.parseDanalTransferBody = parseDanalTransferBody;
|
|
39130
39353
|
exports.parseEvmAddress = parseEvmAddress;
|
|
39131
39354
|
exports.parseInitialAssetsFromUrl = parseInitialAssetsFromUrl;
|
|
39132
39355
|
exports.parseToBigInt = parseToBigInt;
|
|
@@ -39239,4 +39462,4 @@ exports.useXrplTrustLine = useXrplTrustLine;
|
|
|
39239
39462
|
exports.waitForReceiptWithRetry = waitForReceiptWithRetry;
|
|
39240
39463
|
exports.walletIconBaseUrl = walletIconBaseUrl;
|
|
39241
39464
|
exports.walletSupportsChainType = walletSupportsChainType;
|
|
39242
|
-
//# sourceMappingURL=index-
|
|
39465
|
+
//# sourceMappingURL=index-CJVX-7hj.js.map
|