@cloak.dev/sdk 0.2.3-staging.16d1078 → 0.2.3-staging.4f641ea
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/index.cjs +609 -539
- package/dist/index.d.cts +205 -154
- package/dist/index.d.ts +205 -154
- package/dist/index.js +432 -363
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -577,9 +577,9 @@ var LocalStorageAdapter = class {
|
|
|
577
577
|
};
|
|
578
578
|
|
|
579
579
|
// src/scanning/compliance-keys.ts
|
|
580
|
-
import
|
|
580
|
+
import nacl3 from "tweetnacl";
|
|
581
581
|
import { blake3 as blake32 } from "@noble/hashes/blake3";
|
|
582
|
-
import { sha256 } from "@noble/hashes/sha2";
|
|
582
|
+
import { sha256 as sha2562 } from "@noble/hashes/sha2";
|
|
583
583
|
|
|
584
584
|
// src/relay/viewing-key.ts
|
|
585
585
|
var VIEWING_KEY_CHALLENGE_PREFIX = "CLOAK_VIEWING_KEY_REGISTER";
|
|
@@ -766,6 +766,275 @@ function connectionRpcEndpoint(connection) {
|
|
|
766
766
|
return connection?._rpcEndpoint ?? connection?.rpcEndpoint;
|
|
767
767
|
}
|
|
768
768
|
|
|
769
|
+
// src/relay/payload.ts
|
|
770
|
+
import { sha256 } from "@noble/hashes/sha2";
|
|
771
|
+
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
|
|
772
|
+
import nacl2 from "tweetnacl";
|
|
773
|
+
import { Keypair, PublicKey as PublicKey3, SystemProgram, Transaction } from "@solana/web3.js";
|
|
774
|
+
var REQUEST_AUTH_DOMAIN = "CLOAK_RELAY_REQUEST_AUTH_V1";
|
|
775
|
+
var REQUEST_AUTH_MAX_AGE_SECONDS = 300;
|
|
776
|
+
var REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS = 30;
|
|
777
|
+
var RELAY_AUTH_APPROVAL_MARGIN_SECONDS = 15;
|
|
778
|
+
function sha256Hex(text) {
|
|
779
|
+
return bytesToHex2(sha256(new TextEncoder().encode(text)));
|
|
780
|
+
}
|
|
781
|
+
function randomNonceUuid() {
|
|
782
|
+
const webCrypto = globalThis?.crypto;
|
|
783
|
+
if (typeof webCrypto?.randomUUID === "function") return webCrypto.randomUUID();
|
|
784
|
+
const proc = globalThis?.process;
|
|
785
|
+
const nodeCrypto = typeof proc?.getBuiltinModule === "function" ? proc.getBuiltinModule("node:crypto") : void 0;
|
|
786
|
+
if (typeof nodeCrypto?.randomUUID === "function") return nodeCrypto.randomUUID();
|
|
787
|
+
const bytes = randomBytes(16);
|
|
788
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
789
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
790
|
+
const hex = bytesToHex2(bytes);
|
|
791
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
792
|
+
}
|
|
793
|
+
var TRANSACT_AUTH_FIELDS = [
|
|
794
|
+
"encrypted_notes",
|
|
795
|
+
"max_fee",
|
|
796
|
+
"mint",
|
|
797
|
+
"proof_bytes",
|
|
798
|
+
"public_inputs",
|
|
799
|
+
"recipient",
|
|
800
|
+
"recipient_delivery_notes",
|
|
801
|
+
"risk_quote",
|
|
802
|
+
"sender"
|
|
803
|
+
];
|
|
804
|
+
var TRANSACT_SWAP_AUTH_FIELDS = [
|
|
805
|
+
"close_timed_out",
|
|
806
|
+
"dexes",
|
|
807
|
+
"encrypted_notes",
|
|
808
|
+
"exclude_dexes",
|
|
809
|
+
"max_fee",
|
|
810
|
+
"min_output_amount",
|
|
811
|
+
"output_mint",
|
|
812
|
+
"proof_bytes",
|
|
813
|
+
"public_inputs",
|
|
814
|
+
"recipient",
|
|
815
|
+
"recipient_ata",
|
|
816
|
+
"refund_blinding",
|
|
817
|
+
"refund_pubkey",
|
|
818
|
+
"retry_request_id",
|
|
819
|
+
"risk_quote",
|
|
820
|
+
"route_retry_attempts",
|
|
821
|
+
"sender",
|
|
822
|
+
"slippage_bps",
|
|
823
|
+
"swap_max_retries"
|
|
824
|
+
];
|
|
825
|
+
var UTF8 = new TextEncoder();
|
|
826
|
+
function compareKeysBytewise(a, b) {
|
|
827
|
+
if (a === b) return 0;
|
|
828
|
+
const ab = UTF8.encode(a);
|
|
829
|
+
const bb = UTF8.encode(b);
|
|
830
|
+
const shared = Math.min(ab.length, bb.length);
|
|
831
|
+
for (let i = 0; i < shared; i++) {
|
|
832
|
+
if (ab[i] !== bb[i]) return ab[i] - bb[i];
|
|
833
|
+
}
|
|
834
|
+
return ab.length - bb.length;
|
|
835
|
+
}
|
|
836
|
+
function canonicalJson(value) {
|
|
837
|
+
if (value === null || value === void 0) return "null";
|
|
838
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
839
|
+
if (typeof value === "number") {
|
|
840
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || !Number.isSafeInteger(value)) {
|
|
841
|
+
throw new Error(
|
|
842
|
+
`canonicalJson: refusing to sign the number ${String(value)}. The relay renders numbers with serde_json, which spells non-integer, non-finite and very large values differently from JavaScript, so the two digests would differ and the request would be rejected with an unexplained 401. Send it as a decimal string instead, which is how every amount in this schema travels.`
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
return String(value);
|
|
846
|
+
}
|
|
847
|
+
if (typeof value === "bigint") return value.toString();
|
|
848
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
849
|
+
if (Array.isArray(value)) return "[" + value.map(canonicalJson).join(",") + "]";
|
|
850
|
+
if (typeof value === "object") {
|
|
851
|
+
const obj = value;
|
|
852
|
+
const keys = Object.keys(obj).sort(compareKeysBytewise);
|
|
853
|
+
return "{" + keys.map((k) => {
|
|
854
|
+
const v = obj[k];
|
|
855
|
+
if (typeof v === "function" || typeof v === "symbol") {
|
|
856
|
+
throw new Error(
|
|
857
|
+
`canonicalJson: the key "${k}" holds a ${typeof v}, which has no JSON representation. JSON.stringify would drop it from the wire body while it stayed in the signed view, so the relay's digest could never match this one.`
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
return `${JSON.stringify(k)}:${canonicalJson(v)}`;
|
|
861
|
+
}).join(",") + "}";
|
|
862
|
+
}
|
|
863
|
+
throw new Error(`canonicalJson: unsupported value of type ${typeof value}`);
|
|
864
|
+
}
|
|
865
|
+
var FIELDS_WITHOUT_NULL_ENCODING = {
|
|
866
|
+
slippage_bps: "500 (`default_slippage_bps` in api/transact_swap.rs)"
|
|
867
|
+
};
|
|
868
|
+
function buildAuthRequest(body, sender, fields) {
|
|
869
|
+
const out = {};
|
|
870
|
+
for (const k of fields) {
|
|
871
|
+
if (k === "sender") {
|
|
872
|
+
out[k] = sender;
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
const value = body[k];
|
|
876
|
+
if (value === void 0 || value === null) {
|
|
877
|
+
const relayDefault = FIELDS_WITHOUT_NULL_ENCODING[k];
|
|
878
|
+
if (relayDefault) {
|
|
879
|
+
throw new Error(
|
|
880
|
+
`Relay request auth: \`${k}\` must be present in the body. It is the one field in this schema that is not optional on the relay side: when it is missing the relay signs its default, ${relayDefault}, while this request would sign null. The two digests differ and the relay answers 401 "Relay request signature does not match the exact request", which points at nothing. Set \`${k}\` explicitly, to the same value the body will carry.`
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
out[k] = null;
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
out[k] = value;
|
|
887
|
+
}
|
|
888
|
+
return out;
|
|
889
|
+
}
|
|
890
|
+
function buildRequestAuthMessage(endpoint, programId, issuedAt, nonce, request) {
|
|
891
|
+
const digest = sha256Hex(canonicalJson(request));
|
|
892
|
+
return new TextEncoder().encode(
|
|
893
|
+
`${REQUEST_AUTH_DOMAIN}
|
|
894
|
+
${endpoint}
|
|
895
|
+
${programId.toBase58()}
|
|
896
|
+
${nonce}
|
|
897
|
+
${issuedAt}
|
|
898
|
+
${digest}`
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
function buildRelayAuthPreimage(endpoint, programId, body, sender, nowSeconds, fields = TRANSACT_AUTH_FIELDS) {
|
|
902
|
+
const senderB58 = sender.toBase58();
|
|
903
|
+
const issuedAt = String(nowSeconds ?? Math.floor(Date.now() / 1e3));
|
|
904
|
+
const nonce = randomNonceUuid();
|
|
905
|
+
const request = buildAuthRequest({ ...body, sender: senderB58 }, senderB58, fields);
|
|
906
|
+
const message = buildRequestAuthMessage(endpoint, programId, issuedAt, nonce, request);
|
|
907
|
+
return { sender: senderB58, auth_issued_at: issuedAt, auth_nonce: nonce, message };
|
|
908
|
+
}
|
|
909
|
+
function signRelayRequest(endpoint, programId, body, signer, nowSeconds, fields = TRANSACT_AUTH_FIELDS) {
|
|
910
|
+
const preimage = buildRelayAuthPreimage(
|
|
911
|
+
endpoint,
|
|
912
|
+
programId,
|
|
913
|
+
body,
|
|
914
|
+
signer.publicKey,
|
|
915
|
+
nowSeconds,
|
|
916
|
+
fields
|
|
917
|
+
);
|
|
918
|
+
const signature = nacl2.sign.detached(preimage.message, signer.secretKey);
|
|
919
|
+
return {
|
|
920
|
+
sender: preimage.sender,
|
|
921
|
+
auth_issued_at: preimage.auth_issued_at,
|
|
922
|
+
auth_nonce: preimage.auth_nonce,
|
|
923
|
+
auth_signature: Buffer.from(signature).toString("base64")
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
function buildAuthTransactionMessage(sender, digest) {
|
|
927
|
+
if (digest.length !== 32) throw new Error(`auth transaction digest must be 32 bytes (got ${digest.length})`);
|
|
928
|
+
const tx = new Transaction();
|
|
929
|
+
tx.add(SystemProgram.transfer({ fromPubkey: sender, toPubkey: sender, lamports: 0 }));
|
|
930
|
+
tx.feePayer = sender;
|
|
931
|
+
tx.recentBlockhash = new PublicKey3(digest).toBase58();
|
|
932
|
+
return tx;
|
|
933
|
+
}
|
|
934
|
+
function serializeAuthTransactionMessage(sender, digest) {
|
|
935
|
+
return Uint8Array.from(buildAuthTransactionMessage(sender, digest).compileMessage().serialize());
|
|
936
|
+
}
|
|
937
|
+
async function signRelayAuthPayload(signer, payload) {
|
|
938
|
+
if (signer instanceof Keypair) {
|
|
939
|
+
return { signature: nacl2.sign.detached(payload, signer.secretKey), mode: "message" };
|
|
940
|
+
}
|
|
941
|
+
if (signer.signMessage) {
|
|
942
|
+
const signature = await signer.signMessage(payload);
|
|
943
|
+
assertDetachedSignature(signature, "signMessage");
|
|
944
|
+
return { signature, mode: "message" };
|
|
945
|
+
}
|
|
946
|
+
if (signer.signAuthTransaction) {
|
|
947
|
+
const digest = sha256(payload);
|
|
948
|
+
const signed = await signer.signAuthTransaction(buildAuthTransactionMessage(signer.walletPublicKey, digest));
|
|
949
|
+
const own = signed.signatures.find((s) => s.publicKey.equals(signer.walletPublicKey))?.signature;
|
|
950
|
+
if (!own) {
|
|
951
|
+
throw new Error(
|
|
952
|
+
"The wallet returned no signature for the Cloak auth transaction. Nothing was submitted."
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
const signature = Uint8Array.from(own);
|
|
956
|
+
assertDetachedSignature(signature, "signTransaction");
|
|
957
|
+
return { signature, mode: "transaction" };
|
|
958
|
+
}
|
|
959
|
+
throw new Error("Relay auth signer has neither signMessage nor signAuthTransaction.");
|
|
960
|
+
}
|
|
961
|
+
function assertDetachedSignature(signature, method) {
|
|
962
|
+
if (!(signature instanceof Uint8Array) || signature.length !== 64) {
|
|
963
|
+
throw new Error(
|
|
964
|
+
`The wallet's ${method} did not return a 64-byte ed25519 detached signature (got ${signature instanceof Uint8Array ? `${signature.length} bytes` : typeof signature}). Cloak signs with a raw detached signature over the bytes it hands the wallet; adapters that wrap or re-encode the result cannot be used here.`
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
function assertApprovalWithinFreshnessWindow(elapsedMs, maxAgeSeconds = REQUEST_AUTH_MAX_AGE_SECONDS) {
|
|
969
|
+
const elapsedSeconds = elapsedMs / 1e3;
|
|
970
|
+
const budget = maxAgeSeconds - RELAY_AUTH_APPROVAL_MARGIN_SECONDS;
|
|
971
|
+
if (elapsedSeconds <= budget) return;
|
|
972
|
+
throw new Error(
|
|
973
|
+
`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.`
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
function explainRelayAuthRejection(responseText) {
|
|
977
|
+
const text = String(responseText);
|
|
978
|
+
const has = (needle) => text.includes(needle);
|
|
979
|
+
if (has("issued too far in the future")) {
|
|
980
|
+
return `This computer's clock is more than ${REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS} seconds ahead of the network's, so the request looks like it was signed in the future and is refused before anything else happens. Turn on automatic date and time (or resynchronise the clock) and try again. Nothing was submitted.`;
|
|
981
|
+
}
|
|
982
|
+
if (has("signature expired")) {
|
|
983
|
+
return `The request was signed more than ${REQUEST_AUTH_MAX_AGE_SECONDS} seconds before it arrived, usually because a wallet approval was left waiting, or because this computer's clock is running behind. The signed timestamp cannot be refreshed on its own. Start the operation again. Nothing was submitted.`;
|
|
984
|
+
}
|
|
985
|
+
if (has("does not match the exact request")) {
|
|
986
|
+
return `The request body changed after it was signed, so the relay's digest and this one disagree. Sign the exact body that is POSTed, and re-POST a retry byte for byte rather than rebuilding it. If this is a hand-built swap call, check that every field in TRANSACT_SWAP_AUTH_FIELDS is present, including \`slippage_bps\`.`;
|
|
987
|
+
}
|
|
988
|
+
if (has("has no registered viewing key")) {
|
|
989
|
+
return `The signature was accepted, so this is not a signing problem: the wallet that signed has no viewing key registered with Cloak yet. Register one first, or let the SDK do it by leaving \`enforceViewingKeyRegistration\` on and supplying the viewing key.`;
|
|
990
|
+
}
|
|
991
|
+
if (has("Authenticated sender is required") || has("auth_issued_at is required") || has("auth_nonce is required") || has("auth_signature is required")) {
|
|
992
|
+
return `The request reached the relay without its authentication fields. Pass \`depositorKeypair\`, or \`signMessage\` together with \`walletPublicKey\`, so the request can be signed.`;
|
|
993
|
+
}
|
|
994
|
+
if (has("auth_nonce must be a canonical UUID")) {
|
|
995
|
+
return `\`auth_nonce\` must be a canonical lowercase UUID. Reuse the one from the preimage.`;
|
|
996
|
+
}
|
|
997
|
+
if (has("Relay request signature must be exactly 64 bytes") || has("Invalid relay request signature encoding")) {
|
|
998
|
+
return `\`auth_signature\` must be the raw 64-byte ed25519 detached signature, base64 encoded. Wallet adapters that wrap or re-encode what \`signMessage\` returns cannot be used here.`;
|
|
999
|
+
}
|
|
1000
|
+
return null;
|
|
1001
|
+
}
|
|
1002
|
+
async function buildRelayAuthFields(endpoint, programId, body, signers, fields = TRANSACT_AUTH_FIELDS) {
|
|
1003
|
+
if (signers.depositorKeypair) {
|
|
1004
|
+
return signRelayRequest(endpoint, programId, body, signers.depositorKeypair, void 0, fields);
|
|
1005
|
+
}
|
|
1006
|
+
const wallet = signers.relayAuthSigner;
|
|
1007
|
+
if (!wallet) return null;
|
|
1008
|
+
const preimage = buildRelayAuthPreimage(
|
|
1009
|
+
endpoint,
|
|
1010
|
+
programId,
|
|
1011
|
+
body,
|
|
1012
|
+
wallet.walletPublicKey,
|
|
1013
|
+
void 0,
|
|
1014
|
+
fields
|
|
1015
|
+
);
|
|
1016
|
+
const approvalStartedMs = Date.now();
|
|
1017
|
+
const { signature, mode } = await signRelayAuthPayload(wallet, preimage.message);
|
|
1018
|
+
assertApprovalWithinFreshnessWindow(Date.now() - approvalStartedMs);
|
|
1019
|
+
return {
|
|
1020
|
+
sender: preimage.sender,
|
|
1021
|
+
auth_issued_at: preimage.auth_issued_at,
|
|
1022
|
+
auth_nonce: preimage.auth_nonce,
|
|
1023
|
+
auth_signature: Buffer.from(signature).toString("base64"),
|
|
1024
|
+
...mode === "transaction" ? { auth_mode: mode } : {}
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
var REQUEST_AUTH_BATCH_DOMAIN = "CLOAK_RELAY_BATCH_AUTH_V1";
|
|
1028
|
+
var REQUEST_AUTH_BATCH_MAX_AGE_SECONDS = 600;
|
|
1029
|
+
var RELAY_BATCH_AUTH_MAX_ITEMS = 64;
|
|
1030
|
+
function relayRequestDigestHex(preimage) {
|
|
1031
|
+
return bytesToHex2(sha256(preimage.message));
|
|
1032
|
+
}
|
|
1033
|
+
function buildRelayBatchAuthMessage(programId, issuedAt, digests) {
|
|
1034
|
+
const lines = [REQUEST_AUTH_BATCH_DOMAIN, programId.toBase58(), issuedAt, String(digests.length)];
|
|
1035
|
+
return new TextEncoder().encode([...lines, ...digests].join("\n"));
|
|
1036
|
+
}
|
|
1037
|
+
|
|
769
1038
|
// src/scanning/compliance-keys.ts
|
|
770
1039
|
var X25519_KEY_LENGTH = 32;
|
|
771
1040
|
var DIVERSIFIER_LENGTH = 11;
|
|
@@ -803,7 +1072,7 @@ function deriveViewingKeyFromNk(nk) {
|
|
|
803
1072
|
preimage.set(nk, CHAIN_NOTE_VK_DOMAIN.length);
|
|
804
1073
|
const derived = blake32(preimage);
|
|
805
1074
|
const privateKey = clampX25519Secret(derived);
|
|
806
|
-
const publicKey =
|
|
1075
|
+
const publicKey = nacl3.scalarMult.base(privateKey);
|
|
807
1076
|
return { privateKey, publicKey };
|
|
808
1077
|
}
|
|
809
1078
|
function deriveDiversifier(nk, commitmentHex, outputIndex) {
|
|
@@ -838,7 +1107,7 @@ function deriveDiversifiedViewingKey(nk, diversifier) {
|
|
|
838
1107
|
preimage.set(diversifier, SK_D_DOMAIN.length + nk.length);
|
|
839
1108
|
const derived = blake32(preimage);
|
|
840
1109
|
const privateKey = clampX25519Secret(derived);
|
|
841
|
-
const publicKey =
|
|
1110
|
+
const publicKey = nacl3.scalarMult.base(privateKey);
|
|
842
1111
|
return { privateKey, publicKey };
|
|
843
1112
|
}
|
|
844
1113
|
function deriveViewingKeyFromSpendKey(skSpend) {
|
|
@@ -901,19 +1170,19 @@ async function deriveUserCompliancePublicKey(masterCompliancePublicKey, userPubl
|
|
|
901
1170
|
masterCompliancePublicKey,
|
|
902
1171
|
userPublicKey
|
|
903
1172
|
);
|
|
904
|
-
return
|
|
1173
|
+
return nacl3.scalarMult(factor, masterCompliancePublicKey);
|
|
905
1174
|
}
|
|
906
|
-
function
|
|
1175
|
+
function bytesToHex3(bytes) {
|
|
907
1176
|
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
908
1177
|
}
|
|
909
1178
|
function computeViewingKeyIdentifier(userPubkey, nkHex) {
|
|
910
1179
|
const preimage = new TextEncoder().encode(`${userPubkey.toBase58()}${nkHex}`);
|
|
911
|
-
return
|
|
1180
|
+
return bytesToHex3(sha2562(preimage));
|
|
912
1181
|
}
|
|
913
1182
|
var SIGN_IN_MESSAGE = "Cloak: Sign in\n\nSign this message to securely access your Cloak account.\nThis does NOT authorize any transaction or spend any funds.";
|
|
914
1183
|
async function registerViewingKey(relayUrl, userPubkey, nk, signMessage) {
|
|
915
1184
|
assert32Bytes(nk, "nk");
|
|
916
|
-
const nkHex =
|
|
1185
|
+
const nkHex = bytesToHex3(nk);
|
|
917
1186
|
const keyId = computeViewingKeyIdentifier(userPubkey, nkHex);
|
|
918
1187
|
const challengeResponse = await relayFetch(`${relayUrl}/viewing-key/challenge`, {
|
|
919
1188
|
method: "POST",
|
|
@@ -934,8 +1203,11 @@ async function registerViewingKey(relayUrl, userPubkey, nk, signMessage) {
|
|
|
934
1203
|
nkHex
|
|
935
1204
|
);
|
|
936
1205
|
const messageBytes = new TextEncoder().encode(challenge.message);
|
|
937
|
-
const
|
|
938
|
-
|
|
1206
|
+
const signed = await signRelayAuthPayload(
|
|
1207
|
+
typeof signMessage === "function" ? { walletPublicKey: userPubkey, signMessage } : signMessage,
|
|
1208
|
+
messageBytes
|
|
1209
|
+
);
|
|
1210
|
+
const signature = Buffer.from(signed.signature).toString("base64");
|
|
939
1211
|
const response = await relayFetch(`${relayUrl}/viewing-key/register`, {
|
|
940
1212
|
method: "POST",
|
|
941
1213
|
headers: { "Content-Type": "application/json" },
|
|
@@ -944,7 +1216,8 @@ async function registerViewingKey(relayUrl, userPubkey, nk, signMessage) {
|
|
|
944
1216
|
viewing_key: nkHex,
|
|
945
1217
|
nonce: challenge.nonce,
|
|
946
1218
|
identifier: keyId,
|
|
947
|
-
signature
|
|
1219
|
+
signature,
|
|
1220
|
+
...signed.mode === "transaction" ? { auth_mode: signed.mode } : {}
|
|
948
1221
|
})
|
|
949
1222
|
});
|
|
950
1223
|
if (!response.ok) {
|
|
@@ -953,7 +1226,7 @@ async function registerViewingKey(relayUrl, userPubkey, nk, signMessage) {
|
|
|
953
1226
|
}
|
|
954
1227
|
|
|
955
1228
|
// src/scanning/metadata-encryption.ts
|
|
956
|
-
import
|
|
1229
|
+
import nacl4 from "tweetnacl";
|
|
957
1230
|
var AES_GCM_NONCE_LENGTH = 12;
|
|
958
1231
|
var AES_GCM_TAG_LENGTH = 16;
|
|
959
1232
|
var X25519_KEY_LENGTH2 = 32;
|
|
@@ -1050,8 +1323,8 @@ async function aesGcmDecrypt(ciphertext, keyBytes, nonce) {
|
|
|
1050
1323
|
}
|
|
1051
1324
|
async function encryptForRecipient(plaintext, recipientPublicKey) {
|
|
1052
1325
|
assert32Bytes2(recipientPublicKey, "recipientPublicKey");
|
|
1053
|
-
const ephemeral =
|
|
1054
|
-
const sharedSecret =
|
|
1326
|
+
const ephemeral = nacl4.box.keyPair();
|
|
1327
|
+
const sharedSecret = nacl4.scalarMult(ephemeral.secretKey, recipientPublicKey);
|
|
1055
1328
|
const nonce = randomBytes(AES_GCM_NONCE_LENGTH);
|
|
1056
1329
|
const ciphertext = await aesGcmEncrypt(plaintext, sharedSecret, nonce);
|
|
1057
1330
|
return {
|
|
@@ -1110,7 +1383,7 @@ async function encryptTransactionMetadataBundle(metadata, viewingKeyPrivate, use
|
|
|
1110
1383
|
const plaintext = new TextEncoder().encode(JSON.stringify(metadata));
|
|
1111
1384
|
const basePoint = new Uint8Array(32);
|
|
1112
1385
|
basePoint[0] = 9;
|
|
1113
|
-
const viewingKeyPublic =
|
|
1386
|
+
const viewingKeyPublic = nacl4.scalarMult(viewingKeyPrivate, basePoint);
|
|
1114
1387
|
const [userPayload, compliancePayload] = await Promise.all([
|
|
1115
1388
|
encryptForRecipient(plaintext, viewingKeyPublic),
|
|
1116
1389
|
encryptForRecipient(plaintext, viewingKeyPublic)
|
|
@@ -1129,7 +1402,7 @@ async function decryptTransactionMetadata(encrypted, viewKeySecret) {
|
|
|
1129
1402
|
const payload = decodePayload(encrypted);
|
|
1130
1403
|
const ephemeralPk = hexToBytes(payload.ephemeral_pk);
|
|
1131
1404
|
assert32Bytes2(ephemeralPk, "ephemeral public key");
|
|
1132
|
-
const sharedSecret =
|
|
1405
|
+
const sharedSecret = nacl4.scalarMult(viewKeySecret, ephemeralPk);
|
|
1133
1406
|
return decryptWithSharedSecret(payload, sharedSecret);
|
|
1134
1407
|
}
|
|
1135
1408
|
async function decryptComplianceMetadataWithMasterKey(encrypted, masterCompliancePrivateKey, masterCompliancePublicKey, userPublicKey) {
|
|
@@ -1142,8 +1415,8 @@ async function decryptComplianceMetadataWithMasterKey(encrypted, masterComplianc
|
|
|
1142
1415
|
masterCompliancePublicKey,
|
|
1143
1416
|
userPublicKey
|
|
1144
1417
|
);
|
|
1145
|
-
const scaledEphemeral =
|
|
1146
|
-
const sharedSecret =
|
|
1418
|
+
const scaledEphemeral = nacl4.scalarMult(userFactor, ephemeralPk);
|
|
1419
|
+
const sharedSecret = nacl4.scalarMult(masterCompliancePrivateKey, scaledEphemeral);
|
|
1147
1420
|
return decryptWithSharedSecret(payload, sharedSecret);
|
|
1148
1421
|
}
|
|
1149
1422
|
|
|
@@ -2073,22 +2346,22 @@ function formatErrorForLogging(error) {
|
|
|
2073
2346
|
}
|
|
2074
2347
|
|
|
2075
2348
|
// src/program/pda.ts
|
|
2076
|
-
import { PublicKey as
|
|
2349
|
+
import { PublicKey as PublicKey4 } from "@solana/web3.js";
|
|
2077
2350
|
function getShieldPoolPDAs(programId, mint = NATIVE_SOL_MINT) {
|
|
2078
2351
|
const pid = programId || CLOAK_PROGRAM_ID;
|
|
2079
|
-
const [pool] =
|
|
2352
|
+
const [pool] = PublicKey4.findProgramAddressSync(
|
|
2080
2353
|
[Buffer.from("pool"), mint.toBuffer()],
|
|
2081
2354
|
pid
|
|
2082
2355
|
);
|
|
2083
|
-
const [merkleTree] =
|
|
2356
|
+
const [merkleTree] = PublicKey4.findProgramAddressSync(
|
|
2084
2357
|
[Buffer.from("merkle_tree"), mint.toBuffer()],
|
|
2085
2358
|
pid
|
|
2086
2359
|
);
|
|
2087
|
-
const [treasury] =
|
|
2360
|
+
const [treasury] = PublicKey4.findProgramAddressSync(
|
|
2088
2361
|
[Buffer.from("treasury"), mint.toBuffer()],
|
|
2089
2362
|
pid
|
|
2090
2363
|
);
|
|
2091
|
-
const [vaultAuthority] =
|
|
2364
|
+
const [vaultAuthority] = PublicKey4.findProgramAddressSync(
|
|
2092
2365
|
[Buffer.from("vault_authority"), mint.toBuffer()],
|
|
2093
2366
|
pid
|
|
2094
2367
|
);
|
|
@@ -2104,7 +2377,7 @@ function getNullifierPDA(poolPubkey, nullifier, programId) {
|
|
|
2104
2377
|
if (nullifier.length !== 32) {
|
|
2105
2378
|
throw new Error(`Nullifier must be 32 bytes, got ${nullifier.length}`);
|
|
2106
2379
|
}
|
|
2107
|
-
return
|
|
2380
|
+
return PublicKey4.findProgramAddressSync(
|
|
2108
2381
|
[Buffer.from("nullifier"), poolPubkey.toBuffer(), Buffer.from(nullifier)],
|
|
2109
2382
|
pid
|
|
2110
2383
|
);
|
|
@@ -2114,25 +2387,25 @@ function getSwapStatePDA(poolPubkey, nullifier, programId) {
|
|
|
2114
2387
|
if (nullifier.length !== 32) {
|
|
2115
2388
|
throw new Error(`Nullifier must be 32 bytes, got ${nullifier.length}`);
|
|
2116
2389
|
}
|
|
2117
|
-
return
|
|
2390
|
+
return PublicKey4.findProgramAddressSync(
|
|
2118
2391
|
[Buffer.from("swap_state"), poolPubkey.toBuffer(), Buffer.from(nullifier)],
|
|
2119
2392
|
pid
|
|
2120
2393
|
);
|
|
2121
2394
|
}
|
|
2122
2395
|
function getPoolAuthorityConfigPDA(mint = NATIVE_SOL_MINT, programId) {
|
|
2123
2396
|
const pid = programId || CLOAK_PROGRAM_ID;
|
|
2124
|
-
return
|
|
2397
|
+
return PublicKey4.findProgramAddressSync(
|
|
2125
2398
|
[Buffer.from("pool_authority"), mint.toBuffer()],
|
|
2126
2399
|
pid
|
|
2127
2400
|
);
|
|
2128
2401
|
}
|
|
2129
2402
|
function getDeliveryRegistryPDA(programId) {
|
|
2130
2403
|
const pid = programId || CLOAK_PROGRAM_ID;
|
|
2131
|
-
return
|
|
2404
|
+
return PublicKey4.findProgramAddressSync([Buffer.from("cloak_delivery_registry")], pid)[0];
|
|
2132
2405
|
}
|
|
2133
2406
|
function getChainNoteRegistryPDA(programId) {
|
|
2134
2407
|
const pid = programId || CLOAK_PROGRAM_ID;
|
|
2135
|
-
return
|
|
2408
|
+
return PublicKey4.findProgramAddressSync([Buffer.from("cloak_chain_note_registry")], pid)[0];
|
|
2136
2409
|
}
|
|
2137
2410
|
|
|
2138
2411
|
// src/flows/verify-utxos.ts
|
|
@@ -2463,7 +2736,8 @@ function truncate(str, len = 20) {
|
|
|
2463
2736
|
var sdkLogger = createLogger("cloak::sdk");
|
|
2464
2737
|
|
|
2465
2738
|
// src/relay/relay-service.ts
|
|
2466
|
-
import { sha256 as
|
|
2739
|
+
import { sha256 as sha2563 } from "@noble/hashes/sha2";
|
|
2740
|
+
import { PublicKey as PublicKey5 } from "@solana/web3.js";
|
|
2467
2741
|
var RelayService = class {
|
|
2468
2742
|
/**
|
|
2469
2743
|
* Create a new Relay Service client
|
|
@@ -2779,8 +3053,11 @@ var RelayService = class {
|
|
|
2779
3053
|
userPubkey,
|
|
2780
3054
|
viewingKey
|
|
2781
3055
|
);
|
|
2782
|
-
const
|
|
2783
|
-
|
|
3056
|
+
const signed = await signRelayAuthPayload(
|
|
3057
|
+
typeof signMessage === "function" ? { walletPublicKey: new PublicKey5(userPubkey), signMessage } : signMessage,
|
|
3058
|
+
new TextEncoder().encode(challenge.message)
|
|
3059
|
+
);
|
|
3060
|
+
const signature = this.bytesToBase64(signed.signature);
|
|
2784
3061
|
const response = await relayFetch(`${this.baseUrl}/viewing-key/register`, {
|
|
2785
3062
|
method: "POST",
|
|
2786
3063
|
headers: { "Content-Type": "application/json" },
|
|
@@ -2789,7 +3066,8 @@ var RelayService = class {
|
|
|
2789
3066
|
viewing_key: viewingKey,
|
|
2790
3067
|
nonce: challenge.nonce,
|
|
2791
3068
|
identifier,
|
|
2792
|
-
signature
|
|
3069
|
+
signature,
|
|
3070
|
+
...signed.mode === "transaction" ? { auth_mode: signed.mode } : {}
|
|
2793
3071
|
})
|
|
2794
3072
|
});
|
|
2795
3073
|
if (!response.ok) {
|
|
@@ -2827,7 +3105,7 @@ var RelayService = class {
|
|
|
2827
3105
|
}
|
|
2828
3106
|
computeViewingKeyIdentifier(userPubkey, viewingKeyHex) {
|
|
2829
3107
|
const preimage = new TextEncoder().encode(`${userPubkey}${viewingKeyHex}`);
|
|
2830
|
-
const hash =
|
|
3108
|
+
const hash = sha2563(preimage);
|
|
2831
3109
|
return Array.from(hash).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2832
3110
|
}
|
|
2833
3111
|
};
|
|
@@ -2945,10 +3223,10 @@ function encodeNoteSimple(note) {
|
|
|
2945
3223
|
|
|
2946
3224
|
// src/wallet/adapter.ts
|
|
2947
3225
|
import {
|
|
2948
|
-
Keypair
|
|
3226
|
+
Keypair as Keypair2
|
|
2949
3227
|
} from "@solana/web3.js";
|
|
2950
3228
|
function validateWalletConnected(wallet) {
|
|
2951
|
-
if (wallet instanceof
|
|
3229
|
+
if (wallet instanceof Keypair2) {
|
|
2952
3230
|
return;
|
|
2953
3231
|
}
|
|
2954
3232
|
if (!wallet.publicKey) {
|
|
@@ -2960,7 +3238,7 @@ function validateWalletConnected(wallet) {
|
|
|
2960
3238
|
}
|
|
2961
3239
|
}
|
|
2962
3240
|
function getPublicKey(wallet) {
|
|
2963
|
-
if (wallet instanceof
|
|
3241
|
+
if (wallet instanceof Keypair2) {
|
|
2964
3242
|
return wallet.publicKey;
|
|
2965
3243
|
}
|
|
2966
3244
|
if (!wallet.publicKey) {
|
|
@@ -2974,7 +3252,7 @@ function getPublicKey(wallet) {
|
|
|
2974
3252
|
}
|
|
2975
3253
|
async function sendTransaction(transaction, wallet, connection, options) {
|
|
2976
3254
|
assertAllowedRpcConnection(connection);
|
|
2977
|
-
if (wallet instanceof
|
|
3255
|
+
if (wallet instanceof Keypair2) {
|
|
2978
3256
|
return await connection.sendTransaction(transaction, [wallet], options);
|
|
2979
3257
|
}
|
|
2980
3258
|
if (wallet.sendTransaction) {
|
|
@@ -2991,7 +3269,7 @@ async function sendTransaction(transaction, wallet, connection, options) {
|
|
|
2991
3269
|
}
|
|
2992
3270
|
}
|
|
2993
3271
|
async function signTransaction(transaction, wallet) {
|
|
2994
|
-
if (wallet instanceof
|
|
3272
|
+
if (wallet instanceof Keypair2) {
|
|
2995
3273
|
transaction.sign(wallet);
|
|
2996
3274
|
return transaction;
|
|
2997
3275
|
}
|
|
@@ -3020,8 +3298,8 @@ function keypairToAdapter(keypair) {
|
|
|
3020
3298
|
|
|
3021
3299
|
// src/program/instructions.ts
|
|
3022
3300
|
import {
|
|
3023
|
-
PublicKey as
|
|
3024
|
-
SystemProgram,
|
|
3301
|
+
PublicKey as PublicKey7,
|
|
3302
|
+
SystemProgram as SystemProgram2,
|
|
3025
3303
|
TransactionInstruction
|
|
3026
3304
|
} from "@solana/web3.js";
|
|
3027
3305
|
function createDepositInstruction(params) {
|
|
@@ -3053,7 +3331,7 @@ function createDepositInstruction(params) {
|
|
|
3053
3331
|
// Account 1: Pool (writable) - receives SOL
|
|
3054
3332
|
{ pubkey: params.pool, isSigner: false, isWritable: true },
|
|
3055
3333
|
// Account 2: System Program (readonly) - for transfers
|
|
3056
|
-
{ pubkey:
|
|
3334
|
+
{ pubkey: SystemProgram2.programId, isSigner: false, isWritable: false },
|
|
3057
3335
|
// Account 3: Merkle Tree (writable) - stores on-chain Merkle tree
|
|
3058
3336
|
{ pubkey: params.merkleTree, isSigner: false, isWritable: true }
|
|
3059
3337
|
],
|
|
@@ -3061,16 +3339,16 @@ function createDepositInstruction(params) {
|
|
|
3061
3339
|
});
|
|
3062
3340
|
}
|
|
3063
3341
|
function validateDepositParams(params) {
|
|
3064
|
-
if (!(params.programId instanceof
|
|
3342
|
+
if (!(params.programId instanceof PublicKey7)) {
|
|
3065
3343
|
throw new Error("programId must be a PublicKey");
|
|
3066
3344
|
}
|
|
3067
|
-
if (!(params.payer instanceof
|
|
3345
|
+
if (!(params.payer instanceof PublicKey7)) {
|
|
3068
3346
|
throw new Error("payer must be a PublicKey");
|
|
3069
3347
|
}
|
|
3070
|
-
if (!(params.pool instanceof
|
|
3348
|
+
if (!(params.pool instanceof PublicKey7)) {
|
|
3071
3349
|
throw new Error("pool must be a PublicKey");
|
|
3072
3350
|
}
|
|
3073
|
-
if (!(params.merkleTree instanceof
|
|
3351
|
+
if (!(params.merkleTree instanceof PublicKey7)) {
|
|
3074
3352
|
throw new Error("merkleTree must be a PublicKey");
|
|
3075
3353
|
}
|
|
3076
3354
|
if (typeof params.amount !== "number" || params.amount <= 0) {
|
|
@@ -3410,7 +3688,7 @@ async function buildMerkleTree(commitments, height = MERKLE_TREE_HEIGHT2) {
|
|
|
3410
3688
|
}
|
|
3411
3689
|
|
|
3412
3690
|
// src/relay/client.ts
|
|
3413
|
-
import { PublicKey as
|
|
3691
|
+
import { PublicKey as PublicKey8 } from "@solana/web3.js";
|
|
3414
3692
|
import _bs58 from "bs58";
|
|
3415
3693
|
|
|
3416
3694
|
// src/notes/refund-leaf.ts
|
|
@@ -3527,7 +3805,7 @@ var CLOSE_SWAP_STATE_TAG = 13;
|
|
|
3527
3805
|
var CLOSE_SWAP_STATE_PHASE1_PAYLOAD_LEN = 40;
|
|
3528
3806
|
var CLOSE_SWAP_STATE_PHASE1_WIRE_LEN = 1 + CLOSE_SWAP_STATE_PHASE1_PAYLOAD_LEN;
|
|
3529
3807
|
var CLOSE_SWAP_STATE_COMMITMENT_END = 1 + 32;
|
|
3530
|
-
var NATIVE_SOL_MINT2 = new
|
|
3808
|
+
var NATIVE_SOL_MINT2 = new PublicKey8("So11111111111111111111111111111111111111112");
|
|
3531
3809
|
var PROOF_LEN = 256;
|
|
3532
3810
|
var PUBLIC_INPUTS_LEN = 264;
|
|
3533
3811
|
var COMMITMENTS_OFFSET = 168;
|
|
@@ -3817,11 +4095,11 @@ async function preflightCheck(relayUrl, rootHex) {
|
|
|
3817
4095
|
|
|
3818
4096
|
// src/flows/transact.ts
|
|
3819
4097
|
import {
|
|
3820
|
-
PublicKey as
|
|
3821
|
-
Transaction as
|
|
4098
|
+
PublicKey as PublicKey10,
|
|
4099
|
+
Transaction as Transaction3,
|
|
3822
4100
|
TransactionInstruction as TransactionInstruction2,
|
|
3823
4101
|
sendAndConfirmTransaction,
|
|
3824
|
-
SystemProgram as
|
|
4102
|
+
SystemProgram as SystemProgram3,
|
|
3825
4103
|
ComputeBudgetProgram,
|
|
3826
4104
|
SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
3827
4105
|
SYSVAR_SLOT_HASHES_PUBKEY,
|
|
@@ -4060,7 +4338,7 @@ function chainNoteFromBase64(base64) {
|
|
|
4060
4338
|
}
|
|
4061
4339
|
|
|
4062
4340
|
// src/notes/delivery-note.ts
|
|
4063
|
-
import
|
|
4341
|
+
import nacl5 from "tweetnacl";
|
|
4064
4342
|
var RECIPIENT_DELIVERY_EPHEMERAL_PK_LEN = 32;
|
|
4065
4343
|
var RECIPIENT_DELIVERY_NONCE_LEN = 24;
|
|
4066
4344
|
var RECIPIENT_DELIVERY_PLAINTEXT_LEN = 40;
|
|
@@ -4111,10 +4389,10 @@ function encodeRecipientDeliveryNote(note, recipientViewingPublicKey) {
|
|
|
4111
4389
|
const plaintext = new Uint8Array(RECIPIENT_DELIVERY_PLAINTEXT_LEN);
|
|
4112
4390
|
writeU64LE2(plaintext, 0, note.amount);
|
|
4113
4391
|
writeU256BE2(plaintext, 8, note.blinding);
|
|
4114
|
-
const ephemeral =
|
|
4115
|
-
const shared =
|
|
4116
|
-
const nonce =
|
|
4117
|
-
const ciphertext =
|
|
4392
|
+
const ephemeral = nacl5.box.keyPair();
|
|
4393
|
+
const shared = nacl5.box.before(recipientViewingPublicKey, ephemeral.secretKey);
|
|
4394
|
+
const nonce = nacl5.randomBytes(nacl5.secretbox.nonceLength);
|
|
4395
|
+
const ciphertext = nacl5.secretbox(plaintext, nonce, shared);
|
|
4118
4396
|
if (ciphertext.length !== RECIPIENT_DELIVERY_CIPHERTEXT_LEN) {
|
|
4119
4397
|
throw new Error(
|
|
4120
4398
|
`delivery ciphertext must be ${RECIPIENT_DELIVERY_CIPHERTEXT_LEN} bytes, got ${ciphertext.length}`
|
|
@@ -4140,8 +4418,8 @@ function openRecipientDeliveryNote(envelope, viewingSecretKey) {
|
|
|
4140
4418
|
const ciphertext = envelope.slice(
|
|
4141
4419
|
RECIPIENT_DELIVERY_EPHEMERAL_PK_LEN + RECIPIENT_DELIVERY_NONCE_LEN
|
|
4142
4420
|
);
|
|
4143
|
-
const shared =
|
|
4144
|
-
const plaintext =
|
|
4421
|
+
const shared = nacl5.box.before(ephemeralPk, viewingSecretKey);
|
|
4422
|
+
const plaintext = nacl5.secretbox.open(ciphertext, nonce, shared);
|
|
4145
4423
|
if (!plaintext || plaintext.length !== RECIPIENT_DELIVERY_PLAINTEXT_LEN) return null;
|
|
4146
4424
|
return { amount: readU64LE2(plaintext, 0), blinding: readU256BE2(plaintext, 8) };
|
|
4147
4425
|
} catch {
|
|
@@ -4199,7 +4477,7 @@ function parseDeliveryCarrierMemo(data) {
|
|
|
4199
4477
|
|
|
4200
4478
|
// src/notes/swap-refund.ts
|
|
4201
4479
|
import { blake3 as blake33 } from "@noble/hashes/blake3";
|
|
4202
|
-
import { PublicKey as
|
|
4480
|
+
import { PublicKey as PublicKey9 } from "@solana/web3.js";
|
|
4203
4481
|
import _bs582 from "bs58";
|
|
4204
4482
|
var bs582 = _bs582.default || _bs582;
|
|
4205
4483
|
var REFUND_SEED_DOMAIN = new TextEncoder().encode("cloak_swap_refund_v1");
|
|
@@ -4344,7 +4622,7 @@ async function discoverSwapRefunds(connection, programId, viewingKeyNk, options
|
|
|
4344
4622
|
const { limit = 0, untilSignature, batchSize = 50, onStatus } = options;
|
|
4345
4623
|
const poolMint = options.poolMint ?? NATIVE_SOL_MINT;
|
|
4346
4624
|
const programIdBase58 = programId.toBase58();
|
|
4347
|
-
const [pool] =
|
|
4625
|
+
const [pool] = PublicKey9.findProgramAddressSync(
|
|
4348
4626
|
[Buffer.from("pool"), poolMint.toBuffer()],
|
|
4349
4627
|
programId
|
|
4350
4628
|
);
|
|
@@ -4395,7 +4673,7 @@ async function discoverSwapRefunds(connection, programId, viewingKeyNk, options
|
|
|
4395
4673
|
if (data[0] !== TRANSACT_SWAP_TAG2 || data.length < MIN_TRANSACT_SWAP_LEN) continue;
|
|
4396
4674
|
const nullifier = data.slice(NULLIFIER_0_OFFSET, NULLIFIER_0_OFFSET + 32);
|
|
4397
4675
|
if (nullifier.length !== 32) continue;
|
|
4398
|
-
const [swapState] =
|
|
4676
|
+
const [swapState] = PublicKey9.findProgramAddressSync(
|
|
4399
4677
|
[Buffer.from("swap_state"), pool.toBuffer(), Buffer.from(nullifier)],
|
|
4400
4678
|
programId
|
|
4401
4679
|
);
|
|
@@ -4622,236 +4900,6 @@ async function matchChangeNote(params) {
|
|
|
4622
4900
|
import { buildPoseidon as buildPoseidon2 } from "circomlibjs";
|
|
4623
4901
|
import nacl6 from "tweetnacl";
|
|
4624
4902
|
|
|
4625
|
-
// src/relay/payload.ts
|
|
4626
|
-
import { sha256 as sha2563 } from "@noble/hashes/sha2";
|
|
4627
|
-
import { bytesToHex as bytesToHex3 } from "@noble/hashes/utils";
|
|
4628
|
-
import nacl5 from "tweetnacl";
|
|
4629
|
-
var REQUEST_AUTH_DOMAIN = "CLOAK_RELAY_REQUEST_AUTH_V1";
|
|
4630
|
-
var REQUEST_AUTH_MAX_AGE_SECONDS = 300;
|
|
4631
|
-
var REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS = 30;
|
|
4632
|
-
var RELAY_AUTH_APPROVAL_MARGIN_SECONDS = 15;
|
|
4633
|
-
function sha256Hex(text) {
|
|
4634
|
-
return bytesToHex3(sha2563(new TextEncoder().encode(text)));
|
|
4635
|
-
}
|
|
4636
|
-
function randomNonceUuid() {
|
|
4637
|
-
const webCrypto = globalThis?.crypto;
|
|
4638
|
-
if (typeof webCrypto?.randomUUID === "function") return webCrypto.randomUUID();
|
|
4639
|
-
const proc = globalThis?.process;
|
|
4640
|
-
const nodeCrypto = typeof proc?.getBuiltinModule === "function" ? proc.getBuiltinModule("node:crypto") : void 0;
|
|
4641
|
-
if (typeof nodeCrypto?.randomUUID === "function") return nodeCrypto.randomUUID();
|
|
4642
|
-
const bytes = randomBytes(16);
|
|
4643
|
-
bytes[6] = bytes[6] & 15 | 64;
|
|
4644
|
-
bytes[8] = bytes[8] & 63 | 128;
|
|
4645
|
-
const hex = bytesToHex3(bytes);
|
|
4646
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
4647
|
-
}
|
|
4648
|
-
var TRANSACT_AUTH_FIELDS = [
|
|
4649
|
-
"encrypted_notes",
|
|
4650
|
-
"max_fee",
|
|
4651
|
-
"mint",
|
|
4652
|
-
"proof_bytes",
|
|
4653
|
-
"public_inputs",
|
|
4654
|
-
"recipient",
|
|
4655
|
-
"recipient_delivery_notes",
|
|
4656
|
-
"risk_quote",
|
|
4657
|
-
"sender"
|
|
4658
|
-
];
|
|
4659
|
-
var TRANSACT_SWAP_AUTH_FIELDS = [
|
|
4660
|
-
"close_timed_out",
|
|
4661
|
-
"dexes",
|
|
4662
|
-
"encrypted_notes",
|
|
4663
|
-
"exclude_dexes",
|
|
4664
|
-
"max_fee",
|
|
4665
|
-
"min_output_amount",
|
|
4666
|
-
"output_mint",
|
|
4667
|
-
"proof_bytes",
|
|
4668
|
-
"public_inputs",
|
|
4669
|
-
"recipient",
|
|
4670
|
-
"recipient_ata",
|
|
4671
|
-
"refund_blinding",
|
|
4672
|
-
"refund_pubkey",
|
|
4673
|
-
"retry_request_id",
|
|
4674
|
-
"risk_quote",
|
|
4675
|
-
"route_retry_attempts",
|
|
4676
|
-
"sender",
|
|
4677
|
-
"slippage_bps",
|
|
4678
|
-
"swap_max_retries"
|
|
4679
|
-
];
|
|
4680
|
-
var UTF8 = new TextEncoder();
|
|
4681
|
-
function compareKeysBytewise(a, b) {
|
|
4682
|
-
if (a === b) return 0;
|
|
4683
|
-
const ab = UTF8.encode(a);
|
|
4684
|
-
const bb = UTF8.encode(b);
|
|
4685
|
-
const shared = Math.min(ab.length, bb.length);
|
|
4686
|
-
for (let i = 0; i < shared; i++) {
|
|
4687
|
-
if (ab[i] !== bb[i]) return ab[i] - bb[i];
|
|
4688
|
-
}
|
|
4689
|
-
return ab.length - bb.length;
|
|
4690
|
-
}
|
|
4691
|
-
function canonicalJson(value) {
|
|
4692
|
-
if (value === null || value === void 0) return "null";
|
|
4693
|
-
if (typeof value === "boolean") return value ? "true" : "false";
|
|
4694
|
-
if (typeof value === "number") {
|
|
4695
|
-
if (!Number.isFinite(value) || !Number.isInteger(value) || !Number.isSafeInteger(value)) {
|
|
4696
|
-
throw new Error(
|
|
4697
|
-
`canonicalJson: refusing to sign the number ${String(value)}. The relay renders numbers with serde_json, which spells non-integer, non-finite and very large values differently from JavaScript, so the two digests would differ and the request would be rejected with an unexplained 401. Send it as a decimal string instead, which is how every amount in this schema travels.`
|
|
4698
|
-
);
|
|
4699
|
-
}
|
|
4700
|
-
return String(value);
|
|
4701
|
-
}
|
|
4702
|
-
if (typeof value === "bigint") return value.toString();
|
|
4703
|
-
if (typeof value === "string") return JSON.stringify(value);
|
|
4704
|
-
if (Array.isArray(value)) return "[" + value.map(canonicalJson).join(",") + "]";
|
|
4705
|
-
if (typeof value === "object") {
|
|
4706
|
-
const obj = value;
|
|
4707
|
-
const keys = Object.keys(obj).sort(compareKeysBytewise);
|
|
4708
|
-
return "{" + keys.map((k) => {
|
|
4709
|
-
const v = obj[k];
|
|
4710
|
-
if (typeof v === "function" || typeof v === "symbol") {
|
|
4711
|
-
throw new Error(
|
|
4712
|
-
`canonicalJson: the key "${k}" holds a ${typeof v}, which has no JSON representation. JSON.stringify would drop it from the wire body while it stayed in the signed view, so the relay's digest could never match this one.`
|
|
4713
|
-
);
|
|
4714
|
-
}
|
|
4715
|
-
return `${JSON.stringify(k)}:${canonicalJson(v)}`;
|
|
4716
|
-
}).join(",") + "}";
|
|
4717
|
-
}
|
|
4718
|
-
throw new Error(`canonicalJson: unsupported value of type ${typeof value}`);
|
|
4719
|
-
}
|
|
4720
|
-
var FIELDS_WITHOUT_NULL_ENCODING = {
|
|
4721
|
-
slippage_bps: "500 (`default_slippage_bps` in api/transact_swap.rs)"
|
|
4722
|
-
};
|
|
4723
|
-
function buildAuthRequest(body, sender, fields) {
|
|
4724
|
-
const out = {};
|
|
4725
|
-
for (const k of fields) {
|
|
4726
|
-
if (k === "sender") {
|
|
4727
|
-
out[k] = sender;
|
|
4728
|
-
continue;
|
|
4729
|
-
}
|
|
4730
|
-
const value = body[k];
|
|
4731
|
-
if (value === void 0 || value === null) {
|
|
4732
|
-
const relayDefault = FIELDS_WITHOUT_NULL_ENCODING[k];
|
|
4733
|
-
if (relayDefault) {
|
|
4734
|
-
throw new Error(
|
|
4735
|
-
`Relay request auth: \`${k}\` must be present in the body. It is the one field in this schema that is not optional on the relay side: when it is missing the relay signs its default, ${relayDefault}, while this request would sign null. The two digests differ and the relay answers 401 "Relay request signature does not match the exact request", which points at nothing. Set \`${k}\` explicitly, to the same value the body will carry.`
|
|
4736
|
-
);
|
|
4737
|
-
}
|
|
4738
|
-
out[k] = null;
|
|
4739
|
-
continue;
|
|
4740
|
-
}
|
|
4741
|
-
out[k] = value;
|
|
4742
|
-
}
|
|
4743
|
-
return out;
|
|
4744
|
-
}
|
|
4745
|
-
function buildRequestAuthMessage(endpoint, programId, issuedAt, nonce, request) {
|
|
4746
|
-
const digest = sha256Hex(canonicalJson(request));
|
|
4747
|
-
return new TextEncoder().encode(
|
|
4748
|
-
`${REQUEST_AUTH_DOMAIN}
|
|
4749
|
-
${endpoint}
|
|
4750
|
-
${programId.toBase58()}
|
|
4751
|
-
${nonce}
|
|
4752
|
-
${issuedAt}
|
|
4753
|
-
${digest}`
|
|
4754
|
-
);
|
|
4755
|
-
}
|
|
4756
|
-
function buildRelayAuthPreimage(endpoint, programId, body, sender, nowSeconds, fields = TRANSACT_AUTH_FIELDS) {
|
|
4757
|
-
const senderB58 = sender.toBase58();
|
|
4758
|
-
const issuedAt = String(nowSeconds ?? Math.floor(Date.now() / 1e3));
|
|
4759
|
-
const nonce = randomNonceUuid();
|
|
4760
|
-
const request = buildAuthRequest({ ...body, sender: senderB58 }, senderB58, fields);
|
|
4761
|
-
const message = buildRequestAuthMessage(endpoint, programId, issuedAt, nonce, request);
|
|
4762
|
-
return { sender: senderB58, auth_issued_at: issuedAt, auth_nonce: nonce, message };
|
|
4763
|
-
}
|
|
4764
|
-
function signRelayRequest(endpoint, programId, body, signer, nowSeconds, fields = TRANSACT_AUTH_FIELDS) {
|
|
4765
|
-
const preimage = buildRelayAuthPreimage(
|
|
4766
|
-
endpoint,
|
|
4767
|
-
programId,
|
|
4768
|
-
body,
|
|
4769
|
-
signer.publicKey,
|
|
4770
|
-
nowSeconds,
|
|
4771
|
-
fields
|
|
4772
|
-
);
|
|
4773
|
-
const signature = nacl5.sign.detached(preimage.message, signer.secretKey);
|
|
4774
|
-
return {
|
|
4775
|
-
sender: preimage.sender,
|
|
4776
|
-
auth_issued_at: preimage.auth_issued_at,
|
|
4777
|
-
auth_nonce: preimage.auth_nonce,
|
|
4778
|
-
auth_signature: Buffer.from(signature).toString("base64")
|
|
4779
|
-
};
|
|
4780
|
-
}
|
|
4781
|
-
function assertApprovalWithinFreshnessWindow(elapsedMs, maxAgeSeconds = REQUEST_AUTH_MAX_AGE_SECONDS) {
|
|
4782
|
-
const elapsedSeconds = elapsedMs / 1e3;
|
|
4783
|
-
const budget = maxAgeSeconds - RELAY_AUTH_APPROVAL_MARGIN_SECONDS;
|
|
4784
|
-
if (elapsedSeconds <= budget) return;
|
|
4785
|
-
throw new Error(
|
|
4786
|
-
`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.`
|
|
4787
|
-
);
|
|
4788
|
-
}
|
|
4789
|
-
function explainRelayAuthRejection(responseText) {
|
|
4790
|
-
const text = String(responseText);
|
|
4791
|
-
const has = (needle) => text.includes(needle);
|
|
4792
|
-
if (has("issued too far in the future")) {
|
|
4793
|
-
return `This computer's clock is more than ${REQUEST_AUTH_MAX_FUTURE_SKEW_SECONDS} seconds ahead of the network's, so the request looks like it was signed in the future and is refused before anything else happens. Turn on automatic date and time (or resynchronise the clock) and try again. Nothing was submitted.`;
|
|
4794
|
-
}
|
|
4795
|
-
if (has("signature expired")) {
|
|
4796
|
-
return `The request was signed more than ${REQUEST_AUTH_MAX_AGE_SECONDS} seconds before it arrived, usually because a wallet approval was left waiting, or because this computer's clock is running behind. The signed timestamp cannot be refreshed on its own. Start the operation again. Nothing was submitted.`;
|
|
4797
|
-
}
|
|
4798
|
-
if (has("does not match the exact request")) {
|
|
4799
|
-
return `The request body changed after it was signed, so the relay's digest and this one disagree. Sign the exact body that is POSTed, and re-POST a retry byte for byte rather than rebuilding it. If this is a hand-built swap call, check that every field in TRANSACT_SWAP_AUTH_FIELDS is present, including \`slippage_bps\`.`;
|
|
4800
|
-
}
|
|
4801
|
-
if (has("has no registered viewing key")) {
|
|
4802
|
-
return `The signature was accepted, so this is not a signing problem: the wallet that signed has no viewing key registered with Cloak yet. Register one first, or let the SDK do it by leaving \`enforceViewingKeyRegistration\` on and supplying the viewing key.`;
|
|
4803
|
-
}
|
|
4804
|
-
if (has("Authenticated sender is required") || has("auth_issued_at is required") || has("auth_nonce is required") || has("auth_signature is required")) {
|
|
4805
|
-
return `The request reached the relay without its authentication fields. Pass \`depositorKeypair\`, or \`signMessage\` together with \`walletPublicKey\`, so the request can be signed.`;
|
|
4806
|
-
}
|
|
4807
|
-
if (has("auth_nonce must be a canonical UUID")) {
|
|
4808
|
-
return `\`auth_nonce\` must be a canonical lowercase UUID. Reuse the one from the preimage.`;
|
|
4809
|
-
}
|
|
4810
|
-
if (has("Relay request signature must be exactly 64 bytes") || has("Invalid relay request signature encoding")) {
|
|
4811
|
-
return `\`auth_signature\` must be the raw 64-byte ed25519 detached signature, base64 encoded. Wallet adapters that wrap or re-encode what \`signMessage\` returns cannot be used here.`;
|
|
4812
|
-
}
|
|
4813
|
-
return null;
|
|
4814
|
-
}
|
|
4815
|
-
async function buildRelayAuthFields(endpoint, programId, body, signers, fields = TRANSACT_AUTH_FIELDS) {
|
|
4816
|
-
if (signers.depositorKeypair) {
|
|
4817
|
-
return signRelayRequest(endpoint, programId, body, signers.depositorKeypair, void 0, fields);
|
|
4818
|
-
}
|
|
4819
|
-
const wallet = signers.relayAuthSigner;
|
|
4820
|
-
if (!wallet) return null;
|
|
4821
|
-
const preimage = buildRelayAuthPreimage(
|
|
4822
|
-
endpoint,
|
|
4823
|
-
programId,
|
|
4824
|
-
body,
|
|
4825
|
-
wallet.walletPublicKey,
|
|
4826
|
-
void 0,
|
|
4827
|
-
fields
|
|
4828
|
-
);
|
|
4829
|
-
const approvalStartedMs = Date.now();
|
|
4830
|
-
const signature = await wallet.signMessage(preimage.message);
|
|
4831
|
-
assertApprovalWithinFreshnessWindow(Date.now() - approvalStartedMs);
|
|
4832
|
-
if (!(signature instanceof Uint8Array) || signature.length !== 64) {
|
|
4833
|
-
throw new Error(
|
|
4834
|
-
`The wallet's signMessage did not return a 64-byte ed25519 detached signature (got ${signature instanceof Uint8Array ? `${signature.length} bytes` : typeof signature}). Cloak signs with a raw detached signature over the bytes it hands the wallet; adapters that wrap or re-encode the result cannot be used here. Pass depositorKeypair instead, or use an adapter whose signMessage returns the raw signature.`
|
|
4835
|
-
);
|
|
4836
|
-
}
|
|
4837
|
-
return {
|
|
4838
|
-
sender: preimage.sender,
|
|
4839
|
-
auth_issued_at: preimage.auth_issued_at,
|
|
4840
|
-
auth_nonce: preimage.auth_nonce,
|
|
4841
|
-
auth_signature: Buffer.from(signature).toString("base64")
|
|
4842
|
-
};
|
|
4843
|
-
}
|
|
4844
|
-
var REQUEST_AUTH_BATCH_DOMAIN = "CLOAK_RELAY_BATCH_AUTH_V1";
|
|
4845
|
-
var REQUEST_AUTH_BATCH_MAX_AGE_SECONDS = 600;
|
|
4846
|
-
var RELAY_BATCH_AUTH_MAX_ITEMS = 64;
|
|
4847
|
-
function relayRequestDigestHex(preimage) {
|
|
4848
|
-
return bytesToHex3(sha2563(preimage.message));
|
|
4849
|
-
}
|
|
4850
|
-
function buildRelayBatchAuthMessage(programId, issuedAt, digests) {
|
|
4851
|
-
const lines = [REQUEST_AUTH_BATCH_DOMAIN, programId.toBase58(), issuedAt, String(digests.length)];
|
|
4852
|
-
return new TextEncoder().encode([...lines, ...digests].join("\n"));
|
|
4853
|
-
}
|
|
4854
|
-
|
|
4855
4903
|
// src/proving/artifacts.ts
|
|
4856
4904
|
import { sha256 as sha2564 } from "@noble/hashes/sha2";
|
|
4857
4905
|
|
|
@@ -5252,7 +5300,7 @@ async function fetchSupplementalAltFromRelay(relayUrl, params) {
|
|
|
5252
5300
|
throw new Error("Supplemental ALT response is missing a 'table' address");
|
|
5253
5301
|
}
|
|
5254
5302
|
try {
|
|
5255
|
-
return new
|
|
5303
|
+
return new PublicKey10(table);
|
|
5256
5304
|
} catch {
|
|
5257
5305
|
throw new Error(`Supplemental ALT response 'table' is not a valid public key: ${table}`);
|
|
5258
5306
|
}
|
|
@@ -5320,7 +5368,7 @@ async function resolveAddressLookupTableAccounts(connection, relayUrl, altAddres
|
|
|
5320
5368
|
const fetched = await Promise.all(
|
|
5321
5369
|
altAddresses.map(async (addr) => {
|
|
5322
5370
|
try {
|
|
5323
|
-
const pubkey = new
|
|
5371
|
+
const pubkey = new PublicKey10(addr);
|
|
5324
5372
|
const result = await connection.getAddressLookupTable(pubkey);
|
|
5325
5373
|
return result.value ?? null;
|
|
5326
5374
|
} catch {
|
|
@@ -5410,7 +5458,7 @@ function calculateConfiguredProtocolFee(amount, config) {
|
|
|
5410
5458
|
return total;
|
|
5411
5459
|
}
|
|
5412
5460
|
async function readLiveProtocolFee(connection, programId, mint, amount, requireSwapOpen = false) {
|
|
5413
|
-
const [poolConfigPda] =
|
|
5461
|
+
const [poolConfigPda] = PublicKey10.findProgramAddressSync(
|
|
5414
5462
|
[Buffer.from("pool_config"), mint.toBuffer()],
|
|
5415
5463
|
programId
|
|
5416
5464
|
);
|
|
@@ -5572,17 +5620,27 @@ async function registerViewingKeyOnce(relayUrl, userPubkey, viewingKeyHex, cache
|
|
|
5572
5620
|
viewingKeyHex
|
|
5573
5621
|
);
|
|
5574
5622
|
let signatureBase64;
|
|
5623
|
+
let authMode = "message";
|
|
5624
|
+
const messageBytes = new TextEncoder().encode(challenge.message);
|
|
5575
5625
|
if (options.signMessage) {
|
|
5576
|
-
const messageBytes = new TextEncoder().encode(challenge.message);
|
|
5577
5626
|
const sig = await options.signMessage(messageBytes);
|
|
5578
5627
|
signatureBase64 = Buffer.from(sig).toString("base64");
|
|
5579
5628
|
} else if (options.depositorKeypair) {
|
|
5580
|
-
const messageBytes = new TextEncoder().encode(challenge.message);
|
|
5581
5629
|
const sig = nacl6.sign.detached(messageBytes, options.depositorKeypair.secretKey);
|
|
5582
5630
|
signatureBase64 = Buffer.from(sig).toString("base64");
|
|
5631
|
+
} else if (options.signAuthTransaction) {
|
|
5632
|
+
onProgress?.(
|
|
5633
|
+
"Approve the viewing-key registration in your wallet. It shows as a 0 SOL transfer to yourself and is never sent to the network."
|
|
5634
|
+
);
|
|
5635
|
+
const signed = await signRelayAuthPayload(
|
|
5636
|
+
{ walletPublicKey: userPubkey, signAuthTransaction: options.signAuthTransaction },
|
|
5637
|
+
messageBytes
|
|
5638
|
+
);
|
|
5639
|
+
signatureBase64 = Buffer.from(signed.signature).toString("base64");
|
|
5640
|
+
authMode = signed.mode;
|
|
5583
5641
|
} else {
|
|
5584
5642
|
throw new Error(
|
|
5585
|
-
"Viewing key registration is mandatory: signMessage (wallet) or depositorKeypair is required."
|
|
5643
|
+
"Viewing key registration is mandatory: signMessage or signAuthTransaction (wallet) or depositorKeypair is required."
|
|
5586
5644
|
);
|
|
5587
5645
|
}
|
|
5588
5646
|
onProgress?.("Registering viewing key...");
|
|
@@ -5593,7 +5651,8 @@ async function registerViewingKeyOnce(relayUrl, userPubkey, viewingKeyHex, cache
|
|
|
5593
5651
|
user_pubkey: userPubkey.toBase58(),
|
|
5594
5652
|
viewing_key: viewingKeyHex,
|
|
5595
5653
|
nonce: challenge.nonce,
|
|
5596
|
-
signature: signatureBase64
|
|
5654
|
+
signature: signatureBase64,
|
|
5655
|
+
...authMode === "transaction" ? { auth_mode: authMode } : {}
|
|
5597
5656
|
})
|
|
5598
5657
|
});
|
|
5599
5658
|
if (!response.ok) {
|
|
@@ -5687,7 +5746,7 @@ function buildPublicInputsBytesFromSignals(publicSignals) {
|
|
|
5687
5746
|
var TRANSACT_DISCRIMINATOR = 0;
|
|
5688
5747
|
var RANGE_QUOTE_TAG_DEPOSIT = 1;
|
|
5689
5748
|
function deriveRiskNoncePDA(programId, nonce) {
|
|
5690
|
-
return
|
|
5749
|
+
return PublicKey10.findProgramAddressSync(
|
|
5691
5750
|
[Buffer.from("risk_nonce"), Buffer.from(nonce)],
|
|
5692
5751
|
programId
|
|
5693
5752
|
);
|
|
@@ -5706,7 +5765,7 @@ function extractDepositNonceFromEd25519Ix(ix) {
|
|
|
5706
5765
|
}
|
|
5707
5766
|
}
|
|
5708
5767
|
function deriveNullifierPDA(programId, poolPDA, nullifier) {
|
|
5709
|
-
return
|
|
5768
|
+
return PublicKey10.findProgramAddressSync(
|
|
5710
5769
|
[Buffer.from("nullifier"), poolPDA.toBuffer(), Buffer.from(nullifier)],
|
|
5711
5770
|
programId
|
|
5712
5771
|
);
|
|
@@ -5739,7 +5798,7 @@ function buildTransactInstruction(programId, payer, poolPDA, treasuryPDA, merkle
|
|
|
5739
5798
|
}
|
|
5740
5799
|
if (!isDepositData) {
|
|
5741
5800
|
data[offset++] = 3;
|
|
5742
|
-
data.set((relayer ??
|
|
5801
|
+
data.set((relayer ?? PublicKey10.default).toBytes(), offset);
|
|
5743
5802
|
offset += 32;
|
|
5744
5803
|
writeBigUInt64LE(data, relayerFee ?? BigInt(0), offset);
|
|
5745
5804
|
offset += 8;
|
|
@@ -5769,7 +5828,7 @@ function buildTransactInstruction(programId, payer, poolPDA, treasuryPDA, merkle
|
|
|
5769
5828
|
// 4. nullifier PDA 0 (writable)
|
|
5770
5829
|
{ pubkey: nullifierPDA1, isSigner: false, isWritable: true },
|
|
5771
5830
|
// 5. nullifier PDA 1 (writable)
|
|
5772
|
-
{ pubkey:
|
|
5831
|
+
{ pubkey: SystemProgram3.programId, isSigner: false, isWritable: false }
|
|
5773
5832
|
// 6. system_program
|
|
5774
5833
|
];
|
|
5775
5834
|
if (splAccounts) {
|
|
@@ -5814,7 +5873,7 @@ function buildTransactInstruction(programId, payer, poolPDA, treasuryPDA, merkle
|
|
|
5814
5873
|
var PACKET_LIMIT_BYTES = 1232;
|
|
5815
5874
|
function getCommonALTAddresses() {
|
|
5816
5875
|
return [
|
|
5817
|
-
|
|
5876
|
+
SystemProgram3.programId,
|
|
5818
5877
|
SYSVAR_SLOT_HASHES_PUBKEY,
|
|
5819
5878
|
SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
5820
5879
|
ComputeBudgetProgram.programId
|
|
@@ -5892,7 +5951,7 @@ async function createEphemeralALT(connection, depositor, onProgress, additionalA
|
|
|
5892
5951
|
allowSupplementalAlt
|
|
5893
5952
|
);
|
|
5894
5953
|
} else {
|
|
5895
|
-
const tx = new
|
|
5954
|
+
const tx = new Transaction3().add(createIx).add(extendIx);
|
|
5896
5955
|
const { blockhash } = await connection.getLatestBlockhash();
|
|
5897
5956
|
tx.recentBlockhash = blockhash;
|
|
5898
5957
|
tx.feePayer = depositor.publicKey;
|
|
@@ -6553,7 +6612,7 @@ async function submitTransactionDirect(connection, programId, depositor, proofBy
|
|
|
6553
6612
|
let legacyTransportAttempt = 0;
|
|
6554
6613
|
while (true) {
|
|
6555
6614
|
try {
|
|
6556
|
-
const tx = new
|
|
6615
|
+
const tx = new Transaction3();
|
|
6557
6616
|
for (const ix of instructions) {
|
|
6558
6617
|
tx.add(ix);
|
|
6559
6618
|
}
|
|
@@ -6777,7 +6836,7 @@ async function fetchRiskQuote(riskQuoteUrl, wallet, options) {
|
|
|
6777
6836
|
const messageArr = hexToBytes2(messageHex);
|
|
6778
6837
|
let signerPubkey;
|
|
6779
6838
|
try {
|
|
6780
|
-
signerPubkey = new
|
|
6839
|
+
signerPubkey = new PublicKey10(signerB58);
|
|
6781
6840
|
} catch {
|
|
6782
6841
|
throw new Error("Invalid risk quote response: signer_pubkey is not a public key");
|
|
6783
6842
|
}
|
|
@@ -6878,18 +6937,22 @@ function planRelayAuth(options) {
|
|
|
6878
6937
|
const walletPublicKey = resolveUserWallet(options);
|
|
6879
6938
|
if (options.depositorKeypair) return { kind: "keypair" };
|
|
6880
6939
|
const signMessage = options.signMessage;
|
|
6940
|
+
const signAuthTransaction = options.signAuthTransaction;
|
|
6881
6941
|
if (walletPublicKey && signMessage) return { kind: "wallet", signer: { walletPublicKey, signMessage } };
|
|
6882
|
-
if (
|
|
6942
|
+
if (walletPublicKey && signAuthTransaction) {
|
|
6943
|
+
return { kind: "wallet", signer: { walletPublicKey, signAuthTransaction } };
|
|
6944
|
+
}
|
|
6945
|
+
if (!walletPublicKey && !signMessage && !signAuthTransaction) return { kind: "none" };
|
|
6883
6946
|
return {
|
|
6884
6947
|
kind: "incomplete",
|
|
6885
|
-
detail: signMessage ? "
|
|
6948
|
+
detail: signMessage || signAuthTransaction ? "a signer was provided but no wallet public key, so the request has no sender to authenticate as. Add `walletPublicKey` (or `depositorPublicKey`)." : "a wallet public key was provided but no `signMessage` and no `signAuthTransaction`, so nothing can sign. Add `signMessage` from the wallet adapter, or `signAuthTransaction` (its `signTransaction`) for a wallet that cannot sign messages."
|
|
6886
6949
|
};
|
|
6887
6950
|
}
|
|
6888
6951
|
function assertRelayAuthAvailable(plan, flow) {
|
|
6889
6952
|
if (plan.kind === "keypair" || plan.kind === "wallet") return;
|
|
6890
6953
|
const cause = plan.kind === "none" ? "no signer was provided." : `the signer provided cannot produce one: ${plan.detail}`;
|
|
6891
6954
|
throw new Error(
|
|
6892
|
-
`Cloak ${flow} requires an authenticated sender, and ${cause} Pass either \`depositorKeypair\` (a local Keypair) or \`signMessage\`
|
|
6955
|
+
`Cloak ${flow} requires an authenticated sender, and ${cause} Pass either \`depositorKeypair\` (a local Keypair), or \`signMessage\` (a browser wallet adapter) or \`signAuthTransaction\` (a hardware wallet that cannot sign messages) together with \`walletPublicKey\`. The sender must be the end user's own wallet. Checked up front, so nothing has been computed or submitted yet.`
|
|
6893
6956
|
);
|
|
6894
6957
|
}
|
|
6895
6958
|
async function submitTransactToRelay(args) {
|
|
@@ -8035,7 +8098,7 @@ async function transact(params, options) {
|
|
|
8035
8098
|
"All relay-tree attempts failed. Rebuilding merkle tree from chain as last resort..."
|
|
8036
8099
|
);
|
|
8037
8100
|
try {
|
|
8038
|
-
const [merkleTreePda] =
|
|
8101
|
+
const [merkleTreePda] = PublicKey10.findProgramAddressSync(
|
|
8039
8102
|
[Buffer.from("merkle_tree"), mint.toBuffer()],
|
|
8040
8103
|
programId
|
|
8041
8104
|
);
|
|
@@ -9023,7 +9086,7 @@ async function swapUtxo(params, options) {
|
|
|
9023
9086
|
}
|
|
9024
9087
|
if (options.onSwapStatePredicted && inputNullifiers.length > 0) {
|
|
9025
9088
|
const nBytes = bigintToBytes322(inputNullifiers[0]);
|
|
9026
|
-
const [ssp] =
|
|
9089
|
+
const [ssp] = PublicKey10.findProgramAddressSync(
|
|
9027
9090
|
[Buffer.from("swap_state"), pdas.pool.toBuffer(), Buffer.from(nBytes)],
|
|
9028
9091
|
programId
|
|
9029
9092
|
);
|
|
@@ -9443,8 +9506,7 @@ function cleanupStalePendingOperations(maxAgeMs = 24 * 60 * 60 * 1e3) {
|
|
|
9443
9506
|
}
|
|
9444
9507
|
|
|
9445
9508
|
// src/relay/batch-auth.ts
|
|
9446
|
-
import { Keypair as
|
|
9447
|
-
import nacl7 from "tweetnacl";
|
|
9509
|
+
import { Keypair as Keypair4 } from "@solana/web3.js";
|
|
9448
9510
|
var RelayBatchAuthCoordinator = class {
|
|
9449
9511
|
constructor(options) {
|
|
9450
9512
|
this.proofSlotsInUse = 0;
|
|
@@ -9467,7 +9529,7 @@ var RelayBatchAuthCoordinator = class {
|
|
|
9467
9529
|
throw new Error(`submitConcurrency must be a positive integer (got ${concurrency}).`);
|
|
9468
9530
|
}
|
|
9469
9531
|
this.signer = options.signer;
|
|
9470
|
-
this.sender = options.signer instanceof
|
|
9532
|
+
this.sender = options.signer instanceof Keypair4 ? options.signer.publicKey : options.signer.walletPublicKey;
|
|
9471
9533
|
this.expectedItems = options.items;
|
|
9472
9534
|
this.submitConcurrency = concurrency;
|
|
9473
9535
|
const proofConcurrency = options.proofConcurrency ?? 2;
|
|
@@ -9591,24 +9653,19 @@ var RelayBatchAuthCoordinator = class {
|
|
|
9591
9653
|
const issuedAt = preimages[0].auth_issued_at;
|
|
9592
9654
|
const message = buildRelayBatchAuthMessage(programId, issuedAt, digests);
|
|
9593
9655
|
this.waves += 1;
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
signature = nacl7.sign.detached(message, this.signer.secretKey);
|
|
9597
|
-
} else {
|
|
9656
|
+
if (!(this.signer instanceof Keypair4)) {
|
|
9657
|
+
const asTransaction = !this.signer.signMessage;
|
|
9598
9658
|
this.onProgress?.(
|
|
9599
|
-
`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.`
|
|
9659
|
+
`Approve ${waiting.length} request(s) in your wallet` + (this.waves > 1 ? ` (approval ${this.waves}, for the rows that had to re-prove)` : "") + (asTransaction ? `. Your wallet shows this as a 0 SOL transfer to yourself: it is how a wallet that cannot sign messages authorizes the requests for the relay, and it is never sent to the network. Approve it on the device within a few minutes.` : `. This signs the requests for the relay, not a transaction, and must be approved within a few minutes to stay valid.`)
|
|
9600
9660
|
);
|
|
9601
|
-
|
|
9602
|
-
|
|
9661
|
+
}
|
|
9662
|
+
const approvalStartedMs = Date.now();
|
|
9663
|
+
const { signature, mode } = await signRelayAuthPayload(this.signer, message);
|
|
9664
|
+
if (!(this.signer instanceof Keypair4)) {
|
|
9603
9665
|
assertApprovalWithinFreshnessWindow(
|
|
9604
9666
|
Date.now() - approvalStartedMs,
|
|
9605
9667
|
REQUEST_AUTH_BATCH_MAX_AGE_SECONDS
|
|
9606
9668
|
);
|
|
9607
|
-
if (!(signature instanceof Uint8Array) || signature.length !== 64) {
|
|
9608
|
-
throw new Error(
|
|
9609
|
-
`The wallet's signMessage did not return a 64-byte ed25519 detached signature (got ${signature instanceof Uint8Array ? `${signature.length} bytes` : typeof signature}).`
|
|
9610
|
-
);
|
|
9611
|
-
}
|
|
9612
9669
|
}
|
|
9613
9670
|
const signatureB64 = Buffer.from(signature).toString("base64");
|
|
9614
9671
|
wave.forEach((item, i) => {
|
|
@@ -9620,6 +9677,7 @@ var RelayBatchAuthCoordinator = class {
|
|
|
9620
9677
|
auth_issued_at: preimage.auth_issued_at,
|
|
9621
9678
|
auth_nonce: preimage.auth_nonce,
|
|
9622
9679
|
auth_signature: signatureB64,
|
|
9680
|
+
...mode === "transaction" ? { auth_mode: mode } : {},
|
|
9623
9681
|
// A fresh array per item: `Object.assign` puts this on the wire body, and the body is
|
|
9624
9682
|
// serialized again on every network retry.
|
|
9625
9683
|
auth_batch: { digests: [...digests] }
|
|
@@ -9668,10 +9726,18 @@ async function transactBatch(items, options) {
|
|
|
9668
9726
|
throw new Error("transactBatch requires relayUrl: batch approval is a relay authentication scheme.");
|
|
9669
9727
|
}
|
|
9670
9728
|
const { proofConcurrency, submitConcurrency, ...shared } = options;
|
|
9671
|
-
const signer = shared.depositorKeypair ? shared.depositorKeypair :
|
|
9729
|
+
const signer = shared.depositorKeypair ? shared.depositorKeypair : (() => {
|
|
9730
|
+
const wallet = shared.walletPublicKey ?? shared.depositorPublicKey;
|
|
9731
|
+
if (!wallet) return null;
|
|
9732
|
+
if (shared.signMessage) return { walletPublicKey: wallet, signMessage: shared.signMessage };
|
|
9733
|
+
if (shared.signAuthTransaction) {
|
|
9734
|
+
return { walletPublicKey: wallet, signAuthTransaction: shared.signAuthTransaction };
|
|
9735
|
+
}
|
|
9736
|
+
return null;
|
|
9737
|
+
})();
|
|
9672
9738
|
if (!signer) {
|
|
9673
9739
|
throw new Error(
|
|
9674
|
-
"transactBatch requires an authenticated sender: pass `depositorKeypair` (a local Keypair) or `
|
|
9740
|
+
"transactBatch requires an authenticated sender: pass `depositorKeypair` (a local Keypair), or `walletPublicKey` with `signMessage` (a browser wallet adapter) or `signAuthTransaction` (a hardware wallet). Checked up front, so nothing has been computed or submitted yet."
|
|
9675
9741
|
);
|
|
9676
9742
|
}
|
|
9677
9743
|
const coordinator = createRelayBatchAuthCoordinator({
|
|
@@ -9708,7 +9774,7 @@ async function transactBatch(items, options) {
|
|
|
9708
9774
|
// src/scanning/scan.ts
|
|
9709
9775
|
import _bs583 from "bs58";
|
|
9710
9776
|
import {
|
|
9711
|
-
PublicKey as
|
|
9777
|
+
PublicKey as PublicKey12
|
|
9712
9778
|
} from "@solana/web3.js";
|
|
9713
9779
|
import { getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2 } from "@solana/spl-token";
|
|
9714
9780
|
var bs583 = _bs583.default || _bs583;
|
|
@@ -9796,7 +9862,7 @@ function parseSwapOutputMint(data) {
|
|
|
9796
9862
|
const mintBytes = data.slice(SWAP_OUTPUT_MINT_OFFSET, end);
|
|
9797
9863
|
if (mintBytes.every((byte) => byte === 0)) return void 0;
|
|
9798
9864
|
try {
|
|
9799
|
-
return new
|
|
9865
|
+
return new PublicKey12(mintBytes).toBase58();
|
|
9800
9866
|
} catch {
|
|
9801
9867
|
return void 0;
|
|
9802
9868
|
}
|
|
@@ -9886,7 +9952,7 @@ function parseSwapRecipientAta(data) {
|
|
|
9886
9952
|
const recipientAtaBytes = data.slice(recipientAtaStart, recipientAtaEnd);
|
|
9887
9953
|
if (recipientAtaBytes.every((byte) => byte === 0)) return void 0;
|
|
9888
9954
|
try {
|
|
9889
|
-
return new
|
|
9955
|
+
return new PublicKey12(recipientAtaBytes).toBase58();
|
|
9890
9956
|
} catch {
|
|
9891
9957
|
return void 0;
|
|
9892
9958
|
}
|
|
@@ -10048,7 +10114,7 @@ async function scanSwapNoteCarriers(connection, programId, viewingKeyNk, swapCtx
|
|
|
10048
10114
|
let rpcCalls = 0;
|
|
10049
10115
|
const candidates = Array.from(swapCtxByCommitment.keys());
|
|
10050
10116
|
if (candidates.length === 0) return { records, rpcCalls };
|
|
10051
|
-
const [registry] =
|
|
10117
|
+
const [registry] = PublicKey12.findProgramAddressSync(
|
|
10052
10118
|
[Buffer.from("cloak_chain_note_registry")],
|
|
10053
10119
|
programId
|
|
10054
10120
|
);
|
|
@@ -10451,8 +10517,8 @@ async function scanTransactions(opts) {
|
|
|
10451
10517
|
if (onChainAta) {
|
|
10452
10518
|
try {
|
|
10453
10519
|
const expectedAta = getAssociatedTokenAddressSync2(
|
|
10454
|
-
new
|
|
10455
|
-
new
|
|
10520
|
+
new PublicKey12(asset.mint),
|
|
10521
|
+
new PublicKey12(walletPublicKey)
|
|
10456
10522
|
).toBase58();
|
|
10457
10523
|
if (onChainAta === expectedAta) {
|
|
10458
10524
|
isOurs = true;
|
|
@@ -10666,7 +10732,7 @@ async function scanTransactions(opts) {
|
|
|
10666
10732
|
// deposit — that is the public deposit amount. A deposit that also merged inputs
|
|
10667
10733
|
// carries a larger output 0 and is not recoverable from public data alone.
|
|
10668
10734
|
amount: grossAmount,
|
|
10669
|
-
mintAddress: new
|
|
10735
|
+
mintAddress: new PublicKey12(asset.mint),
|
|
10670
10736
|
outputCommitments: ixCtx.outputCommitments ?? []
|
|
10671
10737
|
});
|
|
10672
10738
|
if (recoveredDeposit) {
|
|
@@ -10692,7 +10758,7 @@ async function scanTransactions(opts) {
|
|
|
10692
10758
|
noteSalt: compactNote.noteSalt,
|
|
10693
10759
|
amount: compactNote.outAmount0,
|
|
10694
10760
|
keypair: { privateKey: 0n, publicKey: compactNote.outPubkey0 },
|
|
10695
|
-
mintAddress: new
|
|
10761
|
+
mintAddress: new PublicKey12(asset.mint),
|
|
10696
10762
|
outputIndex: 0,
|
|
10697
10763
|
// v4 describes output 0, which is where change lands
|
|
10698
10764
|
outputCommitments: ixCtx.outputCommitments ?? []
|
|
@@ -10885,7 +10951,7 @@ function formatComplianceCsv(report) {
|
|
|
10885
10951
|
}
|
|
10886
10952
|
|
|
10887
10953
|
// src/wallet/utxo-wallet.ts
|
|
10888
|
-
import { PublicKey as
|
|
10954
|
+
import { PublicKey as PublicKey13 } from "@solana/web3.js";
|
|
10889
10955
|
var UtxoWallet = class _UtxoWallet {
|
|
10890
10956
|
constructor(viewingKey) {
|
|
10891
10957
|
this.wallets = /* @__PURE__ */ new Map();
|
|
@@ -11097,7 +11163,7 @@ var UtxoWallet = class _UtxoWallet {
|
|
|
11097
11163
|
data.viewingKey ? new Uint8Array(data.viewingKey) : void 0
|
|
11098
11164
|
);
|
|
11099
11165
|
for (const w of data.wallets) {
|
|
11100
|
-
const mint = new
|
|
11166
|
+
const mint = new PublicKey13(w.mint);
|
|
11101
11167
|
for (const u of w.utxos) {
|
|
11102
11168
|
wallet.addUtxo({
|
|
11103
11169
|
amount: BigInt(u.amount),
|
|
@@ -11180,7 +11246,7 @@ var SimpleWallet = class {
|
|
|
11180
11246
|
};
|
|
11181
11247
|
|
|
11182
11248
|
// src/bridge/rail-verify.ts
|
|
11183
|
-
import
|
|
11249
|
+
import nacl7 from "tweetnacl";
|
|
11184
11250
|
import { sha256 as sha2565 } from "@noble/hashes/sha256";
|
|
11185
11251
|
var ONECLICK_PUBKEY_B58 = "reYaWhvwu8Jzo3WUM3zhn6VrhuMEF4eADL17qtRVifc";
|
|
11186
11252
|
var b58 = /* @__PURE__ */ (() => {
|
|
@@ -11291,7 +11357,7 @@ function verifyQuoteSignature(resp) {
|
|
|
11291
11357
|
const message = new TextEncoder().encode(b58.encode(new Uint8Array(digest)));
|
|
11292
11358
|
const sig = resp.signature.replace(/^ed25519:/, "");
|
|
11293
11359
|
try {
|
|
11294
|
-
return { valid:
|
|
11360
|
+
return { valid: nacl7.sign.detached.verify(message, b58.decode(sig), b58.decode(ONECLICK_PUBKEY_B58)) };
|
|
11295
11361
|
} catch (e) {
|
|
11296
11362
|
return { valid: false, reason: e instanceof Error ? e.message : String(e) };
|
|
11297
11363
|
}
|
|
@@ -11390,10 +11456,10 @@ function cloakBridgeRail(relayUrl) {
|
|
|
11390
11456
|
|
|
11391
11457
|
// src/bridge/paymaster-client.ts
|
|
11392
11458
|
import {
|
|
11393
|
-
PublicKey as
|
|
11459
|
+
PublicKey as PublicKey14,
|
|
11394
11460
|
SystemInstruction,
|
|
11395
|
-
SystemProgram as
|
|
11396
|
-
Transaction as
|
|
11461
|
+
SystemProgram as SystemProgram4,
|
|
11462
|
+
Transaction as Transaction4
|
|
11397
11463
|
} from "@solana/web3.js";
|
|
11398
11464
|
import { TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID3, decodeTransferInstruction, getAssociatedTokenAddressSync as getAssociatedTokenAddressSync3 } from "@solana/spl-token";
|
|
11399
11465
|
function validatePaymasterTopUpTransaction(tx, expect) {
|
|
@@ -11404,7 +11470,7 @@ function validatePaymasterTopUpTransaction(tx, expect) {
|
|
|
11404
11470
|
);
|
|
11405
11471
|
}
|
|
11406
11472
|
const ix0 = ixs[0];
|
|
11407
|
-
if (!ix0.programId.equals(
|
|
11473
|
+
if (!ix0.programId.equals(SystemProgram4.programId)) {
|
|
11408
11474
|
throw new Error(
|
|
11409
11475
|
`paymaster top-up instruction 0 is owned by ${ix0.programId.toBase58()}, not the System Program \u2014 refusing to sign an unrecognised first instruction`
|
|
11410
11476
|
);
|
|
@@ -11488,10 +11554,10 @@ async function fundReceiverViaPaymaster(relayUrl, receiver, grantLamports) {
|
|
|
11488
11554
|
"paymaster prepare returned a voucher that is already expired \u2014 refusing to sign a stale top-up rather than fail confusingly at cosign"
|
|
11489
11555
|
);
|
|
11490
11556
|
}
|
|
11491
|
-
const feeMint = new
|
|
11492
|
-
const paymentAddress = new
|
|
11557
|
+
const feeMint = new PublicKey14(prepared.fee_mint);
|
|
11558
|
+
const paymentAddress = new PublicKey14(prepared.payment_address);
|
|
11493
11559
|
const feeTokenAmount = BigInt(prepared.fee_token_amount);
|
|
11494
|
-
const tx =
|
|
11560
|
+
const tx = Transaction4.from(Buffer.from(prepared.transaction, "base64"));
|
|
11495
11561
|
validatePaymasterTopUpTransaction(tx, {
|
|
11496
11562
|
recipient: receiver.publicKey,
|
|
11497
11563
|
maxGrantLamports: grantLamports,
|
|
@@ -11510,7 +11576,7 @@ async function fundReceiverViaPaymaster(relayUrl, receiver, grantLamports) {
|
|
|
11510
11576
|
});
|
|
11511
11577
|
const cosigned = await parseBridgeResponse2(cosignRes, "paymaster cosign");
|
|
11512
11578
|
return {
|
|
11513
|
-
transaction:
|
|
11579
|
+
transaction: Transaction4.from(Buffer.from(cosigned.transaction, "base64")),
|
|
11514
11580
|
feeTokenAmount,
|
|
11515
11581
|
feeMint: prepared.fee_mint,
|
|
11516
11582
|
paymentAddress: prepared.payment_address
|
|
@@ -11518,7 +11584,7 @@ async function fundReceiverViaPaymaster(relayUrl, receiver, grantLamports) {
|
|
|
11518
11584
|
}
|
|
11519
11585
|
|
|
11520
11586
|
// src/bridge/derive.ts
|
|
11521
|
-
import { Keypair as
|
|
11587
|
+
import { Keypair as Keypair6 } from "@solana/web3.js";
|
|
11522
11588
|
import { hmac } from "@noble/hashes/hmac";
|
|
11523
11589
|
import { sha512 } from "@noble/hashes/sha512";
|
|
11524
11590
|
var BRIDGE_ESCROW_LABEL = "cloak_bridge_escrow";
|
|
@@ -11533,7 +11599,7 @@ function deriveBridgeReceiver(nk, index) {
|
|
|
11533
11599
|
);
|
|
11534
11600
|
}
|
|
11535
11601
|
const h = hmac(sha512, new Uint8Array(nk), new TextEncoder().encode(`${BRIDGE_ESCROW_LABEL}:${index}`));
|
|
11536
|
-
return
|
|
11602
|
+
return Keypair6.fromSeed(h.slice(0, 32));
|
|
11537
11603
|
}
|
|
11538
11604
|
|
|
11539
11605
|
// src/bridge/discover.ts
|
|
@@ -11615,21 +11681,21 @@ async function listBridgeDeposits(conn, nk, opts) {
|
|
|
11615
11681
|
|
|
11616
11682
|
// src/bridge/deposit-core.ts
|
|
11617
11683
|
import {
|
|
11618
|
-
SystemProgram as
|
|
11619
|
-
Transaction as
|
|
11684
|
+
SystemProgram as SystemProgram5,
|
|
11685
|
+
Transaction as Transaction5,
|
|
11620
11686
|
VersionedTransaction as VersionedTransaction2,
|
|
11621
11687
|
sendAndConfirmTransaction as sendAndConfirmTransaction2
|
|
11622
11688
|
} from "@solana/web3.js";
|
|
11623
|
-
import
|
|
11689
|
+
import nacl8 from "tweetnacl";
|
|
11624
11690
|
|
|
11625
11691
|
// src/bridge/funder.ts
|
|
11626
|
-
import { PublicKey as
|
|
11692
|
+
import { PublicKey as PublicKey16 } from "@solana/web3.js";
|
|
11627
11693
|
import { getAssociatedTokenAddressSync as getAssociatedTokenAddressSync5 } from "@solana/spl-token";
|
|
11628
11694
|
var MAINNET_RENT_0 = 890880;
|
|
11629
11695
|
var MAINNET_RENT_1 = 897840;
|
|
11630
11696
|
var FEE_BUDGET = 13e4;
|
|
11631
11697
|
var CLEANUP_FEE_BUDGET = 5e3;
|
|
11632
|
-
var pda = (seeds, programId) =>
|
|
11698
|
+
var pda = (seeds, programId) => PublicKey16.findProgramAddressSync(seeds.map((s) => Buffer.from(s)), programId)[0];
|
|
11633
11699
|
function deriveFundingTargets(d) {
|
|
11634
11700
|
const pool = pda([Buffer.from("pool"), d.mint.toBuffer()], d.programId);
|
|
11635
11701
|
return {
|
|
@@ -11690,8 +11756,8 @@ async function depositFromDerivedKey(conn, R, funder, amount, log = console.log,
|
|
|
11690
11756
|
if (heldByR >= grantR) {
|
|
11691
11757
|
log(` R already holds ${heldByR} (>= ${grantR}) \u2014 no top-up needed`);
|
|
11692
11758
|
} else {
|
|
11693
|
-
await sendAndConfirmTransaction2(conn, new
|
|
11694
|
-
|
|
11759
|
+
await sendAndConfirmTransaction2(conn, new Transaction5().add(
|
|
11760
|
+
SystemProgram5.transfer({ fromPubkey: funder.publicKey, toPubkey: R.publicKey, lamports: grantR - heldByR })
|
|
11695
11761
|
), [funder]);
|
|
11696
11762
|
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"}`);
|
|
11697
11763
|
}
|
|
@@ -11717,13 +11783,13 @@ async function depositFromDerivedKey(conn, R, funder, amount, log = console.log,
|
|
|
11717
11783
|
address: t.a.toBase58(),
|
|
11718
11784
|
lamportsSent: Math.max(0, t.need - (infos[i]?.lamports ?? 0))
|
|
11719
11785
|
}));
|
|
11720
|
-
const ixs = targets.flatMap((t, i) => (infos[i]?.lamports ?? 0) >= t.need ? [] : [
|
|
11786
|
+
const ixs = targets.flatMap((t, i) => (infos[i]?.lamports ?? 0) >= t.need ? [] : [SystemProgram5.transfer({
|
|
11721
11787
|
fromPubkey: funder.publicKey,
|
|
11722
11788
|
toPubkey: t.a,
|
|
11723
11789
|
lamports: t.need - (infos[i]?.lamports ?? 0)
|
|
11724
11790
|
})]);
|
|
11725
11791
|
if (ixs.length) {
|
|
11726
|
-
const sig = await sendAndConfirmTransaction2(conn, new
|
|
11792
|
+
const sig = await sendAndConfirmTransaction2(conn, new Transaction5().add(...ixs), [funder]);
|
|
11727
11793
|
log(` seam: funded ${ixs.length} program accounts read off the built tx \u2014 ${sig.slice(0, 16)}\u2026`);
|
|
11728
11794
|
}
|
|
11729
11795
|
tx.sign([R]);
|
|
@@ -11732,7 +11798,7 @@ async function depositFromDerivedKey(conn, R, funder, amount, log = console.log,
|
|
|
11732
11798
|
tx.partialSign(R);
|
|
11733
11799
|
return tx;
|
|
11734
11800
|
};
|
|
11735
|
-
const signMessage = async (m) =>
|
|
11801
|
+
const signMessage = async (m) => nacl8.sign.detached(m, R.secretKey);
|
|
11736
11802
|
const utxoKeypair = await deriveUtxoKeypairFromSpendKey(opts.noteSpendKey);
|
|
11737
11803
|
const nk = getNkFromUtxoPrivateKey(utxoKeypair.privateKey);
|
|
11738
11804
|
const { utxo, noteSalt } = await createRecoverableDepositUtxo(amount, nk, mint);
|
|
@@ -11780,8 +11846,8 @@ async function depositFromDerivedKey(conn, R, funder, amount, log = console.log,
|
|
|
11780
11846
|
|
|
11781
11847
|
// src/bridge/cleanup.ts
|
|
11782
11848
|
import {
|
|
11783
|
-
PublicKey as
|
|
11784
|
-
Transaction as
|
|
11849
|
+
PublicKey as PublicKey18,
|
|
11850
|
+
Transaction as Transaction6,
|
|
11785
11851
|
sendAndConfirmTransaction as sendAndConfirmTransaction3
|
|
11786
11852
|
} from "@solana/web3.js";
|
|
11787
11853
|
import {
|
|
@@ -11791,7 +11857,7 @@ import {
|
|
|
11791
11857
|
createAssociatedTokenAccountIdempotentInstruction,
|
|
11792
11858
|
getMinimumBalanceForRentExemptAccount
|
|
11793
11859
|
} from "@solana/spl-token";
|
|
11794
|
-
var DEFAULT_MINT = new
|
|
11860
|
+
var DEFAULT_MINT = new PublicKey18("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
11795
11861
|
async function cleanupReceivingAddress(conn, R, dustDestination, mint = DEFAULT_MINT) {
|
|
11796
11862
|
assertAllowedRpcConnection(conn);
|
|
11797
11863
|
const rAta = getAssociatedTokenAddressSync6(mint, R.publicKey);
|
|
@@ -11837,7 +11903,7 @@ async function cleanupReceivingAddress(conn, R, dustDestination, mint = DEFAULT_
|
|
|
11837
11903
|
destination = destAta.toBase58();
|
|
11838
11904
|
}
|
|
11839
11905
|
ixs.push(createCloseAccountInstruction(rAta, R.publicKey, R.publicKey));
|
|
11840
|
-
const signature = await sendAndConfirmTransaction3(conn, new
|
|
11906
|
+
const signature = await sendAndConfirmTransaction3(conn, new Transaction6().add(...ixs), [R], {
|
|
11841
11907
|
commitment: "confirmed"
|
|
11842
11908
|
});
|
|
11843
11909
|
const after = await conn.getBalance(R.publicKey);
|
|
@@ -12038,6 +12104,7 @@ export {
|
|
|
12038
12104
|
assessBridgeQuote,
|
|
12039
12105
|
bigintToBytes32,
|
|
12040
12106
|
bigintToHex,
|
|
12107
|
+
buildAuthTransactionMessage,
|
|
12041
12108
|
buildMerkleTree,
|
|
12042
12109
|
buildMerkleTreeFromChain,
|
|
12043
12110
|
buildMerkleTreeFromRelay,
|
|
@@ -12218,10 +12285,12 @@ export {
|
|
|
12218
12285
|
sdkLogger,
|
|
12219
12286
|
selectUtxos,
|
|
12220
12287
|
sendTransaction,
|
|
12288
|
+
serializeAuthTransactionMessage,
|
|
12221
12289
|
serializeNote,
|
|
12222
12290
|
serializeUtxo,
|
|
12223
12291
|
setCircuitsPath,
|
|
12224
12292
|
setDebugMode,
|
|
12293
|
+
signRelayAuthPayload,
|
|
12225
12294
|
signTransaction,
|
|
12226
12295
|
splitTo2Limbs,
|
|
12227
12296
|
submitTransactToRelay,
|