@cloak.dev/sdk 0.2.2 → 0.2.3-staging.16d1078
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/README.md +17 -11
- package/dist/index.cjs +1203 -31
- package/dist/index.d.cts +919 -2
- package/dist/index.d.ts +919 -2
- package/dist/index.js +1179 -30
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -542,8 +542,10 @@ var init_utxo = __esm({
|
|
|
542
542
|
// src/index.ts
|
|
543
543
|
var index_exports = {};
|
|
544
544
|
__export(index_exports, {
|
|
545
|
+
BRIDGE_ESCROW_LABEL: () => BRIDGE_ESCROW_LABEL,
|
|
545
546
|
BUILD_ALLOWS_LOCAL_ENDPOINTS: () => BUILD_ALLOWS_LOCAL_ENDPOINTS,
|
|
546
547
|
CHAIN_NOTE_SALT_BITS: () => CHAIN_NOTE_SALT_BITS,
|
|
548
|
+
CLEANUP_FEE_BUDGET: () => CLEANUP_FEE_BUDGET,
|
|
547
549
|
CLOAK_PRODUCTION_RELAY_URL: () => CLOAK_PRODUCTION_RELAY_URL,
|
|
548
550
|
CLOAK_PROGRAM_ID: () => CLOAK_PROGRAM_ID,
|
|
549
551
|
CloakError: () => CloakError,
|
|
@@ -551,25 +553,38 @@ __export(index_exports, {
|
|
|
551
553
|
DEFAULT_TRANSACTION_CIRCUITS_URL: () => DEFAULT_TRANSACTION_CIRCUITS_URL,
|
|
552
554
|
DELIVERY_MEMO_TAG: () => DELIVERY_MEMO_TAG,
|
|
553
555
|
DELIVERY_REGISTRY_SEED: () => DELIVERY_REGISTRY_SEED,
|
|
556
|
+
DepositError: () => DepositError,
|
|
557
|
+
DoNotRetry: () => DoNotRetry,
|
|
558
|
+
ECONOMIC_MINIMUM: () => ECONOMIC_MINIMUM,
|
|
554
559
|
EXPECTED_CIRCUIT_HASHES: () => EXPECTED_CIRCUIT_HASHES,
|
|
560
|
+
FEE_BUDGET: () => FEE_BUDGET,
|
|
555
561
|
FIXED_FEE_LAMPORTS: () => FIXED_FEE_LAMPORTS,
|
|
556
562
|
InsecureRandomnessError: () => InsecureRandomnessError,
|
|
557
563
|
LAMPORTS_PER_SOL: () => LAMPORTS_PER_SOL,
|
|
558
564
|
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
565
|
+
MAINNET_RENT_0: () => MAINNET_RENT_0,
|
|
566
|
+
MAINNET_RENT_1: () => MAINNET_RENT_1,
|
|
567
|
+
MAX_RECEIVER_INDEX: () => MAX_RECEIVER_INDEX,
|
|
559
568
|
MERKLE_TREE_HEIGHT: () => MERKLE_TREE_HEIGHT2,
|
|
560
569
|
MIN_DEPOSIT_LAMPORTS: () => MIN_DEPOSIT_LAMPORTS,
|
|
570
|
+
MIN_DEPOSIT_SPL_BASE_UNITS: () => MIN_DEPOSIT_SPL_BASE_UNITS,
|
|
561
571
|
MemoryStorageAdapter: () => MemoryStorageAdapter,
|
|
562
572
|
MerkleTree: () => MerkleTree,
|
|
563
573
|
NATIVE_SOL_MINT: () => NATIVE_SOL_MINT,
|
|
574
|
+
ONECLICK_PUBKEY_B58: () => ONECLICK_PUBKEY_B58,
|
|
564
575
|
RECIPIENT_DELIVERY_CIPHERTEXT_LEN: () => RECIPIENT_DELIVERY_CIPHERTEXT_LEN,
|
|
565
576
|
RECIPIENT_DELIVERY_EPHEMERAL_PK_LEN: () => RECIPIENT_DELIVERY_EPHEMERAL_PK_LEN,
|
|
566
577
|
RECIPIENT_DELIVERY_NONCE_LEN: () => RECIPIENT_DELIVERY_NONCE_LEN,
|
|
567
578
|
RECIPIENT_DELIVERY_NOTE_BYTES: () => RECIPIENT_DELIVERY_NOTE_BYTES,
|
|
568
579
|
RECIPIENT_DELIVERY_PLAINTEXT_LEN: () => RECIPIENT_DELIVERY_PLAINTEXT_LEN,
|
|
569
580
|
RECIPIENT_DELIVERY_TAG_LEN: () => RECIPIENT_DELIVERY_TAG_LEN,
|
|
581
|
+
RELAY_BATCH_AUTH_MAX_ITEMS: () => RELAY_BATCH_AUTH_MAX_ITEMS,
|
|
570
582
|
RELAY_ORIGIN_ALLOWLIST: () => RELAY_ORIGIN_ALLOWLIST,
|
|
583
|
+
REQUEST_AUTH_BATCH_DOMAIN: () => REQUEST_AUTH_BATCH_DOMAIN,
|
|
584
|
+
REQUEST_AUTH_BATCH_MAX_AGE_SECONDS: () => REQUEST_AUTH_BATCH_MAX_AGE_SECONDS,
|
|
571
585
|
REQUEST_AUTH_MAX_AGE_SECONDS: () => REQUEST_AUTH_MAX_AGE_SECONDS,
|
|
572
586
|
REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS: () => REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS,
|
|
587
|
+
RelayBatchAuthCoordinator: () => RelayBatchAuthCoordinator,
|
|
573
588
|
RelayInternalError: () => RelayInternalError,
|
|
574
589
|
RelayService: () => RelayService,
|
|
575
590
|
RootNotFoundError: () => RootNotFoundError,
|
|
@@ -579,6 +594,7 @@ __export(index_exports, {
|
|
|
579
594
|
SettlementVerificationError: () => SettlementVerificationError,
|
|
580
595
|
ShieldPoolErrors: () => ShieldPoolErrors,
|
|
581
596
|
SimpleWallet: () => SimpleWallet,
|
|
597
|
+
TERMINAL_FAILURE: () => TERMINAL_FAILURE,
|
|
582
598
|
TRANSACTION_CIRCUITS_VERSION: () => TRANSACTION_CIRCUITS_VERSION,
|
|
583
599
|
TRANSACT_AUTH_FIELDS: () => TRANSACT_AUTH_FIELDS,
|
|
584
600
|
TRANSACT_SWAP_AUTH_FIELDS: () => TRANSACT_SWAP_AUTH_FIELDS,
|
|
@@ -588,8 +604,12 @@ __export(index_exports, {
|
|
|
588
604
|
VARIABLE_FEE_NUMERATOR: () => VARIABLE_FEE_NUMERATOR,
|
|
589
605
|
VARIABLE_FEE_RATE: () => VARIABLE_FEE_RATE,
|
|
590
606
|
VERSION: () => VERSION,
|
|
607
|
+
WITHDRAW_FEE_BPS: () => WITHDRAW_FEE_BPS,
|
|
608
|
+
WITHDRAW_FIXED_FEE: () => WITHDRAW_FIXED_FEE,
|
|
591
609
|
assertDirectSubmissionLanded: () => assertDirectSubmissionLanded,
|
|
610
|
+
assertInputMints: () => assertInputMints,
|
|
592
611
|
assertTransactionCircuitIntegrity: () => assertTransactionCircuitIntegrity,
|
|
612
|
+
assessBridgeQuote: () => assessBridgeQuote,
|
|
593
613
|
bigintToBytes32: () => bigintToBytes32,
|
|
594
614
|
bigintToHex: () => bigintToHex,
|
|
595
615
|
buildMerkleTree: () => buildMerkleTree,
|
|
@@ -597,6 +617,7 @@ __export(index_exports, {
|
|
|
597
617
|
buildMerkleTreeFromRelay: () => buildMerkleTreeFromRelay,
|
|
598
618
|
buildRecipientDeliveryNotes: () => buildRecipientDeliveryNotes,
|
|
599
619
|
buildRelayAuthPreimage: () => buildRelayAuthPreimage,
|
|
620
|
+
buildRelayBatchAuthMessage: () => buildRelayBatchAuthMessage,
|
|
600
621
|
buildTransactRequestBody: () => buildTransactRequestBody,
|
|
601
622
|
bytesToHex: () => bytesToHex,
|
|
602
623
|
calculateFee: () => calculateFee,
|
|
@@ -604,12 +625,16 @@ __export(index_exports, {
|
|
|
604
625
|
calculateRelayFee: () => calculateRelayFee,
|
|
605
626
|
canRebuildMerkleTreeFromChain: () => canRebuildMerkleTreeFromChain,
|
|
606
627
|
canonicalJson: () => canonicalJson,
|
|
628
|
+
canonicalPayloadString: () => canonicalPayloadString,
|
|
607
629
|
chainNoteFromBase64: () => chainNoteFromBase64,
|
|
608
630
|
chainNoteToBase64: () => chainNoteToBase64,
|
|
631
|
+
classifyPostFailure: () => classifyPostFailure,
|
|
609
632
|
classifyRelayError: () => classifyRelayError,
|
|
633
|
+
cleanupReceivingAddress: () => cleanupReceivingAddress,
|
|
610
634
|
cleanupStalePendingOperations: () => cleanupStalePendingOperations,
|
|
611
635
|
clearPendingDeposits: () => clearPendingDeposits,
|
|
612
636
|
clearPendingWithdrawals: () => clearPendingWithdrawals,
|
|
637
|
+
cloakBridgeRail: () => cloakBridgeRail,
|
|
613
638
|
computeChainNoteHash: () => computeChainNoteHash,
|
|
614
639
|
computeExtDataHash: () => computeExtDataHash,
|
|
615
640
|
computeMerkleRoot: () => computeMerkleRoot,
|
|
@@ -626,15 +651,19 @@ __export(index_exports, {
|
|
|
626
651
|
createLogger: () => createLogger,
|
|
627
652
|
createRecoverableChangeUtxo: () => createRecoverableChangeUtxo,
|
|
628
653
|
createRecoverableDepositUtxo: () => createRecoverableDepositUtxo,
|
|
654
|
+
createRelayBatchAuthCoordinator: () => createRelayBatchAuthCoordinator,
|
|
629
655
|
createUtxo: () => createUtxo,
|
|
630
656
|
createZeroUtxo: () => createZeroUtxo,
|
|
631
657
|
decryptCompactChainNote: () => decryptCompactChainNote,
|
|
632
658
|
decryptComplianceMetadataWithMasterKey: () => decryptComplianceMetadataWithMasterKey,
|
|
633
659
|
decryptTransactionMetadata: () => decryptTransactionMetadata,
|
|
660
|
+
depositFromDerivedKey: () => depositFromDerivedKey,
|
|
661
|
+
deriveBridgeReceiver: () => deriveBridgeReceiver,
|
|
634
662
|
deriveChangeNoteBlinding: () => deriveChangeNoteBlinding,
|
|
635
663
|
deriveDepositNoteSecrets: () => deriveDepositNoteSecrets,
|
|
636
664
|
deriveDiversifiedViewingKey: () => deriveDiversifiedViewingKey,
|
|
637
665
|
deriveDiversifier: () => deriveDiversifier,
|
|
666
|
+
deriveFundingTargets: () => deriveFundingTargets,
|
|
638
667
|
deriveInputNullifierPdas: () => deriveInputNullifierPdas,
|
|
639
668
|
derivePublicKey: () => derivePublicKey,
|
|
640
669
|
deriveSpendKey: () => deriveSpendKey,
|
|
@@ -673,6 +702,7 @@ __export(index_exports, {
|
|
|
673
702
|
formatErrorForLogging: () => formatErrorForLogging,
|
|
674
703
|
formatSol: () => formatSol,
|
|
675
704
|
fullWithdraw: () => fullWithdraw,
|
|
705
|
+
fundReceiverViaPaymaster: () => fundReceiverViaPaymaster,
|
|
676
706
|
generateCloakKeys: () => generateCloakKeys,
|
|
677
707
|
generateCommitmentAsync: () => generateCommitmentAsync,
|
|
678
708
|
generateMasterSeed: () => generateMasterSeed,
|
|
@@ -708,18 +738,21 @@ __export(index_exports, {
|
|
|
708
738
|
isReactNative: () => isReactNative,
|
|
709
739
|
isRootNotFoundError: () => isRootNotFoundError,
|
|
710
740
|
isSubmissionOutcomeUnknownResponse: () => isSubmissionOutcomeUnknownResponse,
|
|
741
|
+
isTerminalFailure: () => isTerminalFailure,
|
|
711
742
|
isValidHex: () => isValidHex,
|
|
712
743
|
isValidRpcUrl: () => isValidRpcUrl,
|
|
713
744
|
isValidSolanaAddress: () => isValidSolanaAddress,
|
|
714
745
|
isWithdrawAmountSufficient: () => isWithdrawAmountSufficient,
|
|
715
746
|
isWithdrawable: () => isWithdrawable,
|
|
716
747
|
keypairToAdapter: () => keypairToAdapter,
|
|
748
|
+
listBridgeDeposits: () => listBridgeDeposits,
|
|
717
749
|
loadPendingDeposits: () => loadPendingDeposits,
|
|
718
750
|
loadPendingWithdrawals: () => loadPendingWithdrawals,
|
|
719
751
|
loadVerifiedCircuitArtifacts: () => loadVerifiedCircuitArtifacts,
|
|
720
752
|
matchChangeNote: () => matchChangeNote,
|
|
721
753
|
matchDepositNote: () => matchDepositNote,
|
|
722
754
|
matchSwapRefundLeaf: () => matchSwapRefundLeaf,
|
|
755
|
+
mayRetry: () => mayRetry,
|
|
723
756
|
openRecipientDeliveryNote: () => openRecipientDeliveryNote,
|
|
724
757
|
parseAmount: () => parseAmount,
|
|
725
758
|
parseDeliveryCarrierMemo: () => parseDeliveryCarrierMemo,
|
|
@@ -742,11 +775,15 @@ __export(index_exports, {
|
|
|
742
775
|
randomDepositNoteSalt: () => randomDepositNoteSalt,
|
|
743
776
|
randomFieldElement: () => randomFieldElement,
|
|
744
777
|
readMerkleTreeState: () => readMerkleTreeState,
|
|
778
|
+
readRent: () => readRent,
|
|
745
779
|
recipientDeliveryNoteToBase64: () => recipientDeliveryNoteToBase64,
|
|
746
780
|
registerViewingKey: () => registerViewingKey,
|
|
781
|
+
relayRequestDigestHex: () => relayRequestDigestHex,
|
|
747
782
|
removePendingDeposit: () => removePendingDeposit,
|
|
748
783
|
removePendingWithdrawal: () => removePendingWithdrawal,
|
|
784
|
+
renderAssessment: () => renderAssessment,
|
|
749
785
|
resolveCircuitsBase: () => resolveCircuitsBase,
|
|
786
|
+
retryDelayMs: () => retryDelayMs,
|
|
750
787
|
savePendingDeposit: () => savePendingDeposit,
|
|
751
788
|
savePendingWithdrawal: () => savePendingWithdrawal,
|
|
752
789
|
scanNotesForWallet: () => scanNotesForWallet,
|
|
@@ -767,6 +804,7 @@ __export(index_exports, {
|
|
|
767
804
|
swapWithChange: () => swapWithChange,
|
|
768
805
|
toComplianceReport: () => toComplianceReport,
|
|
769
806
|
transact: () => transact,
|
|
807
|
+
transactBatch: () => transactBatch,
|
|
770
808
|
transfer: () => transfer,
|
|
771
809
|
truncate: () => truncate,
|
|
772
810
|
tryDecryptNote: () => tryDecryptNote,
|
|
@@ -779,15 +817,19 @@ __export(index_exports, {
|
|
|
779
817
|
validateDepositParams: () => validateDepositParams,
|
|
780
818
|
validateNote: () => validateNote,
|
|
781
819
|
validateOutputsSum: () => validateOutputsSum,
|
|
820
|
+
validatePaymasterTopUpTransaction: () => validatePaymasterTopUpTransaction,
|
|
782
821
|
validateRoot: () => validateRoot,
|
|
783
822
|
validateTransfers: () => validateTransfers,
|
|
784
823
|
validateWalletConnected: () => validateWalletConnected,
|
|
785
824
|
validateWithdrawableNote: () => validateWithdrawableNote,
|
|
786
825
|
verifyAllCircuits: () => verifyAllCircuits,
|
|
787
826
|
verifyCircuitIntegrity: () => verifyCircuitIntegrity,
|
|
827
|
+
verifyQuoteSignature: () => verifyQuoteSignature,
|
|
788
828
|
verifyUtxos: () => verifyUtxos,
|
|
789
829
|
waitForRoot: () => waitForRoot,
|
|
790
|
-
|
|
830
|
+
withShieldRetries: () => withShieldRetries,
|
|
831
|
+
withTiming: () => withTiming,
|
|
832
|
+
withdrawFeeFor: () => withdrawFeeFor
|
|
791
833
|
});
|
|
792
834
|
module.exports = __toCommonJS(index_exports);
|
|
793
835
|
|
|
@@ -1391,7 +1433,7 @@ function parseStrictViewingKeyChallenge(response, userPubkey, viewingKeyHex) {
|
|
|
1391
1433
|
|
|
1392
1434
|
// src/config/relay.ts
|
|
1393
1435
|
var CLOAK_PRODUCTION_RELAY_URL = "https://api.cloak.ag";
|
|
1394
|
-
var RELAY_ORIGIN_ALLOWLIST = [
|
|
1436
|
+
var RELAY_ORIGIN_ALLOWLIST = ["https://staging-api.cloak.ag"];
|
|
1395
1437
|
var ABSOLUTE_URL = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
1396
1438
|
var LOOPBACK_IPV4 = /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
|
1397
1439
|
var THIS_NETWORK_IPV4 = /^0\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
|
@@ -2929,8 +2971,8 @@ async function verifyUtxos(utxos, connection, programId, commitment = "confirmed
|
|
|
2929
2971
|
const nullifierHex = nullifierBig.toString(16).padStart(64, "0");
|
|
2930
2972
|
const nullifierBytes = Buffer.from(nullifierHex, "hex");
|
|
2931
2973
|
const { pool } = getShieldPoolPDAs(programId, utxo.mintAddress);
|
|
2932
|
-
const [
|
|
2933
|
-
checkable.push({ utxo, pda });
|
|
2974
|
+
const [pda2] = getNullifierPDA(pool, nullifierBytes, programId);
|
|
2975
|
+
checkable.push({ utxo, pda: pda2 });
|
|
2934
2976
|
} catch {
|
|
2935
2977
|
skipped.push(utxo);
|
|
2936
2978
|
}
|
|
@@ -3000,8 +3042,8 @@ function deriveInputNullifierPdas(programId, mint, inputNullifiers) {
|
|
|
3000
3042
|
const pdas = [];
|
|
3001
3043
|
for (const nullifier of inputNullifiers) {
|
|
3002
3044
|
if (nullifier === BigInt(0)) continue;
|
|
3003
|
-
const [
|
|
3004
|
-
pdas.push(
|
|
3045
|
+
const [pda2] = getNullifierPDA(pool, bigintToBytes324(nullifier), programId);
|
|
3046
|
+
pdas.push(pda2);
|
|
3005
3047
|
}
|
|
3006
3048
|
return pdas;
|
|
3007
3049
|
}
|
|
@@ -4320,7 +4362,7 @@ async function fetchWithRetry(url, options = {}) {
|
|
|
4320
4362
|
const {
|
|
4321
4363
|
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
4322
4364
|
maxRetries = DEFAULT_MAX_RETRIES,
|
|
4323
|
-
retryDelayMs = 1e3
|
|
4365
|
+
retryDelayMs: retryDelayMs2 = 1e3
|
|
4324
4366
|
} = options;
|
|
4325
4367
|
assertAllowedRelayOrigin(url);
|
|
4326
4368
|
let lastError = null;
|
|
@@ -4344,7 +4386,7 @@ async function fetchWithRetry(url, options = {}) {
|
|
|
4344
4386
|
`Relay request failed after ${attempt + 1} attempts: ${lastError.message}`
|
|
4345
4387
|
);
|
|
4346
4388
|
}
|
|
4347
|
-
const delay =
|
|
4389
|
+
const delay = retryDelayMs2 * Math.pow(2, attempt) + Math.random() * 500;
|
|
4348
4390
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
4349
4391
|
}
|
|
4350
4392
|
}
|
|
@@ -5546,12 +5588,12 @@ function signRelayRequest(endpoint, programId, body, signer, nowSeconds, fields
|
|
|
5546
5588
|
auth_signature: Buffer.from(signature).toString("base64")
|
|
5547
5589
|
};
|
|
5548
5590
|
}
|
|
5549
|
-
function assertApprovalWithinFreshnessWindow(elapsedMs) {
|
|
5591
|
+
function assertApprovalWithinFreshnessWindow(elapsedMs, maxAgeSeconds = REQUEST_AUTH_MAX_AGE_SECONDS) {
|
|
5550
5592
|
const elapsedSeconds = elapsedMs / 1e3;
|
|
5551
|
-
const budget =
|
|
5593
|
+
const budget = maxAgeSeconds - RELAY_AUTH_APPROVAL_MARGIN_SECONDS;
|
|
5552
5594
|
if (elapsedSeconds <= budget) return;
|
|
5553
5595
|
throw new Error(
|
|
5554
|
-
`The wallet approval took ${Math.round(elapsedSeconds)} seconds, and Cloak requests stay valid for ${
|
|
5596
|
+
`The wallet approval took ${Math.round(elapsedSeconds)} seconds, and Cloak requests stay valid for ${maxAgeSeconds} seconds from the moment they are signed (${RELAY_AUTH_APPROVAL_MARGIN_SECONDS} of those are held back for the request to reach the network). The timestamp is inside the signature, so it cannot be refreshed without your approval again. NOTHING WAS SUBMITTED and no funds moved. Start the same operation again and approve the prompt when it appears.`
|
|
5555
5597
|
);
|
|
5556
5598
|
}
|
|
5557
5599
|
function explainRelayAuthRejection(responseText) {
|
|
@@ -5609,6 +5651,16 @@ async function buildRelayAuthFields(endpoint, programId, body, signers, fields =
|
|
|
5609
5651
|
auth_signature: Buffer.from(signature).toString("base64")
|
|
5610
5652
|
};
|
|
5611
5653
|
}
|
|
5654
|
+
var REQUEST_AUTH_BATCH_DOMAIN = "CLOAK_RELAY_BATCH_AUTH_V1";
|
|
5655
|
+
var REQUEST_AUTH_BATCH_MAX_AGE_SECONDS = 600;
|
|
5656
|
+
var RELAY_BATCH_AUTH_MAX_ITEMS = 64;
|
|
5657
|
+
function relayRequestDigestHex(preimage) {
|
|
5658
|
+
return (0, import_utils.bytesToHex)((0, import_sha23.sha256)(preimage.message));
|
|
5659
|
+
}
|
|
5660
|
+
function buildRelayBatchAuthMessage(programId, issuedAt, digests) {
|
|
5661
|
+
const lines = [REQUEST_AUTH_BATCH_DOMAIN, programId.toBase58(), issuedAt, String(digests.length)];
|
|
5662
|
+
return new TextEncoder().encode([...lines, ...digests].join("\n"));
|
|
5663
|
+
}
|
|
5612
5664
|
|
|
5613
5665
|
// src/flows/transact.ts
|
|
5614
5666
|
init_inputs();
|
|
@@ -5888,6 +5940,7 @@ async function resolveTransactionCircuitFiles() {
|
|
|
5888
5940
|
}
|
|
5889
5941
|
var _transactPaddingSaltCounter = 0;
|
|
5890
5942
|
var _registeredViewingKeys = /* @__PURE__ */ new Set();
|
|
5943
|
+
var _viewingKeyRegistrationsInFlight = /* @__PURE__ */ new Map();
|
|
5891
5944
|
var _resolvedAltAccountsCache = /* @__PURE__ */ new Map();
|
|
5892
5945
|
var _relayMerkleDisabledMints = /* @__PURE__ */ new Set();
|
|
5893
5946
|
var _relayMerkleFallbackNotifiedMints = /* @__PURE__ */ new Set();
|
|
@@ -6220,6 +6273,23 @@ async function computeSwapExtDataHash(outputMint, recipientAta, minOutputAmount,
|
|
|
6220
6273
|
]);
|
|
6221
6274
|
return poseidonHasher.F.toObject(hash);
|
|
6222
6275
|
}
|
|
6276
|
+
function assertInputMints(inputUtxos, expectedMint) {
|
|
6277
|
+
const funded = inputUtxos.filter((u) => u.amount > BigInt(0));
|
|
6278
|
+
if (funded.length === 0) return;
|
|
6279
|
+
const first = funded[0].mintAddress;
|
|
6280
|
+
for (const utxo of funded) {
|
|
6281
|
+
if (!utxo.mintAddress.equals(first)) {
|
|
6282
|
+
throw new Error(
|
|
6283
|
+
`Input notes span more than one mint (${first.toBase58()} and ${utxo.mintAddress.toBase58()}). Every pool holds a single mint, so this cannot be a valid spend of either. Select notes of one mint.`
|
|
6284
|
+
);
|
|
6285
|
+
}
|
|
6286
|
+
}
|
|
6287
|
+
if (expectedMint && !first.equals(expectedMint)) {
|
|
6288
|
+
throw new Error(
|
|
6289
|
+
`Input notes are ${first.toBase58()} but this transaction expects ${expectedMint.toBase58()}. The notes decide which pool is spent, so proceeding would move the wrong asset \u2014 refusing. This usually means note selection ignored the mint.`
|
|
6290
|
+
);
|
|
6291
|
+
}
|
|
6292
|
+
}
|
|
6223
6293
|
function parseNkInput(input) {
|
|
6224
6294
|
if (typeof input !== "string") {
|
|
6225
6295
|
if (input.length !== 32) {
|
|
@@ -6275,6 +6345,26 @@ async function ensureViewingKeyRegistered(options, onProgress, fallbackNk) {
|
|
|
6275
6345
|
if (_registeredViewingKeys.has(cacheKey)) {
|
|
6276
6346
|
return;
|
|
6277
6347
|
}
|
|
6348
|
+
const inFlight = _viewingKeyRegistrationsInFlight.get(cacheKey);
|
|
6349
|
+
if (inFlight) {
|
|
6350
|
+
return inFlight;
|
|
6351
|
+
}
|
|
6352
|
+
const registration = registerViewingKeyOnce(
|
|
6353
|
+
relayUrl,
|
|
6354
|
+
userPubkey,
|
|
6355
|
+
viewingKeyHex,
|
|
6356
|
+
cacheKey,
|
|
6357
|
+
options,
|
|
6358
|
+
onProgress
|
|
6359
|
+
);
|
|
6360
|
+
_viewingKeyRegistrationsInFlight.set(cacheKey, registration);
|
|
6361
|
+
try {
|
|
6362
|
+
await registration;
|
|
6363
|
+
} finally {
|
|
6364
|
+
_viewingKeyRegistrationsInFlight.delete(cacheKey);
|
|
6365
|
+
}
|
|
6366
|
+
}
|
|
6367
|
+
async function registerViewingKeyOnce(relayUrl, userPubkey, viewingKeyHex, cacheKey, options, onProgress) {
|
|
6278
6368
|
const challengeResponse = await relayFetch(`${relayUrl}/viewing-key/challenge`, {
|
|
6279
6369
|
method: "POST",
|
|
6280
6370
|
headers: { "Content-Type": "application/json" },
|
|
@@ -7630,12 +7720,12 @@ async function submitTransactToRelay(args) {
|
|
|
7630
7720
|
const doFetch = args.fetchImpl ?? fetch;
|
|
7631
7721
|
const maxNetworkRetries = args.maxNetworkRetries ?? relayNetworkRetries();
|
|
7632
7722
|
const requestTimeoutMs = args.requestTimeoutMs ?? relayRequestTimeoutMs();
|
|
7633
|
-
if (!depositorKeypair && args.relayAuthSigner) {
|
|
7723
|
+
if (!depositorKeypair && args.relayAuthSigner && !args.relayAuthBatch) {
|
|
7634
7724
|
onProgress?.(
|
|
7635
7725
|
"Approve the request in your wallet. This signs the request for the relay, not a transaction, and it must be approved within a few minutes to stay valid."
|
|
7636
7726
|
);
|
|
7637
7727
|
}
|
|
7638
|
-
const authFields = await buildRelayAuthFields("/transact", programId, requestBody, {
|
|
7728
|
+
const authFields = args.relayAuthBatch ? await args.relayAuthBatch.authorize("/transact", programId, requestBody, TRANSACT_AUTH_FIELDS) : await buildRelayAuthFields("/transact", programId, requestBody, {
|
|
7639
7729
|
depositorKeypair,
|
|
7640
7730
|
relayAuthSigner: args.relayAuthSigner
|
|
7641
7731
|
});
|
|
@@ -7831,7 +7921,7 @@ async function transact(params, options) {
|
|
|
7831
7921
|
// names is refused.
|
|
7832
7922
|
maxRootRetries = 40,
|
|
7833
7923
|
// Increased to handle prolonged relay sync recovery under high concurrency
|
|
7834
|
-
retryDelayMs = 500,
|
|
7924
|
+
retryDelayMs: retryDelayMs2 = 500,
|
|
7835
7925
|
// Start with short delay, will exponentially back off
|
|
7836
7926
|
riskOracleQueue,
|
|
7837
7927
|
riskQuoteUrl: riskQuoteUrlOption,
|
|
@@ -7886,6 +7976,7 @@ async function transact(params, options) {
|
|
|
7886
7976
|
}
|
|
7887
7977
|
}
|
|
7888
7978
|
assertAllowedRpcConnection(connection);
|
|
7979
|
+
assertInputMints(inputUtxos, options.expectedMint);
|
|
7889
7980
|
await ensureViewingKeyRegistered(options, onProgress, fallbackNk);
|
|
7890
7981
|
const resolvedChainNoteNk = explicitChainNoteNk ?? fallbackNk ?? null;
|
|
7891
7982
|
if (inputUtxos.length > 2) {
|
|
@@ -8252,7 +8343,7 @@ async function transact(params, options) {
|
|
|
8252
8343
|
while (submissionAttempts <= maxRootRetries) {
|
|
8253
8344
|
const isRetry = submissionAttempts > 0;
|
|
8254
8345
|
if (isRetry) {
|
|
8255
|
-
const exponentialDelay = Math.min(
|
|
8346
|
+
const exponentialDelay = Math.min(retryDelayMs2 * Math.pow(1.5, submissionAttempts - 1), 5e3);
|
|
8256
8347
|
const jitter = Math.random() * 500;
|
|
8257
8348
|
const totalDelay = Math.floor(exponentialDelay + jitter);
|
|
8258
8349
|
await sleep2(totalDelay);
|
|
@@ -8391,11 +8482,14 @@ async function transact(params, options) {
|
|
|
8391
8482
|
}
|
|
8392
8483
|
let proof;
|
|
8393
8484
|
let publicSignals;
|
|
8485
|
+
const releaseProofSlot = options.relayAuthBatch ? await options.relayAuthBatch.proofSlot() : void 0;
|
|
8394
8486
|
try {
|
|
8395
8487
|
const result = await generateTransactionProof(circuitInputs, onProofProgress);
|
|
8396
8488
|
proof = result.proof;
|
|
8397
8489
|
publicSignals = result.publicSignals;
|
|
8490
|
+
releaseProofSlot?.();
|
|
8398
8491
|
} catch (proofError) {
|
|
8492
|
+
releaseProofSlot?.();
|
|
8399
8493
|
const errorMsg = proofError?.message || String(proofError);
|
|
8400
8494
|
if ((errorMsg.includes("ForceEqual") || errorMsg.includes("Assert Failed")) && submissionAttempts < maxRootRetries) {
|
|
8401
8495
|
onProgress?.(`Merkle proof error (stale sibling data), will regenerate with fresh tree...`);
|
|
@@ -8415,7 +8509,7 @@ async function transact(params, options) {
|
|
|
8415
8509
|
}
|
|
8416
8510
|
treeState = null;
|
|
8417
8511
|
useSiblingInfo = false;
|
|
8418
|
-
const waitMs = Math.min(
|
|
8512
|
+
const waitMs = Math.min(retryDelayMs2 * Math.pow(2, Math.min(submissionAttempts, 4)), 5e3);
|
|
8419
8513
|
if (waitMs > 0) {
|
|
8420
8514
|
onProgress?.(`Waiting ${waitMs}ms for relay sync before retry...`);
|
|
8421
8515
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
@@ -8708,6 +8802,7 @@ async function transact(params, options) {
|
|
|
8708
8802
|
// point where they reach the relay submission instead of stopping at viewing-key
|
|
8709
8803
|
// registration. The keypair still wins when both are present.
|
|
8710
8804
|
relayAuthSigner: relayAuthPlan.kind === "wallet" ? relayAuthPlan.signer : void 0,
|
|
8805
|
+
relayAuthBatch: options.relayAuthBatch,
|
|
8711
8806
|
settlement: {
|
|
8712
8807
|
connection,
|
|
8713
8808
|
programId,
|
|
@@ -9109,7 +9204,7 @@ async function swapUtxo(params, options) {
|
|
|
9109
9204
|
riskQuoteUrl: riskQuoteUrlOption,
|
|
9110
9205
|
maxRootRetries = 40,
|
|
9111
9206
|
// Increased to handle prolonged relay sync recovery under high concurrency
|
|
9112
|
-
retryDelayMs = 500,
|
|
9207
|
+
retryDelayMs: retryDelayMs2 = 500,
|
|
9113
9208
|
// Start with short delay, will exponentially back off
|
|
9114
9209
|
useUniqueNullifiers,
|
|
9115
9210
|
useChainRootForProof = true
|
|
@@ -9128,6 +9223,12 @@ async function swapUtxo(params, options) {
|
|
|
9128
9223
|
if (inputUtxos.length > 2) {
|
|
9129
9224
|
throw new Error("Maximum 2 input UTXOs allowed");
|
|
9130
9225
|
}
|
|
9226
|
+
assertInputMints(inputUtxos, NATIVE_SOL_MINT);
|
|
9227
|
+
if (options.expectedMint && !options.expectedMint.equals(NATIVE_SOL_MINT)) {
|
|
9228
|
+
throw new Error(
|
|
9229
|
+
`swapUtxo expects ${NATIVE_SOL_MINT.toBase58()} input notes (a swap always spends from the wSOL pool), but expectedMint was ${options.expectedMint.toBase58()}. Drop expectedMint, or spend the other mint with transact/transfer instead.`
|
|
9230
|
+
);
|
|
9231
|
+
}
|
|
9131
9232
|
await preflightNullifiers(inputUtxos, connection, programId);
|
|
9132
9233
|
const outputMintAccount = await connection.getAccountInfo(outputMint, {
|
|
9133
9234
|
commitment: "confirmed"
|
|
@@ -9514,7 +9615,7 @@ async function swapUtxo(params, options) {
|
|
|
9514
9615
|
let walletApprovals = 0;
|
|
9515
9616
|
for (let attempt = 0; attempt <= maxRootRetries; attempt++) {
|
|
9516
9617
|
if (attempt > 0) {
|
|
9517
|
-
const exponentialDelay = Math.min(
|
|
9618
|
+
const exponentialDelay = Math.min(retryDelayMs2 * Math.pow(1.5, attempt - 1), 5e3);
|
|
9518
9619
|
const jitter = Math.random() * 500;
|
|
9519
9620
|
const totalDelay = Math.floor(exponentialDelay + jitter);
|
|
9520
9621
|
await sleep2(totalDelay);
|
|
@@ -9652,7 +9753,7 @@ async function swapUtxo(params, options) {
|
|
|
9652
9753
|
}
|
|
9653
9754
|
merkleState = null;
|
|
9654
9755
|
useSiblingInfo = false;
|
|
9655
|
-
const waitMs = Math.min(
|
|
9756
|
+
const waitMs = Math.min(retryDelayMs2 * Math.pow(2, Math.min(attempt, 4)), 5e3);
|
|
9656
9757
|
if (waitMs > 0) {
|
|
9657
9758
|
onProgress?.(`Waiting ${waitMs}ms for relay sync before retry...`);
|
|
9658
9759
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
@@ -10157,9 +10258,272 @@ function cleanupStalePendingOperations(maxAgeMs = 24 * 60 * 60 * 1e3) {
|
|
|
10157
10258
|
// src/index.ts
|
|
10158
10259
|
init_utxo();
|
|
10159
10260
|
|
|
10261
|
+
// src/relay/batch-auth.ts
|
|
10262
|
+
var import_web310 = require("@solana/web3.js");
|
|
10263
|
+
var import_tweetnacl7 = __toESM(require("tweetnacl"), 1);
|
|
10264
|
+
var RelayBatchAuthCoordinator = class {
|
|
10265
|
+
constructor(options) {
|
|
10266
|
+
this.proofSlotsInUse = 0;
|
|
10267
|
+
this.proofQueue = [];
|
|
10268
|
+
this.items = [];
|
|
10269
|
+
this.signing = false;
|
|
10270
|
+
this.slotsInUse = 0;
|
|
10271
|
+
this.ready = [];
|
|
10272
|
+
this.waves = 0;
|
|
10273
|
+
if (!Number.isInteger(options.items) || options.items < 1) {
|
|
10274
|
+
throw new Error(`A batch needs at least one item (got ${options.items}).`);
|
|
10275
|
+
}
|
|
10276
|
+
if (options.items > RELAY_BATCH_AUTH_MAX_ITEMS) {
|
|
10277
|
+
throw new Error(
|
|
10278
|
+
`A batch may cover at most ${RELAY_BATCH_AUTH_MAX_ITEMS} requests (got ${options.items}); the relay refuses longer digest lists. Chunk the payout. The practical bound is lower still: each spend appends two roots to the on-chain ring of 100, so a batch proved against one root evicts it by itself before 50 items.`
|
|
10279
|
+
);
|
|
10280
|
+
}
|
|
10281
|
+
const concurrency = options.submitConcurrency ?? 3;
|
|
10282
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) {
|
|
10283
|
+
throw new Error(`submitConcurrency must be a positive integer (got ${concurrency}).`);
|
|
10284
|
+
}
|
|
10285
|
+
this.signer = options.signer;
|
|
10286
|
+
this.sender = options.signer instanceof import_web310.Keypair ? options.signer.publicKey : options.signer.walletPublicKey;
|
|
10287
|
+
this.expectedItems = options.items;
|
|
10288
|
+
this.submitConcurrency = concurrency;
|
|
10289
|
+
const proofConcurrency = options.proofConcurrency ?? 2;
|
|
10290
|
+
if (!Number.isInteger(proofConcurrency) || proofConcurrency < 1) {
|
|
10291
|
+
throw new Error(`proofConcurrency must be a positive integer (got ${proofConcurrency}).`);
|
|
10292
|
+
}
|
|
10293
|
+
this.proofConcurrency = proofConcurrency;
|
|
10294
|
+
this.onProgress = options.onProgress;
|
|
10295
|
+
}
|
|
10296
|
+
/** The wallet that signs and is every item's authenticated `sender`. */
|
|
10297
|
+
get walletPublicKey() {
|
|
10298
|
+
return this.sender;
|
|
10299
|
+
}
|
|
10300
|
+
/** How many times the signer has been asked so far: one per wave. */
|
|
10301
|
+
get approvals() {
|
|
10302
|
+
return this.waves;
|
|
10303
|
+
}
|
|
10304
|
+
/** Where every item is right now. For progress UIs and for diagnosing a batch that never signs. */
|
|
10305
|
+
snapshot() {
|
|
10306
|
+
const count = (state) => this.items.filter((i) => i.state === state).length;
|
|
10307
|
+
return {
|
|
10308
|
+
building: count("building"),
|
|
10309
|
+
waiting: count("waiting"),
|
|
10310
|
+
submitting: count("submitting"),
|
|
10311
|
+
done: count("done"),
|
|
10312
|
+
proving: this.proofSlotsInUse,
|
|
10313
|
+
proofQueue: this.proofQueue.length,
|
|
10314
|
+
registered: this.items.length,
|
|
10315
|
+
expected: this.expectedItems
|
|
10316
|
+
};
|
|
10317
|
+
}
|
|
10318
|
+
/** Register one item. Throws past `items`: the wave condition would never be reachable. */
|
|
10319
|
+
item() {
|
|
10320
|
+
if (this.items.length >= this.expectedItems) {
|
|
10321
|
+
throw new Error(
|
|
10322
|
+
`This batch was created for ${this.expectedItems} item(s) and all of them are registered.`
|
|
10323
|
+
);
|
|
10324
|
+
}
|
|
10325
|
+
const item = { state: "building", waiting: null, holdsSlot: false };
|
|
10326
|
+
this.items.push(item);
|
|
10327
|
+
return {
|
|
10328
|
+
proofSlot: () => this.acquireProofSlot(),
|
|
10329
|
+
authorize: (endpoint, programId, body, fields) => this.authorize(item, endpoint, programId, body, fields),
|
|
10330
|
+
finish: () => this.finish(item)
|
|
10331
|
+
};
|
|
10332
|
+
}
|
|
10333
|
+
acquireProofSlot() {
|
|
10334
|
+
return new Promise((resolve) => {
|
|
10335
|
+
const grant = () => {
|
|
10336
|
+
this.proofSlotsInUse += 1;
|
|
10337
|
+
let released = false;
|
|
10338
|
+
resolve(() => {
|
|
10339
|
+
if (released) return;
|
|
10340
|
+
released = true;
|
|
10341
|
+
this.proofSlotsInUse -= 1;
|
|
10342
|
+
const next = this.proofQueue.shift();
|
|
10343
|
+
if (next) next();
|
|
10344
|
+
});
|
|
10345
|
+
};
|
|
10346
|
+
if (this.proofSlotsInUse < this.proofConcurrency) grant();
|
|
10347
|
+
else this.proofQueue.push(grant);
|
|
10348
|
+
});
|
|
10349
|
+
}
|
|
10350
|
+
authorize(item, endpoint, programId, body, fields) {
|
|
10351
|
+
if (item.state === "done") {
|
|
10352
|
+
return Promise.reject(new Error("This batch item already finished; it cannot authorize."));
|
|
10353
|
+
}
|
|
10354
|
+
if (item.state === "waiting") {
|
|
10355
|
+
return Promise.reject(new Error("This batch item is already waiting for a signature."));
|
|
10356
|
+
}
|
|
10357
|
+
this.releaseSlot(item);
|
|
10358
|
+
return new Promise((resolve, reject) => {
|
|
10359
|
+
item.state = "waiting";
|
|
10360
|
+
item.waiting = { endpoint, programId, body, fields, resolve, reject };
|
|
10361
|
+
this.maybeSign();
|
|
10362
|
+
});
|
|
10363
|
+
}
|
|
10364
|
+
finish(item) {
|
|
10365
|
+
if (item.state === "done") return;
|
|
10366
|
+
if (item.state === "waiting" && item.waiting) {
|
|
10367
|
+
item.waiting.reject(new Error("Batch item finished before it was signed."));
|
|
10368
|
+
item.waiting = null;
|
|
10369
|
+
}
|
|
10370
|
+
this.releaseSlot(item);
|
|
10371
|
+
item.state = "done";
|
|
10372
|
+
this.maybeSign();
|
|
10373
|
+
this.pump();
|
|
10374
|
+
}
|
|
10375
|
+
releaseSlot(item) {
|
|
10376
|
+
if (!item.holdsSlot) return;
|
|
10377
|
+
item.holdsSlot = false;
|
|
10378
|
+
this.slotsInUse -= 1;
|
|
10379
|
+
}
|
|
10380
|
+
/** Sign when every registered item is either waiting here or finished, and nothing is building. */
|
|
10381
|
+
maybeSign() {
|
|
10382
|
+
if (this.signing) return;
|
|
10383
|
+
if (this.items.length < this.expectedItems) return;
|
|
10384
|
+
if (this.items.some((item) => item.state === "building" || item.state === "submitting")) return;
|
|
10385
|
+
const wave = this.items.filter((item) => item.state === "waiting");
|
|
10386
|
+
if (wave.length === 0) return;
|
|
10387
|
+
this.signing = true;
|
|
10388
|
+
void this.signWave(wave).finally(() => {
|
|
10389
|
+
this.signing = false;
|
|
10390
|
+
this.maybeSign();
|
|
10391
|
+
});
|
|
10392
|
+
}
|
|
10393
|
+
async signWave(wave) {
|
|
10394
|
+
const waiting = wave.map((item) => item.waiting);
|
|
10395
|
+
try {
|
|
10396
|
+
const programId = waiting[0].programId;
|
|
10397
|
+
for (const w of waiting) {
|
|
10398
|
+
if (!w.programId.equals(programId)) {
|
|
10399
|
+
throw new Error("Every item of a batch must target the same program id.");
|
|
10400
|
+
}
|
|
10401
|
+
}
|
|
10402
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
10403
|
+
const preimages = waiting.map(
|
|
10404
|
+
(w) => buildRelayAuthPreimage(w.endpoint, programId, w.body, this.sender, nowSeconds, w.fields)
|
|
10405
|
+
);
|
|
10406
|
+
const digests = preimages.map(relayRequestDigestHex);
|
|
10407
|
+
const issuedAt = preimages[0].auth_issued_at;
|
|
10408
|
+
const message = buildRelayBatchAuthMessage(programId, issuedAt, digests);
|
|
10409
|
+
this.waves += 1;
|
|
10410
|
+
let signature;
|
|
10411
|
+
if (this.signer instanceof import_web310.Keypair) {
|
|
10412
|
+
signature = import_tweetnacl7.default.sign.detached(message, this.signer.secretKey);
|
|
10413
|
+
} else {
|
|
10414
|
+
this.onProgress?.(
|
|
10415
|
+
`Approve ${waiting.length} request(s) in your wallet` + (this.waves > 1 ? ` (approval ${this.waves}, for the rows that had to re-prove)` : "") + `. This signs the requests for the relay, not a transaction, and must be approved within a few minutes to stay valid.`
|
|
10416
|
+
);
|
|
10417
|
+
const approvalStartedMs = Date.now();
|
|
10418
|
+
signature = await this.signer.signMessage(message);
|
|
10419
|
+
assertApprovalWithinFreshnessWindow(
|
|
10420
|
+
Date.now() - approvalStartedMs,
|
|
10421
|
+
REQUEST_AUTH_BATCH_MAX_AGE_SECONDS
|
|
10422
|
+
);
|
|
10423
|
+
if (!(signature instanceof Uint8Array) || signature.length !== 64) {
|
|
10424
|
+
throw new Error(
|
|
10425
|
+
`The wallet's signMessage did not return a 64-byte ed25519 detached signature (got ${signature instanceof Uint8Array ? `${signature.length} bytes` : typeof signature}).`
|
|
10426
|
+
);
|
|
10427
|
+
}
|
|
10428
|
+
}
|
|
10429
|
+
const signatureB64 = Buffer.from(signature).toString("base64");
|
|
10430
|
+
wave.forEach((item, i) => {
|
|
10431
|
+
const preimage = preimages[i];
|
|
10432
|
+
this.ready.push({
|
|
10433
|
+
item,
|
|
10434
|
+
fields: {
|
|
10435
|
+
sender: preimage.sender,
|
|
10436
|
+
auth_issued_at: preimage.auth_issued_at,
|
|
10437
|
+
auth_nonce: preimage.auth_nonce,
|
|
10438
|
+
auth_signature: signatureB64,
|
|
10439
|
+
// A fresh array per item: `Object.assign` puts this on the wire body, and the body is
|
|
10440
|
+
// serialized again on every network retry.
|
|
10441
|
+
auth_batch: { digests: [...digests] }
|
|
10442
|
+
}
|
|
10443
|
+
});
|
|
10444
|
+
});
|
|
10445
|
+
this.pump();
|
|
10446
|
+
} catch (error) {
|
|
10447
|
+
for (const item of wave) {
|
|
10448
|
+
const w = item.waiting;
|
|
10449
|
+
item.waiting = null;
|
|
10450
|
+
item.state = "done";
|
|
10451
|
+
w?.reject(error);
|
|
10452
|
+
}
|
|
10453
|
+
}
|
|
10454
|
+
}
|
|
10455
|
+
/** Release signed items to submit, `submitConcurrency` at a time, in signing order. */
|
|
10456
|
+
pump() {
|
|
10457
|
+
while (this.ready.length > 0 && this.slotsInUse < this.submitConcurrency) {
|
|
10458
|
+
const next = this.ready.shift();
|
|
10459
|
+
const w = next.item.waiting;
|
|
10460
|
+
next.item.waiting = null;
|
|
10461
|
+
if (next.item.state !== "waiting" || !w) continue;
|
|
10462
|
+
next.item.state = "submitting";
|
|
10463
|
+
next.item.holdsSlot = true;
|
|
10464
|
+
this.slotsInUse += 1;
|
|
10465
|
+
w.resolve(next.fields);
|
|
10466
|
+
}
|
|
10467
|
+
}
|
|
10468
|
+
};
|
|
10469
|
+
function createRelayBatchAuthCoordinator(options) {
|
|
10470
|
+
return new RelayBatchAuthCoordinator(options);
|
|
10471
|
+
}
|
|
10472
|
+
|
|
10473
|
+
// src/flows/transact-batch.ts
|
|
10474
|
+
async function transactBatch(items, options) {
|
|
10475
|
+
if (items.length === 0) {
|
|
10476
|
+
return { results: [], approvals: 0 };
|
|
10477
|
+
}
|
|
10478
|
+
if (items.length > RELAY_BATCH_AUTH_MAX_ITEMS) {
|
|
10479
|
+
throw new Error(
|
|
10480
|
+
`transactBatch accepts at most ${RELAY_BATCH_AUTH_MAX_ITEMS} items per call (got ${items.length}). Chunk the payout; see RELAY_BATCH_AUTH_MAX_ITEMS for why the practical bound is lower.`
|
|
10481
|
+
);
|
|
10482
|
+
}
|
|
10483
|
+
if (!options.relayUrl) {
|
|
10484
|
+
throw new Error("transactBatch requires relayUrl: batch approval is a relay authentication scheme.");
|
|
10485
|
+
}
|
|
10486
|
+
const { proofConcurrency, submitConcurrency, ...shared } = options;
|
|
10487
|
+
const signer = shared.depositorKeypair ? shared.depositorKeypair : shared.walletPublicKey && shared.signMessage ? { walletPublicKey: shared.walletPublicKey, signMessage: shared.signMessage } : shared.depositorPublicKey && shared.signMessage ? { walletPublicKey: shared.depositorPublicKey, signMessage: shared.signMessage } : null;
|
|
10488
|
+
if (!signer) {
|
|
10489
|
+
throw new Error(
|
|
10490
|
+
"transactBatch requires an authenticated sender: pass `depositorKeypair` (a local Keypair) or `signMessage` together with `walletPublicKey` (a browser wallet adapter). Checked before proof generation, so nothing has been computed or submitted yet."
|
|
10491
|
+
);
|
|
10492
|
+
}
|
|
10493
|
+
const coordinator = createRelayBatchAuthCoordinator({
|
|
10494
|
+
signer,
|
|
10495
|
+
items: items.length,
|
|
10496
|
+
proofConcurrency,
|
|
10497
|
+
submitConcurrency,
|
|
10498
|
+
onProgress: shared.onProgress
|
|
10499
|
+
});
|
|
10500
|
+
const handles = items.map(() => coordinator.item());
|
|
10501
|
+
const results = new Array(items.length);
|
|
10502
|
+
const runOne = async (index) => {
|
|
10503
|
+
const item = items[index];
|
|
10504
|
+
const handle = handles[index];
|
|
10505
|
+
const label = `[batch ${index + 1}/${items.length}]`;
|
|
10506
|
+
try {
|
|
10507
|
+
const value = await transact(item.params, {
|
|
10508
|
+
...shared,
|
|
10509
|
+
...item.options,
|
|
10510
|
+
relayAuthBatch: handle,
|
|
10511
|
+
onProgress: shared.onProgress ? (status) => shared.onProgress?.(`${label} ${status}`) : void 0
|
|
10512
|
+
});
|
|
10513
|
+
results[index] = { status: "fulfilled", value };
|
|
10514
|
+
} catch (reason) {
|
|
10515
|
+
results[index] = { status: "rejected", reason };
|
|
10516
|
+
} finally {
|
|
10517
|
+
handle.finish();
|
|
10518
|
+
}
|
|
10519
|
+
};
|
|
10520
|
+
await Promise.all(items.map((_, index) => runOne(index)));
|
|
10521
|
+
return { results, approvals: coordinator.approvals };
|
|
10522
|
+
}
|
|
10523
|
+
|
|
10160
10524
|
// src/scanning/scan.ts
|
|
10161
10525
|
var import_bs583 = __toESM(require("bs58"), 1);
|
|
10162
|
-
var
|
|
10526
|
+
var import_web311 = require("@solana/web3.js");
|
|
10163
10527
|
var import_spl_token2 = require("@solana/spl-token");
|
|
10164
10528
|
init_inputs();
|
|
10165
10529
|
init_utxo();
|
|
@@ -10248,7 +10612,7 @@ function parseSwapOutputMint(data) {
|
|
|
10248
10612
|
const mintBytes = data.slice(SWAP_OUTPUT_MINT_OFFSET, end);
|
|
10249
10613
|
if (mintBytes.every((byte) => byte === 0)) return void 0;
|
|
10250
10614
|
try {
|
|
10251
|
-
return new
|
|
10615
|
+
return new import_web311.PublicKey(mintBytes).toBase58();
|
|
10252
10616
|
} catch {
|
|
10253
10617
|
return void 0;
|
|
10254
10618
|
}
|
|
@@ -10338,7 +10702,7 @@ function parseSwapRecipientAta(data) {
|
|
|
10338
10702
|
const recipientAtaBytes = data.slice(recipientAtaStart, recipientAtaEnd);
|
|
10339
10703
|
if (recipientAtaBytes.every((byte) => byte === 0)) return void 0;
|
|
10340
10704
|
try {
|
|
10341
|
-
return new
|
|
10705
|
+
return new import_web311.PublicKey(recipientAtaBytes).toBase58();
|
|
10342
10706
|
} catch {
|
|
10343
10707
|
return void 0;
|
|
10344
10708
|
}
|
|
@@ -10500,7 +10864,7 @@ async function scanSwapNoteCarriers(connection, programId, viewingKeyNk, swapCtx
|
|
|
10500
10864
|
let rpcCalls = 0;
|
|
10501
10865
|
const candidates = Array.from(swapCtxByCommitment.keys());
|
|
10502
10866
|
if (candidates.length === 0) return { records, rpcCalls };
|
|
10503
|
-
const [registry] =
|
|
10867
|
+
const [registry] = import_web311.PublicKey.findProgramAddressSync(
|
|
10504
10868
|
[Buffer.from("cloak_chain_note_registry")],
|
|
10505
10869
|
programId
|
|
10506
10870
|
);
|
|
@@ -10903,8 +11267,8 @@ async function scanTransactions(opts) {
|
|
|
10903
11267
|
if (onChainAta) {
|
|
10904
11268
|
try {
|
|
10905
11269
|
const expectedAta = (0, import_spl_token2.getAssociatedTokenAddressSync)(
|
|
10906
|
-
new
|
|
10907
|
-
new
|
|
11270
|
+
new import_web311.PublicKey(asset.mint),
|
|
11271
|
+
new import_web311.PublicKey(walletPublicKey)
|
|
10908
11272
|
).toBase58();
|
|
10909
11273
|
if (onChainAta === expectedAta) {
|
|
10910
11274
|
isOurs = true;
|
|
@@ -11118,7 +11482,7 @@ async function scanTransactions(opts) {
|
|
|
11118
11482
|
// deposit — that is the public deposit amount. A deposit that also merged inputs
|
|
11119
11483
|
// carries a larger output 0 and is not recoverable from public data alone.
|
|
11120
11484
|
amount: grossAmount,
|
|
11121
|
-
mintAddress: new
|
|
11485
|
+
mintAddress: new import_web311.PublicKey(asset.mint),
|
|
11122
11486
|
outputCommitments: ixCtx.outputCommitments ?? []
|
|
11123
11487
|
});
|
|
11124
11488
|
if (recoveredDeposit) {
|
|
@@ -11144,7 +11508,7 @@ async function scanTransactions(opts) {
|
|
|
11144
11508
|
noteSalt: compactNote.noteSalt,
|
|
11145
11509
|
amount: compactNote.outAmount0,
|
|
11146
11510
|
keypair: { privateKey: 0n, publicKey: compactNote.outPubkey0 },
|
|
11147
|
-
mintAddress: new
|
|
11511
|
+
mintAddress: new import_web311.PublicKey(asset.mint),
|
|
11148
11512
|
outputIndex: 0,
|
|
11149
11513
|
// v4 describes output 0, which is where change lands
|
|
11150
11514
|
outputCommitments: ixCtx.outputCommitments ?? []
|
|
@@ -11337,7 +11701,7 @@ function formatComplianceCsv(report) {
|
|
|
11337
11701
|
}
|
|
11338
11702
|
|
|
11339
11703
|
// src/wallet/utxo-wallet.ts
|
|
11340
|
-
var
|
|
11704
|
+
var import_web312 = require("@solana/web3.js");
|
|
11341
11705
|
init_utxo();
|
|
11342
11706
|
var UtxoWallet = class _UtxoWallet {
|
|
11343
11707
|
constructor(viewingKey) {
|
|
@@ -11550,7 +11914,7 @@ var UtxoWallet = class _UtxoWallet {
|
|
|
11550
11914
|
data.viewingKey ? new Uint8Array(data.viewingKey) : void 0
|
|
11551
11915
|
);
|
|
11552
11916
|
for (const w of data.wallets) {
|
|
11553
|
-
const mint = new
|
|
11917
|
+
const mint = new import_web312.PublicKey(w.mint);
|
|
11554
11918
|
for (const u of w.utxos) {
|
|
11555
11919
|
wallet.addUtxo({
|
|
11556
11920
|
amount: BigInt(u.amount),
|
|
@@ -11632,13 +11996,781 @@ var SimpleWallet = class {
|
|
|
11632
11996
|
}
|
|
11633
11997
|
};
|
|
11634
11998
|
|
|
11999
|
+
// src/bridge/rail-verify.ts
|
|
12000
|
+
var import_tweetnacl8 = __toESM(require("tweetnacl"), 1);
|
|
12001
|
+
var import_sha256 = require("@noble/hashes/sha256");
|
|
12002
|
+
var ONECLICK_PUBKEY_B58 = "reYaWhvwu8Jzo3WUM3zhn6VrhuMEF4eADL17qtRVifc";
|
|
12003
|
+
var b58 = /* @__PURE__ */ (() => {
|
|
12004
|
+
const A = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
12005
|
+
return {
|
|
12006
|
+
decode(s) {
|
|
12007
|
+
let n = 0n;
|
|
12008
|
+
for (const c of s) {
|
|
12009
|
+
const i = A.indexOf(c);
|
|
12010
|
+
if (i < 0) throw new Error("bad base58");
|
|
12011
|
+
n = n * 58n + BigInt(i);
|
|
12012
|
+
}
|
|
12013
|
+
const bytes = [];
|
|
12014
|
+
while (n > 0n) {
|
|
12015
|
+
bytes.unshift(Number(n & 255n));
|
|
12016
|
+
n >>= 8n;
|
|
12017
|
+
}
|
|
12018
|
+
for (const c of s) {
|
|
12019
|
+
if (c === "1") bytes.unshift(0);
|
|
12020
|
+
else break;
|
|
12021
|
+
}
|
|
12022
|
+
return Uint8Array.from(bytes);
|
|
12023
|
+
},
|
|
12024
|
+
encode(b) {
|
|
12025
|
+
let n = 0n;
|
|
12026
|
+
for (const x of b) n = n * 256n + BigInt(x);
|
|
12027
|
+
let s = "";
|
|
12028
|
+
while (n > 0n) {
|
|
12029
|
+
s = A[Number(n % 58n)] + s;
|
|
12030
|
+
n /= 58n;
|
|
12031
|
+
}
|
|
12032
|
+
for (const x of b) {
|
|
12033
|
+
if (x === 0) s = "1" + s;
|
|
12034
|
+
else break;
|
|
12035
|
+
}
|
|
12036
|
+
return s;
|
|
12037
|
+
}
|
|
12038
|
+
};
|
|
12039
|
+
})();
|
|
12040
|
+
function stable(v) {
|
|
12041
|
+
if (v === null || typeof v !== "object") return JSON.stringify(v) ?? "null";
|
|
12042
|
+
if (Array.isArray(v)) return `[${v.map(stable).join(",")}]`;
|
|
12043
|
+
const o = v;
|
|
12044
|
+
const parts = Object.keys(o).sort().filter((k) => o[k] !== void 0).map((k) => `${JSON.stringify(k)}:${stable(o[k])}`);
|
|
12045
|
+
return `{${parts.join(",")}}`;
|
|
12046
|
+
}
|
|
12047
|
+
function signedRequest(r) {
|
|
12048
|
+
const q = r.quoteRequest ?? {};
|
|
12049
|
+
return {
|
|
12050
|
+
dry: q.dry,
|
|
12051
|
+
swapType: q.swapType,
|
|
12052
|
+
slippageTolerance: q.slippageTolerance,
|
|
12053
|
+
originAsset: q.originAsset,
|
|
12054
|
+
depositType: q.depositType,
|
|
12055
|
+
destinationAsset: q.destinationAsset,
|
|
12056
|
+
amount: q.amount,
|
|
12057
|
+
refundTo: q.refundTo,
|
|
12058
|
+
refundType: q.refundType,
|
|
12059
|
+
recipient: q.recipient,
|
|
12060
|
+
recipientType: q.recipientType,
|
|
12061
|
+
deadline: q.deadline,
|
|
12062
|
+
quoteWaitingTimeMs: q.quoteWaitingTimeMs || void 0,
|
|
12063
|
+
referral: q.referral || void 0,
|
|
12064
|
+
virtualChainRecipient: q.virtualChainRecipient || void 0,
|
|
12065
|
+
virtualChainRefundRecipient: q.virtualChainRefundRecipient || void 0,
|
|
12066
|
+
customRecipientMsg: q.customRecipientMsg || void 0
|
|
12067
|
+
// Deliberately unsigned by the rail: sessionId, connectedWallets, correlationId, appFees,
|
|
12068
|
+
// partnerId, userAccountId, depositMode. APP FEES ARE NOT AUTHENTICATED — never display a fee
|
|
12069
|
+
// caption as if the signature vouched for it.
|
|
12070
|
+
};
|
|
12071
|
+
}
|
|
12072
|
+
function signedQuote(r) {
|
|
12073
|
+
const q = r.quote ?? {};
|
|
12074
|
+
const base = {
|
|
12075
|
+
amountIn: q.amountIn,
|
|
12076
|
+
amountInFormatted: q.amountInFormatted,
|
|
12077
|
+
amountInUsd: q.amountInUsd,
|
|
12078
|
+
minAmountIn: q.minAmountIn,
|
|
12079
|
+
amountOut: q.amountOut,
|
|
12080
|
+
amountOutFormatted: q.amountOutFormatted,
|
|
12081
|
+
amountOutUsd: q.amountOutUsd,
|
|
12082
|
+
minAmountOut: q.minAmountOut
|
|
12083
|
+
};
|
|
12084
|
+
if (r.quoteRequest?.dry) return base;
|
|
12085
|
+
return {
|
|
12086
|
+
...base,
|
|
12087
|
+
depositAddress: q.depositAddress || void 0,
|
|
12088
|
+
// <- the field that makes substitution detectable
|
|
12089
|
+
depositMemo: q.depositMemo || void 0,
|
|
12090
|
+
deadline: q.deadline || void 0,
|
|
12091
|
+
// <- a real, SIGNED expiry
|
|
12092
|
+
timeWhenInactive: q.timeWhenInactive || void 0,
|
|
12093
|
+
timeEstimate: q.timeEstimate || void 0,
|
|
12094
|
+
virtualChainRecipient: q.virtualChainRecipient || void 0,
|
|
12095
|
+
virtualChainRefundRecipient: q.virtualChainRefundRecipient || void 0,
|
|
12096
|
+
customRecipientMsg: q.customRecipientMsg || void 0,
|
|
12097
|
+
refundFee: q.refundFee || void 0,
|
|
12098
|
+
withdrawFee: q.withdrawFee || void 0
|
|
12099
|
+
};
|
|
12100
|
+
}
|
|
12101
|
+
function canonicalPayloadString(resp) {
|
|
12102
|
+
return stable({ ...signedRequest(resp), ...signedQuote(resp), timestamp: resp.timestamp });
|
|
12103
|
+
}
|
|
12104
|
+
function verifyQuoteSignature(resp) {
|
|
12105
|
+
if (!resp?.signature) return { valid: false, reason: "no signature on the response" };
|
|
12106
|
+
const payload = { ...signedRequest(resp), ...signedQuote(resp), timestamp: resp.timestamp };
|
|
12107
|
+
const digest = (0, import_sha256.sha256)(new TextEncoder().encode(stable(payload)));
|
|
12108
|
+
const message = new TextEncoder().encode(b58.encode(new Uint8Array(digest)));
|
|
12109
|
+
const sig = resp.signature.replace(/^ed25519:/, "");
|
|
12110
|
+
try {
|
|
12111
|
+
return { valid: import_tweetnacl8.default.sign.detached.verify(message, b58.decode(sig), b58.decode(ONECLICK_PUBKEY_B58)) };
|
|
12112
|
+
} catch (e) {
|
|
12113
|
+
return { valid: false, reason: e instanceof Error ? e.message : String(e) };
|
|
12114
|
+
}
|
|
12115
|
+
}
|
|
12116
|
+
|
|
12117
|
+
// src/bridge/rails.ts
|
|
12118
|
+
async function parseBridgeResponse(res, what) {
|
|
12119
|
+
let body;
|
|
12120
|
+
try {
|
|
12121
|
+
body = await res.json();
|
|
12122
|
+
} catch {
|
|
12123
|
+
throw new Error(`${what} failed: ${res.status} ${res.statusText} (response body was not JSON)`);
|
|
12124
|
+
}
|
|
12125
|
+
if (!res.ok) {
|
|
12126
|
+
const b = body ?? {};
|
|
12127
|
+
throw new Error(
|
|
12128
|
+
`${what} failed: ${b.message ?? res.statusText} (${b.code ?? res.status}${b.retryable ? ", retryable" : ""})`
|
|
12129
|
+
);
|
|
12130
|
+
}
|
|
12131
|
+
return body;
|
|
12132
|
+
}
|
|
12133
|
+
function toAttestation(raw) {
|
|
12134
|
+
if (raw.kind === "ed25519") {
|
|
12135
|
+
return { kind: "ed25519", verified: raw.verified ?? false, signer: raw.signer ?? "" };
|
|
12136
|
+
}
|
|
12137
|
+
return { kind: "none", checks: raw.checks ?? [], note: raw.note ?? "" };
|
|
12138
|
+
}
|
|
12139
|
+
async function fetchBridgeQuote(relayUrl, req) {
|
|
12140
|
+
const body = {
|
|
12141
|
+
recipient: req.recipient,
|
|
12142
|
+
origin_chain: req.originChain,
|
|
12143
|
+
amount_base_units: req.amountBaseUnits.toString(),
|
|
12144
|
+
allocate: req.allocate ?? false
|
|
12145
|
+
};
|
|
12146
|
+
if (req.refundTo !== void 0) body.refund_to = req.refundTo;
|
|
12147
|
+
if (req.rails !== void 0) body.rails = req.rails;
|
|
12148
|
+
const res = await relayFetch(`${relayUrl}/bridge/quote`, {
|
|
12149
|
+
method: "POST",
|
|
12150
|
+
headers: { "Content-Type": "application/json" },
|
|
12151
|
+
body: JSON.stringify(body)
|
|
12152
|
+
});
|
|
12153
|
+
const raw = await parseBridgeResponse(res, "bridge quote");
|
|
12154
|
+
const options = [];
|
|
12155
|
+
const unavailable = (raw.unavailable ?? []).map((p) => ({
|
|
12156
|
+
rail: p.rail,
|
|
12157
|
+
reason: p.reason
|
|
12158
|
+
}));
|
|
12159
|
+
for (const opt of raw.options) {
|
|
12160
|
+
const attestation = toAttestation(opt.attestation);
|
|
12161
|
+
if (attestation.kind === "ed25519") {
|
|
12162
|
+
const verdict = opt.rail_response ? verifyQuoteSignature(opt.rail_response) : { valid: false, reason: "no rail_response to verify against" };
|
|
12163
|
+
if (!verdict.valid) {
|
|
12164
|
+
unavailable.push({
|
|
12165
|
+
rail: opt.rail,
|
|
12166
|
+
reason: `deposit address failed independent signature verification, refusing to display it (${verdict.reason ?? "signature did not verify"})`
|
|
12167
|
+
});
|
|
12168
|
+
continue;
|
|
12169
|
+
}
|
|
12170
|
+
}
|
|
12171
|
+
options.push({
|
|
12172
|
+
rail: opt.rail,
|
|
12173
|
+
refunds: opt.refunds,
|
|
12174
|
+
addressLifetime: opt.address_lifetime,
|
|
12175
|
+
amountOut: BigInt(opt.amount_out),
|
|
12176
|
+
minAmountOut: BigInt(opt.min_amount_out),
|
|
12177
|
+
timeEstimateSeconds: opt.time_estimate_s,
|
|
12178
|
+
expiresAt: opt.expires_at,
|
|
12179
|
+
depositAddress: opt.deposit_address,
|
|
12180
|
+
attestation
|
|
12181
|
+
});
|
|
12182
|
+
}
|
|
12183
|
+
return { options, unavailable };
|
|
12184
|
+
}
|
|
12185
|
+
var STATUS_STATES = [
|
|
12186
|
+
"pending",
|
|
12187
|
+
"delivered",
|
|
12188
|
+
"refunded",
|
|
12189
|
+
"expired",
|
|
12190
|
+
"unknown"
|
|
12191
|
+
];
|
|
12192
|
+
async function fetchBridgeStatus(relayUrl, depositAddress, rail) {
|
|
12193
|
+
const qs = new URLSearchParams({ deposit_address: depositAddress, rail });
|
|
12194
|
+
const res = await relayFetch(`${relayUrl}/bridge/status?${qs.toString()}`);
|
|
12195
|
+
const raw = await parseBridgeResponse(res, "bridge status");
|
|
12196
|
+
const state = STATUS_STATES.includes(raw.state ?? "") ? raw.state : "unknown";
|
|
12197
|
+
return { rail: raw.rail ?? rail, state, detail: raw.detail ?? "" };
|
|
12198
|
+
}
|
|
12199
|
+
function cloakBridgeRail(relayUrl) {
|
|
12200
|
+
assertAllowedRelayOrigin(relayUrl);
|
|
12201
|
+
return {
|
|
12202
|
+
id: "cloak-bridge",
|
|
12203
|
+
quote: (req) => fetchBridgeQuote(relayUrl, req),
|
|
12204
|
+
status: (depositAddress, rail) => fetchBridgeStatus(relayUrl, depositAddress, rail)
|
|
12205
|
+
};
|
|
12206
|
+
}
|
|
12207
|
+
|
|
12208
|
+
// src/bridge/paymaster-client.ts
|
|
12209
|
+
var import_web313 = require("@solana/web3.js");
|
|
12210
|
+
var import_spl_token3 = require("@solana/spl-token");
|
|
12211
|
+
function validatePaymasterTopUpTransaction(tx, expect) {
|
|
12212
|
+
const ixs = tx.instructions;
|
|
12213
|
+
if (ixs.length !== 1 && ixs.length !== 2) {
|
|
12214
|
+
throw new Error(
|
|
12215
|
+
`paymaster top-up must be exactly 1 or 2 instructions, got ${ixs.length} \u2014 refusing to sign a transaction whose shape does not match the documented top-up contract`
|
|
12216
|
+
);
|
|
12217
|
+
}
|
|
12218
|
+
const ix0 = ixs[0];
|
|
12219
|
+
if (!ix0.programId.equals(import_web313.SystemProgram.programId)) {
|
|
12220
|
+
throw new Error(
|
|
12221
|
+
`paymaster top-up instruction 0 is owned by ${ix0.programId.toBase58()}, not the System Program \u2014 refusing to sign an unrecognised first instruction`
|
|
12222
|
+
);
|
|
12223
|
+
}
|
|
12224
|
+
const transfer2 = import_web313.SystemInstruction.decodeTransfer(ix0);
|
|
12225
|
+
if (!transfer2.toPubkey.equals(expect.recipient)) {
|
|
12226
|
+
throw new Error(
|
|
12227
|
+
`paymaster top-up sends SOL to ${transfer2.toPubkey.toBase58()}, which is not our recipient ${expect.recipient.toBase58()} \u2014 refusing to sign a transaction that funds a key we do not control`
|
|
12228
|
+
);
|
|
12229
|
+
}
|
|
12230
|
+
if (BigInt(transfer2.lamports) > expect.maxGrantLamports) {
|
|
12231
|
+
throw new Error(
|
|
12232
|
+
`paymaster top-up grants ${transfer2.lamports} lamports, more than the ${expect.maxGrantLamports} we asked for \u2014 refusing a transaction that funds beyond our own request`
|
|
12233
|
+
);
|
|
12234
|
+
}
|
|
12235
|
+
if (ixs.length === 1) return;
|
|
12236
|
+
const ix1 = ixs[1];
|
|
12237
|
+
if (!ix1.programId.equals(import_spl_token3.TOKEN_PROGRAM_ID)) {
|
|
12238
|
+
throw new Error(
|
|
12239
|
+
`paymaster top-up instruction 1 is owned by ${ix1.programId.toBase58()}, not the SPL Token program \u2014 refusing to sign an unrecognised second instruction`
|
|
12240
|
+
);
|
|
12241
|
+
}
|
|
12242
|
+
const spl = (0, import_spl_token3.decodeTransferInstruction)(ix1, import_spl_token3.TOKEN_PROGRAM_ID);
|
|
12243
|
+
const authority = spl.keys.owner.pubkey;
|
|
12244
|
+
if (!authority.equals(expect.recipient)) {
|
|
12245
|
+
throw new Error(
|
|
12246
|
+
`paymaster top-up's fee transfer is authorised by ${authority.toBase58()}, not our recipient ${expect.recipient.toBase58()} \u2014 refusing to sign away tokens from an account we do not control`
|
|
12247
|
+
);
|
|
12248
|
+
}
|
|
12249
|
+
const expectedDestination = (0, import_spl_token3.getAssociatedTokenAddressSync)(
|
|
12250
|
+
expect.feeMint,
|
|
12251
|
+
expect.paymentAddress,
|
|
12252
|
+
false
|
|
12253
|
+
);
|
|
12254
|
+
const destination = spl.keys.destination.pubkey;
|
|
12255
|
+
if (!destination.equals(expectedDestination)) {
|
|
12256
|
+
throw new Error(
|
|
12257
|
+
`paymaster top-up's fee transfer is addressed to ${destination.toBase58()}, not the paymaster's own payment ATA ${expectedDestination.toBase58()} \u2014 refusing to sign a payment to an unverified destination`
|
|
12258
|
+
);
|
|
12259
|
+
}
|
|
12260
|
+
const paidAmount = BigInt(spl.data.amount);
|
|
12261
|
+
if (paidAmount !== expect.feeTokenAmount) {
|
|
12262
|
+
throw new Error(
|
|
12263
|
+
`paymaster top-up's fee amount is ${paidAmount}, not the ${expect.feeTokenAmount} Kora quoted at prepare time \u2014 refusing to sign a payment that does not match the quote`
|
|
12264
|
+
);
|
|
12265
|
+
}
|
|
12266
|
+
}
|
|
12267
|
+
async function parseBridgeResponse2(res, what) {
|
|
12268
|
+
let body;
|
|
12269
|
+
try {
|
|
12270
|
+
body = await res.json();
|
|
12271
|
+
} catch {
|
|
12272
|
+
throw new Error(`${what} failed: ${res.status} ${res.statusText} (response body was not JSON)`);
|
|
12273
|
+
}
|
|
12274
|
+
if (!res.ok) {
|
|
12275
|
+
const b = body ?? {};
|
|
12276
|
+
throw new Error(
|
|
12277
|
+
`${what} failed: ${b.message ?? res.statusText} (${b.code ?? res.status}${b.retryable ? ", retryable" : ""})`
|
|
12278
|
+
);
|
|
12279
|
+
}
|
|
12280
|
+
return body;
|
|
12281
|
+
}
|
|
12282
|
+
async function fundReceiverViaPaymaster(relayUrl, receiver, grantLamports) {
|
|
12283
|
+
assertAllowedRelayOrigin(relayUrl);
|
|
12284
|
+
if (grantLamports <= 0n || grantLamports > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
12285
|
+
throw new Error(
|
|
12286
|
+
`grantLamports must be a positive value representable as a JS number, got ${grantLamports}`
|
|
12287
|
+
);
|
|
12288
|
+
}
|
|
12289
|
+
const prepareRes = await relayFetch(`${relayUrl}/bridge/paymaster/prepare`, {
|
|
12290
|
+
method: "POST",
|
|
12291
|
+
headers: { "Content-Type": "application/json" },
|
|
12292
|
+
body: JSON.stringify({
|
|
12293
|
+
recipient: receiver.publicKey.toBase58(),
|
|
12294
|
+
grant_lamports: Number(grantLamports)
|
|
12295
|
+
})
|
|
12296
|
+
});
|
|
12297
|
+
const prepared = await parseBridgeResponse2(prepareRes, "paymaster prepare");
|
|
12298
|
+
if (Math.floor(Date.now() / 1e3) >= prepared.expires_at_unix) {
|
|
12299
|
+
throw new Error(
|
|
12300
|
+
"paymaster prepare returned a voucher that is already expired \u2014 refusing to sign a stale top-up rather than fail confusingly at cosign"
|
|
12301
|
+
);
|
|
12302
|
+
}
|
|
12303
|
+
const feeMint = new import_web313.PublicKey(prepared.fee_mint);
|
|
12304
|
+
const paymentAddress = new import_web313.PublicKey(prepared.payment_address);
|
|
12305
|
+
const feeTokenAmount = BigInt(prepared.fee_token_amount);
|
|
12306
|
+
const tx = import_web313.Transaction.from(Buffer.from(prepared.transaction, "base64"));
|
|
12307
|
+
validatePaymasterTopUpTransaction(tx, {
|
|
12308
|
+
recipient: receiver.publicKey,
|
|
12309
|
+
maxGrantLamports: grantLamports,
|
|
12310
|
+
feeMint,
|
|
12311
|
+
feeTokenAmount,
|
|
12312
|
+
paymentAddress
|
|
12313
|
+
});
|
|
12314
|
+
tx.partialSign(receiver);
|
|
12315
|
+
const cosignRes = await relayFetch(`${relayUrl}/bridge/paymaster/cosign`, {
|
|
12316
|
+
method: "POST",
|
|
12317
|
+
headers: { "Content-Type": "application/json" },
|
|
12318
|
+
body: JSON.stringify({
|
|
12319
|
+
transaction: tx.serialize({ requireAllSignatures: false, verifySignatures: false }).toString("base64"),
|
|
12320
|
+
voucher: prepared.voucher
|
|
12321
|
+
})
|
|
12322
|
+
});
|
|
12323
|
+
const cosigned = await parseBridgeResponse2(cosignRes, "paymaster cosign");
|
|
12324
|
+
return {
|
|
12325
|
+
transaction: import_web313.Transaction.from(Buffer.from(cosigned.transaction, "base64")),
|
|
12326
|
+
feeTokenAmount,
|
|
12327
|
+
feeMint: prepared.fee_mint,
|
|
12328
|
+
paymentAddress: prepared.payment_address
|
|
12329
|
+
};
|
|
12330
|
+
}
|
|
12331
|
+
|
|
12332
|
+
// src/bridge/derive.ts
|
|
12333
|
+
var import_web314 = require("@solana/web3.js");
|
|
12334
|
+
var import_hmac = require("@noble/hashes/hmac");
|
|
12335
|
+
var import_sha512 = require("@noble/hashes/sha512");
|
|
12336
|
+
var BRIDGE_ESCROW_LABEL = "cloak_bridge_escrow";
|
|
12337
|
+
var MAX_RECEIVER_INDEX = 1e4;
|
|
12338
|
+
function deriveBridgeReceiver(nk, index) {
|
|
12339
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
12340
|
+
throw new Error(`index must be a non-negative integer, got ${index}`);
|
|
12341
|
+
}
|
|
12342
|
+
if (index > MAX_RECEIVER_INDEX) {
|
|
12343
|
+
throw new Error(
|
|
12344
|
+
`index ${index} exceeds MAX_RECEIVER_INDEX (${MAX_RECEIVER_INDEX}). Indices must be a small sequential counter \u2014 discovery derives 0\u2026N and reads each on chain, so a timestamp index is unreachable by any scan and the deposit would be invisible to every device but this one.`
|
|
12345
|
+
);
|
|
12346
|
+
}
|
|
12347
|
+
const h = (0, import_hmac.hmac)(import_sha512.sha512, new Uint8Array(nk), new TextEncoder().encode(`${BRIDGE_ESCROW_LABEL}:${index}`));
|
|
12348
|
+
return import_web314.Keypair.fromSeed(h.slice(0, 32));
|
|
12349
|
+
}
|
|
12350
|
+
|
|
12351
|
+
// src/bridge/discover.ts
|
|
12352
|
+
var import_web315 = require("@solana/web3.js");
|
|
12353
|
+
var import_spl_token4 = require("@solana/spl-token");
|
|
12354
|
+
function describeError(err) {
|
|
12355
|
+
return err instanceof Error ? err.message : String(err);
|
|
12356
|
+
}
|
|
12357
|
+
async function listBridgeDeposits(conn, nk, opts) {
|
|
12358
|
+
const scanDepth = Math.min(opts.scanDepth ?? 20, MAX_RECEIVER_INDEX + 1);
|
|
12359
|
+
const stopAfterUnused = opts.stopAfterUnused ?? 5;
|
|
12360
|
+
const out = [];
|
|
12361
|
+
let consecutiveUnused = 0;
|
|
12362
|
+
for (let index = 0; index < scanDepth; index++) {
|
|
12363
|
+
const kp = deriveBridgeReceiver(nk, index);
|
|
12364
|
+
const receiver = kp.publicKey;
|
|
12365
|
+
const tokenAccount = (0, import_spl_token4.getAssociatedTokenAddressSync)(opts.mint, receiver, false);
|
|
12366
|
+
try {
|
|
12367
|
+
const [recvInfo, ataInfo] = await conn.getMultipleAccountsInfo([receiver, tokenAccount]);
|
|
12368
|
+
const lamports = recvInfo?.lamports ?? 0;
|
|
12369
|
+
let tokenBalance = 0n;
|
|
12370
|
+
if (ataInfo) {
|
|
12371
|
+
try {
|
|
12372
|
+
tokenBalance = BigInt((await conn.getTokenAccountBalance(tokenAccount)).value.amount);
|
|
12373
|
+
} catch (err) {
|
|
12374
|
+
if (!(err instanceof import_web315.SolanaJSONRPCError && /could not find account/i.test(err.message))) {
|
|
12375
|
+
throw err;
|
|
12376
|
+
}
|
|
12377
|
+
}
|
|
12378
|
+
}
|
|
12379
|
+
if (lamports === 0 && !ataInfo) {
|
|
12380
|
+
consecutiveUnused++;
|
|
12381
|
+
if (consecutiveUnused >= stopAfterUnused) break;
|
|
12382
|
+
continue;
|
|
12383
|
+
}
|
|
12384
|
+
consecutiveUnused = 0;
|
|
12385
|
+
const shieldSignatures = [];
|
|
12386
|
+
const sigs = await conn.getSignaturesForAddress(receiver, { limit: 20 });
|
|
12387
|
+
for (const s of sigs) {
|
|
12388
|
+
if (s.err) continue;
|
|
12389
|
+
const tx = await conn.getTransaction(s.signature, { maxSupportedTransactionVersion: 0 });
|
|
12390
|
+
const keys = tx?.transaction.message.getAccountKeys({
|
|
12391
|
+
accountKeysFromLookups: tx.meta?.loadedAddresses
|
|
12392
|
+
});
|
|
12393
|
+
if (keys && [...Array(keys.length).keys()].some((i) => keys.get(i)?.equals(opts.programId))) {
|
|
12394
|
+
shieldSignatures.push(s.signature);
|
|
12395
|
+
}
|
|
12396
|
+
}
|
|
12397
|
+
const shieldSignature = shieldSignatures[0];
|
|
12398
|
+
const indexReused = shieldSignatures.length > 1;
|
|
12399
|
+
let state;
|
|
12400
|
+
if (tokenBalance > 0n) state = shieldSignature ? "needs-cleanup" : "arrived";
|
|
12401
|
+
else if (shieldSignature) state = ataInfo ? "needs-cleanup" : "complete";
|
|
12402
|
+
else state = "awaiting";
|
|
12403
|
+
out.push({
|
|
12404
|
+
index,
|
|
12405
|
+
receiver,
|
|
12406
|
+
tokenAccount,
|
|
12407
|
+
state,
|
|
12408
|
+
tokenBalance,
|
|
12409
|
+
lamports,
|
|
12410
|
+
shieldSignature,
|
|
12411
|
+
...indexReused ? { indexReused, shieldSignatures } : {}
|
|
12412
|
+
});
|
|
12413
|
+
} catch (err) {
|
|
12414
|
+
out.push({
|
|
12415
|
+
index,
|
|
12416
|
+
receiver,
|
|
12417
|
+
tokenAccount,
|
|
12418
|
+
state: "unknown",
|
|
12419
|
+
tokenBalance: 0n,
|
|
12420
|
+
lamports: 0,
|
|
12421
|
+
error: describeError(err)
|
|
12422
|
+
});
|
|
12423
|
+
}
|
|
12424
|
+
}
|
|
12425
|
+
return out;
|
|
12426
|
+
}
|
|
12427
|
+
|
|
12428
|
+
// src/bridge/deposit-core.ts
|
|
12429
|
+
var import_web317 = require("@solana/web3.js");
|
|
12430
|
+
var import_tweetnacl9 = __toESM(require("tweetnacl"), 1);
|
|
12431
|
+
init_utxo();
|
|
12432
|
+
|
|
12433
|
+
// src/bridge/funder.ts
|
|
12434
|
+
var import_web316 = require("@solana/web3.js");
|
|
12435
|
+
var import_spl_token5 = require("@solana/spl-token");
|
|
12436
|
+
var MAINNET_RENT_0 = 890880;
|
|
12437
|
+
var MAINNET_RENT_1 = 897840;
|
|
12438
|
+
var FEE_BUDGET = 13e4;
|
|
12439
|
+
var CLEANUP_FEE_BUDGET = 5e3;
|
|
12440
|
+
var pda = (seeds, programId) => import_web316.PublicKey.findProgramAddressSync(seeds.map((s) => Buffer.from(s)), programId)[0];
|
|
12441
|
+
function deriveFundingTargets(d) {
|
|
12442
|
+
const pool = pda([Buffer.from("pool"), d.mint.toBuffer()], d.programId);
|
|
12443
|
+
return {
|
|
12444
|
+
pool,
|
|
12445
|
+
// program: NullifierAccount::derive_pda_with_pool -> [b"nullifier", pool, nullifier]
|
|
12446
|
+
nullifier0: pda([Buffer.from("nullifier"), pool.toBuffer(), d.nullifiers[0]], d.programId),
|
|
12447
|
+
nullifier1: pda([Buffer.from("nullifier"), pool.toBuffer(), d.nullifiers[1]], d.programId),
|
|
12448
|
+
// relay: derive_risk_nonce_pda -> [b"risk_nonce", bind0]
|
|
12449
|
+
riskNonce: pda([Buffer.from("risk_nonce"), d.bind0], d.programId),
|
|
12450
|
+
depositorAta: (0, import_spl_token5.getAssociatedTokenAddressSync)(d.mint, d.depositor, false)
|
|
12451
|
+
};
|
|
12452
|
+
}
|
|
12453
|
+
async function readRent(conn) {
|
|
12454
|
+
const [zero, one] = await Promise.all([
|
|
12455
|
+
conn.getMinimumBalanceForRentExemption(0),
|
|
12456
|
+
conn.getMinimumBalanceForRentExemption(1)
|
|
12457
|
+
]);
|
|
12458
|
+
return { zero, one };
|
|
12459
|
+
}
|
|
12460
|
+
|
|
12461
|
+
// src/bridge/deposit-core.ts
|
|
12462
|
+
var DepositError = class extends Error {
|
|
12463
|
+
constructor(message, fundedAccounts, measuredTxSize) {
|
|
12464
|
+
super(message);
|
|
12465
|
+
this.fundedAccounts = fundedAccounts;
|
|
12466
|
+
this.measuredTxSize = measuredTxSize;
|
|
12467
|
+
this.name = "DepositError";
|
|
12468
|
+
}
|
|
12469
|
+
};
|
|
12470
|
+
async function depositFromDerivedKey(conn, R, funder, amount, log = console.log, grantOverride, opts) {
|
|
12471
|
+
if (!opts.noteSpendKey || opts.noteSpendKey.length !== 32) {
|
|
12472
|
+
throw new Error(
|
|
12473
|
+
"opts.noteSpendKey (32 bytes) is required. It derives the note's spending key, and a note whose key is not derived from something you can reproduce is unspendable forever. Pass the same Cloak seed you derived the receiving address from."
|
|
12474
|
+
);
|
|
12475
|
+
}
|
|
12476
|
+
if (!opts.relayUrl) {
|
|
12477
|
+
throw new Error(
|
|
12478
|
+
"opts.relayUrl is required. There is no default: a missing relay used to resolve silently to the local stack, which is how a live run built a deposit against infrastructure that only existed on someone's laptop. Say which relay, every call."
|
|
12479
|
+
);
|
|
12480
|
+
}
|
|
12481
|
+
if (!opts.programId) {
|
|
12482
|
+
throw new Error(
|
|
12483
|
+
"opts.programId is required. There is no default: this is the exact field a live mainnet run once inherited from this module's local-stack constant, built a deposit against a program that does not exist on mainnet, and failed hunting a merkle tree that was never there."
|
|
12484
|
+
);
|
|
12485
|
+
}
|
|
12486
|
+
if (!opts.mint) {
|
|
12487
|
+
throw new Error(
|
|
12488
|
+
"opts.mint is required. There is no default: a deposit built for the wrong mint produces a note for an asset that never arrived at R, and there is nothing to silently fall back to."
|
|
12489
|
+
);
|
|
12490
|
+
}
|
|
12491
|
+
assertAllowedRpcConnection(conn);
|
|
12492
|
+
const relayUrl = opts.relayUrl;
|
|
12493
|
+
const programId = opts.programId;
|
|
12494
|
+
const mint = opts.mint;
|
|
12495
|
+
setCircuitsPath(resolveCircuitsBase());
|
|
12496
|
+
const grantR = grantOverride ?? MAINNET_RENT_0 + FEE_BUDGET + CLEANUP_FEE_BUDGET;
|
|
12497
|
+
const heldByR = await conn.getBalance(R.publicKey);
|
|
12498
|
+
if (heldByR >= grantR) {
|
|
12499
|
+
log(` R already holds ${heldByR} (>= ${grantR}) \u2014 no top-up needed`);
|
|
12500
|
+
} else {
|
|
12501
|
+
await (0, import_web317.sendAndConfirmTransaction)(conn, new import_web317.Transaction().add(
|
|
12502
|
+
import_web317.SystemProgram.transfer({ fromPubkey: funder.publicKey, toPubkey: R.publicKey, lamports: grantR - heldByR })
|
|
12503
|
+
), [funder]);
|
|
12504
|
+
log(` funder topped R up by ${grantR - heldByR} to ${grantR}${grantOverride !== void 0 ? " (OVERRIDDEN for a deliberate test)" : " \u2014 floor + deposit fee + cleanup fee, no rent"}`);
|
|
12505
|
+
}
|
|
12506
|
+
let measuredSize = -1;
|
|
12507
|
+
let fundedAccounts = [];
|
|
12508
|
+
const signTransaction2 = async (tx) => {
|
|
12509
|
+
if (tx instanceof import_web317.VersionedTransaction) {
|
|
12510
|
+
measuredSize = tx.serialize().length;
|
|
12511
|
+
const alts = await Promise.all(
|
|
12512
|
+
tx.message.addressTableLookups.map(async (l) => (await conn.getAddressLookupTable(l.accountKey)).value)
|
|
12513
|
+
);
|
|
12514
|
+
const keys = tx.message.getAccountKeys({ addressLookupTableAccounts: alts });
|
|
12515
|
+
const ix = tx.message.compiledInstructions.find((i) => keys.get(i.programIdIndex).equals(programId));
|
|
12516
|
+
const at = (n) => keys.get(ix.accountKeyIndexes[n]);
|
|
12517
|
+
const targets = [
|
|
12518
|
+
{ a: at(12), need: MAINNET_RENT_0, name: "risk_nonce" },
|
|
12519
|
+
{ a: at(4), need: MAINNET_RENT_1, name: "nullifier_0" },
|
|
12520
|
+
{ a: at(5), need: MAINNET_RENT_1, name: "nullifier_1" }
|
|
12521
|
+
];
|
|
12522
|
+
const infos = await conn.getMultipleAccountsInfo(targets.map((t) => t.a));
|
|
12523
|
+
fundedAccounts = targets.map((t, i) => ({
|
|
12524
|
+
name: t.name,
|
|
12525
|
+
address: t.a.toBase58(),
|
|
12526
|
+
lamportsSent: Math.max(0, t.need - (infos[i]?.lamports ?? 0))
|
|
12527
|
+
}));
|
|
12528
|
+
const ixs = targets.flatMap((t, i) => (infos[i]?.lamports ?? 0) >= t.need ? [] : [import_web317.SystemProgram.transfer({
|
|
12529
|
+
fromPubkey: funder.publicKey,
|
|
12530
|
+
toPubkey: t.a,
|
|
12531
|
+
lamports: t.need - (infos[i]?.lamports ?? 0)
|
|
12532
|
+
})]);
|
|
12533
|
+
if (ixs.length) {
|
|
12534
|
+
const sig = await (0, import_web317.sendAndConfirmTransaction)(conn, new import_web317.Transaction().add(...ixs), [funder]);
|
|
12535
|
+
log(` seam: funded ${ixs.length} program accounts read off the built tx \u2014 ${sig.slice(0, 16)}\u2026`);
|
|
12536
|
+
}
|
|
12537
|
+
tx.sign([R]);
|
|
12538
|
+
return tx;
|
|
12539
|
+
}
|
|
12540
|
+
tx.partialSign(R);
|
|
12541
|
+
return tx;
|
|
12542
|
+
};
|
|
12543
|
+
const signMessage = async (m) => import_tweetnacl9.default.sign.detached(m, R.secretKey);
|
|
12544
|
+
const utxoKeypair = await deriveUtxoKeypairFromSpendKey(opts.noteSpendKey);
|
|
12545
|
+
const nk = getNkFromUtxoPrivateKey(utxoKeypair.privateKey);
|
|
12546
|
+
const { utxo, noteSalt } = await createRecoverableDepositUtxo(amount, nk, mint);
|
|
12547
|
+
const zeroInput = await createZeroUtxo(mint);
|
|
12548
|
+
const rBefore = await conn.getBalance(R.publicKey);
|
|
12549
|
+
let result;
|
|
12550
|
+
try {
|
|
12551
|
+
result = await transact(
|
|
12552
|
+
{ inputUtxos: [zeroInput], outputUtxos: [utxo], externalAmount: amount, depositor: R.publicKey },
|
|
12553
|
+
{
|
|
12554
|
+
connection: conn,
|
|
12555
|
+
programId,
|
|
12556
|
+
relayUrl,
|
|
12557
|
+
// NO depositorKeypair: the SDK branches `if (keypair) … else if (signTransaction)`
|
|
12558
|
+
// (transact.ts:2786, :2936), so passing one silently skips the funding seam.
|
|
12559
|
+
depositorPublicKey: R.publicKey,
|
|
12560
|
+
walletPublicKey: R.publicKey,
|
|
12561
|
+
signTransaction: signTransaction2,
|
|
12562
|
+
signMessage,
|
|
12563
|
+
chainNoteViewingKeyNk: nk,
|
|
12564
|
+
chainNoteSalt: noteSalt,
|
|
12565
|
+
relaySupplementalAlt: true,
|
|
12566
|
+
onProgress: (s) => log(` \xB7 ${s}`)
|
|
12567
|
+
}
|
|
12568
|
+
);
|
|
12569
|
+
} catch (e) {
|
|
12570
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
12571
|
+
throw new DepositError(msg, fundedAccounts, measuredSize);
|
|
12572
|
+
}
|
|
12573
|
+
const rAfter = await conn.getBalance(R.publicKey);
|
|
12574
|
+
const noteIndex = result.outputUtxos[0].index;
|
|
12575
|
+
if (noteIndex === void 0) throw new Error("deposit landed but the note has no index \u2014 it cannot be discovered later");
|
|
12576
|
+
return {
|
|
12577
|
+
signature: result.signature,
|
|
12578
|
+
noteIndex,
|
|
12579
|
+
amount: result.outputUtxos[0].amount,
|
|
12580
|
+
txSize: measuredSize,
|
|
12581
|
+
rBefore,
|
|
12582
|
+
rAfter,
|
|
12583
|
+
rentExempt: rAfter >= MAINNET_RENT_0,
|
|
12584
|
+
inputNullifiers: result.inputNullifiers,
|
|
12585
|
+
fundedAccounts
|
|
12586
|
+
};
|
|
12587
|
+
}
|
|
12588
|
+
|
|
12589
|
+
// src/bridge/cleanup.ts
|
|
12590
|
+
var import_web318 = require("@solana/web3.js");
|
|
12591
|
+
var import_spl_token6 = require("@solana/spl-token");
|
|
12592
|
+
var DEFAULT_MINT = new import_web318.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
12593
|
+
async function cleanupReceivingAddress(conn, R, dustDestination, mint = DEFAULT_MINT) {
|
|
12594
|
+
assertAllowedRpcConnection(conn);
|
|
12595
|
+
const rAta = (0, import_spl_token6.getAssociatedTokenAddressSync)(mint, R.publicKey);
|
|
12596
|
+
const info = await conn.getAccountInfo(rAta);
|
|
12597
|
+
if (!info) {
|
|
12598
|
+
return {
|
|
12599
|
+
closed: false,
|
|
12600
|
+
dustSwept: 0n,
|
|
12601
|
+
rentReturned: 0,
|
|
12602
|
+
destination: null,
|
|
12603
|
+
signature: null,
|
|
12604
|
+
note: "no token account \u2014 nothing to recover"
|
|
12605
|
+
};
|
|
12606
|
+
}
|
|
12607
|
+
const bal = BigInt((await conn.getTokenAccountBalance(rAta)).value.amount);
|
|
12608
|
+
const rentHeld = info.lamports;
|
|
12609
|
+
const before = await conn.getBalance(R.publicKey);
|
|
12610
|
+
const ixs = [];
|
|
12611
|
+
let destination = null;
|
|
12612
|
+
if (bal > 0n && !dustDestination) {
|
|
12613
|
+
return {
|
|
12614
|
+
closed: false,
|
|
12615
|
+
dustSwept: 0n,
|
|
12616
|
+
rentReturned: 0,
|
|
12617
|
+
destination: null,
|
|
12618
|
+
signature: null,
|
|
12619
|
+
note: `dusted with ${bal} base units after the deposit; leaving the account open and ${rentHeld.toLocaleString()} lamports of rent unrecovered, because sweeping it would publish a link from this deposit to whoever receives it`
|
|
12620
|
+
};
|
|
12621
|
+
}
|
|
12622
|
+
if (bal > 0n && dustDestination) {
|
|
12623
|
+
const destAta = (0, import_spl_token6.getAssociatedTokenAddressSync)(mint, dustDestination);
|
|
12624
|
+
const destInfo = await conn.getAccountInfo(destAta);
|
|
12625
|
+
if (!destInfo) {
|
|
12626
|
+
const ataRent = await (0, import_spl_token6.getMinimumBalanceForRentExemptAccount)(conn);
|
|
12627
|
+
if (before < ataRent + CLEANUP_FEE_BUDGET) {
|
|
12628
|
+
throw new Error(
|
|
12629
|
+
`dustDestination ${dustDestination.toBase58()} has no token account for mint ${mint.toBase58()}, and creating one costs ${ataRent} lamports of rent that R does not have: R holds ${before}, which deposit-core.ts reserved only for this transaction's own fee (CLEANUP_FEE_BUDGET = ${CLEANUP_FEE_BUDGET} in funder.ts), not for a fresh account's rent. Fund R with at least ${ataRent + CLEANUP_FEE_BUDGET - before} more lamports first, or choose a destination that already has a token account for this mint.`
|
|
12630
|
+
);
|
|
12631
|
+
}
|
|
12632
|
+
ixs.push((0, import_spl_token6.createAssociatedTokenAccountIdempotentInstruction)(R.publicKey, destAta, dustDestination, mint));
|
|
12633
|
+
}
|
|
12634
|
+
ixs.push((0, import_spl_token6.createTransferInstruction)(rAta, destAta, R.publicKey, bal));
|
|
12635
|
+
destination = destAta.toBase58();
|
|
12636
|
+
}
|
|
12637
|
+
ixs.push((0, import_spl_token6.createCloseAccountInstruction)(rAta, R.publicKey, R.publicKey));
|
|
12638
|
+
const signature = await (0, import_web318.sendAndConfirmTransaction)(conn, new import_web318.Transaction().add(...ixs), [R], {
|
|
12639
|
+
commitment: "confirmed"
|
|
12640
|
+
});
|
|
12641
|
+
const after = await conn.getBalance(R.publicKey);
|
|
12642
|
+
const gone = await conn.getAccountInfo(rAta) === null;
|
|
12643
|
+
return {
|
|
12644
|
+
closed: gone,
|
|
12645
|
+
dustSwept: bal,
|
|
12646
|
+
rentReturned: after - before,
|
|
12647
|
+
destination,
|
|
12648
|
+
signature,
|
|
12649
|
+
note: bal > 0n ? `swept ${bal} base units of dust to the caller-chosen destination and closed, in one transaction \u2014 note this publishes a link from the deposit to that destination` : `closed cleanly, nothing to sweep`
|
|
12650
|
+
};
|
|
12651
|
+
}
|
|
12652
|
+
|
|
12653
|
+
// src/bridge/quote.ts
|
|
12654
|
+
var MIN_DEPOSIT_SPL_BASE_UNITS = 1000000n;
|
|
12655
|
+
var WITHDRAW_FIXED_FEE = 450000n;
|
|
12656
|
+
var WITHDRAW_FEE_BPS = 30n;
|
|
12657
|
+
function withdrawFeeAt(amount, fixedFee, feeBps) {
|
|
12658
|
+
return fixedFee + amount * feeBps / 10000n;
|
|
12659
|
+
}
|
|
12660
|
+
var MAX_FEE_FRACTION = 0.063;
|
|
12661
|
+
var FIXED_ROUND_TRIP = 903000n;
|
|
12662
|
+
var ECONOMIC_MINIMUM = FIXED_ROUND_TRIP * 1000000n / BigInt(Math.round((MAX_FEE_FRACTION - 3e-3) * 1e6));
|
|
12663
|
+
function withdrawFeeFor(amount) {
|
|
12664
|
+
return withdrawFeeAt(amount, WITHDRAW_FIXED_FEE, WITHDRAW_FEE_BPS);
|
|
12665
|
+
}
|
|
12666
|
+
function assessBridgeQuote(q) {
|
|
12667
|
+
if (q.sent < 0n) {
|
|
12668
|
+
throw new Error(`assessBridgeQuote: sent must not be negative (got ${q.sent}).`);
|
|
12669
|
+
}
|
|
12670
|
+
if (q.arrivesMin > q.sent) {
|
|
12671
|
+
throw new Error(
|
|
12672
|
+
`assessBridgeQuote: arrivesMin (${q.arrivesMin}) exceeds sent (${q.sent}) \u2014 no rail returns more than it was given, so this input did not come from a real quote.`
|
|
12673
|
+
);
|
|
12674
|
+
}
|
|
12675
|
+
const shieldedMinRaw = q.arrivesMin - q.paymasterFee;
|
|
12676
|
+
const shieldedTargetRaw = q.arrivesTarget - q.paymasterFee;
|
|
12677
|
+
const shieldedMin = shieldedMinRaw > 0n ? shieldedMinRaw : 0n;
|
|
12678
|
+
const shieldedTarget = shieldedTargetRaw > 0n ? shieldedTargetRaw : 0n;
|
|
12679
|
+
const viable = shieldedMinRaw >= MIN_DEPOSIT_SPL_BASE_UNITS;
|
|
12680
|
+
const economic = q.sent >= ECONOMIC_MINIMUM;
|
|
12681
|
+
const withdrawFee = shieldedMin > 0n ? withdrawFeeAt(shieldedMin, q.liveWithdrawFixedFee ?? WITHDRAW_FIXED_FEE, q.liveWithdrawFeeBps ?? WITHDRAW_FEE_BPS) : 0n;
|
|
12682
|
+
const roundTripCost = q.sent - (shieldedMin > withdrawFee ? shieldedMin - withdrawFee : 0n);
|
|
12683
|
+
const roundTripFraction = q.sent > 0n ? Number(roundTripCost) / Number(q.sent) : 0;
|
|
12684
|
+
const reasons = [];
|
|
12685
|
+
if (!viable) {
|
|
12686
|
+
const short = MIN_DEPOSIT_SPL_BASE_UNITS - shieldedMinRaw;
|
|
12687
|
+
reasons.push(
|
|
12688
|
+
(shieldedMinRaw <= 0n ? `The ${fmt(q.paymasterFee)} paymaster fee alone is ${shieldedMinRaw < 0n ? `${fmt(-shieldedMinRaw)} more than` : "all of"} the ${fmt(q.arrivesMin)} the rail guarantees, so nothing would be left to shield.` : `Only ${fmt(shieldedMinRaw)} would be left to shield after the ${fmt(q.paymasterFee)} paymaster fee.`) + ` The program's minimum deposit is ${fmt(MIN_DEPOSIT_SPL_BASE_UNITS)}. Send at least ${fmt(q.sent + short)} to clear it.`
|
|
12689
|
+
);
|
|
12690
|
+
}
|
|
12691
|
+
if (viable && !economic) {
|
|
12692
|
+
reasons.push(
|
|
12693
|
+
`This works, but ${fmt(roundTripCost)} of ${fmt(q.sent)} goes to fees (${(roundTripFraction * 100).toFixed(0)}%). The costs are almost all fixed, so bridging ${fmt(ECONOMIC_MINIMUM)} or more spreads them much further.`
|
|
12694
|
+
);
|
|
12695
|
+
}
|
|
12696
|
+
return {
|
|
12697
|
+
...q,
|
|
12698
|
+
shieldedMin,
|
|
12699
|
+
shieldedTarget,
|
|
12700
|
+
viable,
|
|
12701
|
+
economic,
|
|
12702
|
+
withdrawFee,
|
|
12703
|
+
roundTripCost,
|
|
12704
|
+
roundTripFraction,
|
|
12705
|
+
reasons
|
|
12706
|
+
};
|
|
12707
|
+
}
|
|
12708
|
+
var fmt = (v) => `${(Number(v) / 1e6).toFixed(6)} USDC`;
|
|
12709
|
+
function renderAssessment(a) {
|
|
12710
|
+
const L = [];
|
|
12711
|
+
L.push(` you send ${fmt(a.sent)}`);
|
|
12712
|
+
L.push(` arrives at least ${fmt(a.arrivesMin)} (target ${fmt(a.arrivesTarget)}, not a promise)`);
|
|
12713
|
+
L.push(` paymaster fee ${fmt(a.paymasterFee)} buys the receiver its SOL, so your wallet stays off chain`);
|
|
12714
|
+
L.push(` SHIELDED ${fmt(a.shieldedMin)} at least \u2014 this is what you end up with`);
|
|
12715
|
+
L.push(` to withdraw later ${fmt(a.withdrawFee)} the program's fee, whenever you take it out`);
|
|
12716
|
+
for (const r of a.reasons) L.push(`
|
|
12717
|
+
${a.viable ? "NOTE" : "REFUSED"}: ${r}`);
|
|
12718
|
+
return L.join("\n");
|
|
12719
|
+
}
|
|
12720
|
+
|
|
12721
|
+
// src/bridge/retry.ts
|
|
12722
|
+
var TERMINAL_FAILURE = /DepositTooSmall|minimum is 1\.000000|insufficient|holds only|below the program|kora wants|over the .* ceiling/i;
|
|
12723
|
+
function isTerminalFailure(message) {
|
|
12724
|
+
return TERMINAL_FAILURE.test(message);
|
|
12725
|
+
}
|
|
12726
|
+
function classifyPostFailure(attempted, after) {
|
|
12727
|
+
if (after === null) return "unknown";
|
|
12728
|
+
if (after === 0n) return "landed";
|
|
12729
|
+
if (after >= attempted) return "did-not-land";
|
|
12730
|
+
return "unknown";
|
|
12731
|
+
}
|
|
12732
|
+
function mayRetry(verdict, message) {
|
|
12733
|
+
return verdict === "did-not-land" && !isTerminalFailure(message);
|
|
12734
|
+
}
|
|
12735
|
+
function retryDelayMs(attempt) {
|
|
12736
|
+
return 15e3 * Math.max(0, attempt - 1);
|
|
12737
|
+
}
|
|
12738
|
+
var DoNotRetry = class extends Error {
|
|
12739
|
+
constructor(message) {
|
|
12740
|
+
super(message);
|
|
12741
|
+
this.name = "DoNotRetry";
|
|
12742
|
+
}
|
|
12743
|
+
};
|
|
12744
|
+
async function withShieldRetries(attempt, hooks = {}) {
|
|
12745
|
+
const attempts = hooks.attempts ?? 3;
|
|
12746
|
+
const sleep4 = hooks.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
12747
|
+
let last = "";
|
|
12748
|
+
for (let n = 1; n <= attempts; n++) {
|
|
12749
|
+
if (n > 1) {
|
|
12750
|
+
const delay = retryDelayMs(n);
|
|
12751
|
+
hooks.onRetry?.(n, delay, last);
|
|
12752
|
+
await sleep4(delay);
|
|
12753
|
+
}
|
|
12754
|
+
try {
|
|
12755
|
+
return await attempt(n);
|
|
12756
|
+
} catch (e) {
|
|
12757
|
+
if (e instanceof DoNotRetry) throw e;
|
|
12758
|
+
last = e instanceof Error ? e.message : String(e);
|
|
12759
|
+
if (isTerminalFailure(last) || n === attempts) throw e;
|
|
12760
|
+
}
|
|
12761
|
+
}
|
|
12762
|
+
throw new Error("unreachable");
|
|
12763
|
+
}
|
|
12764
|
+
|
|
11635
12765
|
// src/index.ts
|
|
11636
|
-
var VERSION = "0.2.
|
|
12766
|
+
var VERSION = "0.2.3";
|
|
11637
12767
|
var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
11638
12768
|
// Annotate the CommonJS export names for ESM import in node:
|
|
11639
12769
|
0 && (module.exports = {
|
|
12770
|
+
BRIDGE_ESCROW_LABEL,
|
|
11640
12771
|
BUILD_ALLOWS_LOCAL_ENDPOINTS,
|
|
11641
12772
|
CHAIN_NOTE_SALT_BITS,
|
|
12773
|
+
CLEANUP_FEE_BUDGET,
|
|
11642
12774
|
CLOAK_PRODUCTION_RELAY_URL,
|
|
11643
12775
|
CLOAK_PROGRAM_ID,
|
|
11644
12776
|
CloakError,
|
|
@@ -11646,25 +12778,38 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11646
12778
|
DEFAULT_TRANSACTION_CIRCUITS_URL,
|
|
11647
12779
|
DELIVERY_MEMO_TAG,
|
|
11648
12780
|
DELIVERY_REGISTRY_SEED,
|
|
12781
|
+
DepositError,
|
|
12782
|
+
DoNotRetry,
|
|
12783
|
+
ECONOMIC_MINIMUM,
|
|
11649
12784
|
EXPECTED_CIRCUIT_HASHES,
|
|
12785
|
+
FEE_BUDGET,
|
|
11650
12786
|
FIXED_FEE_LAMPORTS,
|
|
11651
12787
|
InsecureRandomnessError,
|
|
11652
12788
|
LAMPORTS_PER_SOL,
|
|
11653
12789
|
LocalStorageAdapter,
|
|
12790
|
+
MAINNET_RENT_0,
|
|
12791
|
+
MAINNET_RENT_1,
|
|
12792
|
+
MAX_RECEIVER_INDEX,
|
|
11654
12793
|
MERKLE_TREE_HEIGHT,
|
|
11655
12794
|
MIN_DEPOSIT_LAMPORTS,
|
|
12795
|
+
MIN_DEPOSIT_SPL_BASE_UNITS,
|
|
11656
12796
|
MemoryStorageAdapter,
|
|
11657
12797
|
MerkleTree,
|
|
11658
12798
|
NATIVE_SOL_MINT,
|
|
12799
|
+
ONECLICK_PUBKEY_B58,
|
|
11659
12800
|
RECIPIENT_DELIVERY_CIPHERTEXT_LEN,
|
|
11660
12801
|
RECIPIENT_DELIVERY_EPHEMERAL_PK_LEN,
|
|
11661
12802
|
RECIPIENT_DELIVERY_NONCE_LEN,
|
|
11662
12803
|
RECIPIENT_DELIVERY_NOTE_BYTES,
|
|
11663
12804
|
RECIPIENT_DELIVERY_PLAINTEXT_LEN,
|
|
11664
12805
|
RECIPIENT_DELIVERY_TAG_LEN,
|
|
12806
|
+
RELAY_BATCH_AUTH_MAX_ITEMS,
|
|
11665
12807
|
RELAY_ORIGIN_ALLOWLIST,
|
|
12808
|
+
REQUEST_AUTH_BATCH_DOMAIN,
|
|
12809
|
+
REQUEST_AUTH_BATCH_MAX_AGE_SECONDS,
|
|
11666
12810
|
REQUEST_AUTH_MAX_AGE_SECONDS,
|
|
11667
12811
|
REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS,
|
|
12812
|
+
RelayBatchAuthCoordinator,
|
|
11668
12813
|
RelayInternalError,
|
|
11669
12814
|
RelayService,
|
|
11670
12815
|
RootNotFoundError,
|
|
@@ -11674,6 +12819,7 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11674
12819
|
SettlementVerificationError,
|
|
11675
12820
|
ShieldPoolErrors,
|
|
11676
12821
|
SimpleWallet,
|
|
12822
|
+
TERMINAL_FAILURE,
|
|
11677
12823
|
TRANSACTION_CIRCUITS_VERSION,
|
|
11678
12824
|
TRANSACT_AUTH_FIELDS,
|
|
11679
12825
|
TRANSACT_SWAP_AUTH_FIELDS,
|
|
@@ -11683,8 +12829,12 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11683
12829
|
VARIABLE_FEE_NUMERATOR,
|
|
11684
12830
|
VARIABLE_FEE_RATE,
|
|
11685
12831
|
VERSION,
|
|
12832
|
+
WITHDRAW_FEE_BPS,
|
|
12833
|
+
WITHDRAW_FIXED_FEE,
|
|
11686
12834
|
assertDirectSubmissionLanded,
|
|
12835
|
+
assertInputMints,
|
|
11687
12836
|
assertTransactionCircuitIntegrity,
|
|
12837
|
+
assessBridgeQuote,
|
|
11688
12838
|
bigintToBytes32,
|
|
11689
12839
|
bigintToHex,
|
|
11690
12840
|
buildMerkleTree,
|
|
@@ -11692,6 +12842,7 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11692
12842
|
buildMerkleTreeFromRelay,
|
|
11693
12843
|
buildRecipientDeliveryNotes,
|
|
11694
12844
|
buildRelayAuthPreimage,
|
|
12845
|
+
buildRelayBatchAuthMessage,
|
|
11695
12846
|
buildTransactRequestBody,
|
|
11696
12847
|
bytesToHex,
|
|
11697
12848
|
calculateFee,
|
|
@@ -11699,12 +12850,16 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11699
12850
|
calculateRelayFee,
|
|
11700
12851
|
canRebuildMerkleTreeFromChain,
|
|
11701
12852
|
canonicalJson,
|
|
12853
|
+
canonicalPayloadString,
|
|
11702
12854
|
chainNoteFromBase64,
|
|
11703
12855
|
chainNoteToBase64,
|
|
12856
|
+
classifyPostFailure,
|
|
11704
12857
|
classifyRelayError,
|
|
12858
|
+
cleanupReceivingAddress,
|
|
11705
12859
|
cleanupStalePendingOperations,
|
|
11706
12860
|
clearPendingDeposits,
|
|
11707
12861
|
clearPendingWithdrawals,
|
|
12862
|
+
cloakBridgeRail,
|
|
11708
12863
|
computeChainNoteHash,
|
|
11709
12864
|
computeExtDataHash,
|
|
11710
12865
|
computeMerkleRoot,
|
|
@@ -11721,15 +12876,19 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11721
12876
|
createLogger,
|
|
11722
12877
|
createRecoverableChangeUtxo,
|
|
11723
12878
|
createRecoverableDepositUtxo,
|
|
12879
|
+
createRelayBatchAuthCoordinator,
|
|
11724
12880
|
createUtxo,
|
|
11725
12881
|
createZeroUtxo,
|
|
11726
12882
|
decryptCompactChainNote,
|
|
11727
12883
|
decryptComplianceMetadataWithMasterKey,
|
|
11728
12884
|
decryptTransactionMetadata,
|
|
12885
|
+
depositFromDerivedKey,
|
|
12886
|
+
deriveBridgeReceiver,
|
|
11729
12887
|
deriveChangeNoteBlinding,
|
|
11730
12888
|
deriveDepositNoteSecrets,
|
|
11731
12889
|
deriveDiversifiedViewingKey,
|
|
11732
12890
|
deriveDiversifier,
|
|
12891
|
+
deriveFundingTargets,
|
|
11733
12892
|
deriveInputNullifierPdas,
|
|
11734
12893
|
derivePublicKey,
|
|
11735
12894
|
deriveSpendKey,
|
|
@@ -11768,6 +12927,7 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11768
12927
|
formatErrorForLogging,
|
|
11769
12928
|
formatSol,
|
|
11770
12929
|
fullWithdraw,
|
|
12930
|
+
fundReceiverViaPaymaster,
|
|
11771
12931
|
generateCloakKeys,
|
|
11772
12932
|
generateCommitmentAsync,
|
|
11773
12933
|
generateMasterSeed,
|
|
@@ -11803,18 +12963,21 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11803
12963
|
isReactNative,
|
|
11804
12964
|
isRootNotFoundError,
|
|
11805
12965
|
isSubmissionOutcomeUnknownResponse,
|
|
12966
|
+
isTerminalFailure,
|
|
11806
12967
|
isValidHex,
|
|
11807
12968
|
isValidRpcUrl,
|
|
11808
12969
|
isValidSolanaAddress,
|
|
11809
12970
|
isWithdrawAmountSufficient,
|
|
11810
12971
|
isWithdrawable,
|
|
11811
12972
|
keypairToAdapter,
|
|
12973
|
+
listBridgeDeposits,
|
|
11812
12974
|
loadPendingDeposits,
|
|
11813
12975
|
loadPendingWithdrawals,
|
|
11814
12976
|
loadVerifiedCircuitArtifacts,
|
|
11815
12977
|
matchChangeNote,
|
|
11816
12978
|
matchDepositNote,
|
|
11817
12979
|
matchSwapRefundLeaf,
|
|
12980
|
+
mayRetry,
|
|
11818
12981
|
openRecipientDeliveryNote,
|
|
11819
12982
|
parseAmount,
|
|
11820
12983
|
parseDeliveryCarrierMemo,
|
|
@@ -11837,11 +13000,15 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11837
13000
|
randomDepositNoteSalt,
|
|
11838
13001
|
randomFieldElement,
|
|
11839
13002
|
readMerkleTreeState,
|
|
13003
|
+
readRent,
|
|
11840
13004
|
recipientDeliveryNoteToBase64,
|
|
11841
13005
|
registerViewingKey,
|
|
13006
|
+
relayRequestDigestHex,
|
|
11842
13007
|
removePendingDeposit,
|
|
11843
13008
|
removePendingWithdrawal,
|
|
13009
|
+
renderAssessment,
|
|
11844
13010
|
resolveCircuitsBase,
|
|
13011
|
+
retryDelayMs,
|
|
11845
13012
|
savePendingDeposit,
|
|
11846
13013
|
savePendingWithdrawal,
|
|
11847
13014
|
scanNotesForWallet,
|
|
@@ -11862,6 +13029,7 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11862
13029
|
swapWithChange,
|
|
11863
13030
|
toComplianceReport,
|
|
11864
13031
|
transact,
|
|
13032
|
+
transactBatch,
|
|
11865
13033
|
transfer,
|
|
11866
13034
|
truncate,
|
|
11867
13035
|
tryDecryptNote,
|
|
@@ -11874,13 +13042,17 @@ var SCANNER_SUPPORTS_TRANSACT_SWAP = true;
|
|
|
11874
13042
|
validateDepositParams,
|
|
11875
13043
|
validateNote,
|
|
11876
13044
|
validateOutputsSum,
|
|
13045
|
+
validatePaymasterTopUpTransaction,
|
|
11877
13046
|
validateRoot,
|
|
11878
13047
|
validateTransfers,
|
|
11879
13048
|
validateWalletConnected,
|
|
11880
13049
|
validateWithdrawableNote,
|
|
11881
13050
|
verifyAllCircuits,
|
|
11882
13051
|
verifyCircuitIntegrity,
|
|
13052
|
+
verifyQuoteSignature,
|
|
11883
13053
|
verifyUtxos,
|
|
11884
13054
|
waitForRoot,
|
|
11885
|
-
|
|
13055
|
+
withShieldRetries,
|
|
13056
|
+
withTiming,
|
|
13057
|
+
withdrawFeeFor
|
|
11886
13058
|
});
|