@rhea-finance/confidential-swap 0.2.2 → 0.2.4
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 +5 -1
- package/dist/adapters/index.cjs +1 -1
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/{api-QSK8yzxM.d.cts → api-DuPBsPnx.d.cts} +8 -4
- package/dist/{api-QSK8yzxM.d.ts → api-DuPBsPnx.d.ts} +8 -4
- package/dist/index.cjs +150 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -6
- package/dist/index.d.ts +12 -6
- package/dist/index.js +149 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,6 +47,8 @@ const client = createConfidentialSwapClient({
|
|
|
47
47
|
api,
|
|
48
48
|
signingAdapters: [linking],
|
|
49
49
|
fundingAdapters: [funding],
|
|
50
|
+
// Optional dynamic provider. Loaded once and frozen into each new plan.
|
|
51
|
+
getAppFees: signal => loadAppFeesForCurrentApplication(signal),
|
|
50
52
|
// Add fundingSwapAdapter for non-DIRECT routes.
|
|
51
53
|
});
|
|
52
54
|
|
|
@@ -71,7 +73,9 @@ The example assumes the application's existing wallet, asset, funding adapter an
|
|
|
71
73
|
|
|
72
74
|
`Token.assetId` identifies the registry asset. `blockchain` names its chain. `contractAddress` identifies its actual on-chain token. For numeric EVM funding identities supply the matching token `swapChain` (e.g. `"1"`); `swapAddress` explicitly maps an asset to the funding swap token address/native sentinel used by the injected `FundingSwapAdapter`. No arbitrary EVM chain is guessed from `evm` kind alone.
|
|
73
75
|
|
|
74
|
-
`preview` accepts an optional linking identity and returns `executable: false`. A placeholder identity is display-only. Preview/quote may allocate deposit addresses on the backend, but neither signs nor broadcasts funds. `quote` requires a real linking identity. Default withdrawal fees match the reference app: 2 bps to `dcl0001.near`, referral `rhea`. App fees are attached only to withdrawal quotes; funding deposit quotes do not include them.
|
|
76
|
+
`preview` accepts an optional linking identity and returns `executable: false`. A placeholder identity is display-only. Preview/quote may allocate deposit addresses on the backend, but neither signs nor broadcasts funds. `quote` requires a real linking identity. Default withdrawal fees match the reference app: 2 bps to `dcl0001.near`, referral `rhea`. App fees are attached only to withdrawal quotes; funding deposit quotes do not include them. Static `HttpConfidentialApi.appFees` must contain at least one valid NEAR recipient and an integer fee from 1 to 10000.
|
|
77
|
+
|
|
78
|
+
`ClientConfig.getAppFees` may return a per-application fee list asynchronously. It is called once for every new quote or preview, and the result is frozen in `QuotePlan.appFees` for later formal withdrawal re-quotes. Return `[]` to explicitly disable app fees for that plan. A refreshed withdrawal review loads a new fee configuration; funding-only quote refreshes preserve the fee already reviewed by the user.
|
|
75
79
|
|
|
76
80
|
## Funding adapters and paths
|
|
77
81
|
|
package/dist/adapters/index.cjs
CHANGED
|
@@ -106,7 +106,7 @@ function deriveConfidentialAccountId(kind, address) {
|
|
|
106
106
|
return `0x${import_base.base16.encode(bytes.slice(1)).toLowerCase()}`;
|
|
107
107
|
}
|
|
108
108
|
function identityKey(identity) {
|
|
109
|
-
return `${identity.kind}:${deriveConfidentialAccountId(identity.kind, identity.accountId)}
|
|
109
|
+
return `${identity.kind}:${deriveConfidentialAccountId(identity.kind, identity.accountId)}`;
|
|
110
110
|
}
|
|
111
111
|
function sameLinking(a, b) {
|
|
112
112
|
return identityKey(a) === identityKey(b);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/index.ts","../../src/errors.ts","../../src/identity.ts","../../src/utils.ts","../../src/signatures.ts","../../src/validation.ts"],"sourcesContent":["import { base58 } from \"@scure/base\";\nimport { assert, ConfidentialSwapError, isUserRejectedError, responseAssert, throwIfAborted, userRejectedError } from \"../errors\";\nimport { sameLinking } from \"../identity\";\nimport { decodeEd25519, decodeNep413Nonce, normalizeSecpSignature, toSignatureBytes } from \"../signatures\";\nimport { clone } from \"../utils\";\nimport { validateSigned } from \"../validation\";\nimport type { HttpConfidentialApi } from \"../api\";\nimport type { LinkingIdentity, SignedData, SigningAdapter, UnsignedData } from \"../types\";\n\nexport interface EvmMessageSigner { getAddress(): Promise<string>; signMessage(message: string): Promise<string> }\nexport function createEvmSigningAdapter(getSigner: () => Promise<EvmMessageSigner> | EvmMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"evm\", accountId: await (await getSigner()).getAddress() }),\n sign: async (data) => {\n assert(data.standard === \"erc191\", \"Expected ERC-191 payload.\");\n return { ...data, signature: normalizeSecpSignature(await (await getSigner()).signMessage(data.payload)) };\n },\n });\n}\nexport interface SolanaMessageSigner { publicKey: { toBase58(): string }; signMessage(message: Uint8Array): Promise<unknown> }\nexport function createSolanaSigningAdapter(getWallet: () => SolanaMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"solana\", accountId: getWallet().publicKey.toBase58(), publicKey: `ed25519:${getWallet().publicKey.toBase58()}` }),\n sign: async (data) => {\n assert(data.standard === \"raw_ed25519\", \"Expected raw Ed25519 payload.\");\n const wallet = getWallet();\n const signature = toSignatureBytes(await wallet.signMessage(new TextEncoder().encode(data.payload)));\n return { ...data, public_key: `ed25519:${wallet.publicKey.toBase58()}`, signature: `ed25519:${base58.encode(decodeEd25519(signature, 64))}` };\n },\n });\n}\nexport interface TronMessageSigner { getAddress(): Promise<string> | string; signMessageV2(message: string): Promise<string> }\nexport function createTronSigningAdapter(getWallet: () => TronMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"tron\", accountId: await getWallet().getAddress() }),\n sign: async (data) => {\n assert(data.standard === \"tip191\", \"Expected TIP-191 payload.\");\n return { ...data, signature: normalizeSecpSignature(await getWallet().signMessageV2(data.payload)) };\n },\n });\n}\nexport interface NearMessageSigner {\n getIdentity(): Promise<{ accountId: string; publicKey: string }>;\n signMessage(params: { message: string; recipient: string; nonce: Uint8Array; callbackUrl?: string }): Promise<{ accountId: string; publicKey: string; signature: string }>;\n /** Explicit user-approved NEAR transaction: add_public_key on the configured Intents contract. */\n registerPublicKey?(accountId: string, publicKey: string, contract: string, signal?: AbortSignal): Promise<void>;\n}\nexport function createNearSigningAdapter(wallet: NearMessageSigner, api: Pick<HttpConfidentialApi, \"viewIntents\" | \"intentsContract\">): SigningAdapter {\n const getIdentity = async (): Promise<LinkingIdentity> => {\n const identity = await wallet.getIdentity();\n return { kind: \"near\", accountId: identity.accountId, publicKey: `ed25519:${base58.encode(decodeEd25519(identity.publicKey, 32))}` };\n };\n return createCheckedAdapter({\n getIdentity,\n prepare: async (signal) => {\n const identity = await getIdentity();\n if (/^[0-9a-f]{64}$/.test(identity.accountId)) {\n const keyHex = Array.from(decodeEd25519(identity.publicKey!, 32), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n assert(keyHex === identity.accountId, \"Implicit NEAR account does not match its signing public key.\");\n } else {\n const registered = await api.viewIntents<boolean>(\"has_public_key\", { account_id: identity.accountId, public_key: identity.publicKey }, signal);\n responseAssert(typeof registered === \"boolean\", \"Invalid key registration response.\");\n if (!registered) {\n assert(wallet.registerPublicKey, \"NEAR key registration requires an explicit registerPublicKey callback.\");\n throwIfAborted(signal);\n await wallet.registerPublicKey(identity.accountId, identity.publicKey!, api.intentsContract, signal);\n responseAssert(await api.viewIntents<boolean>(\"has_public_key\", { account_id: identity.accountId, public_key: identity.publicKey }, signal) === true, \"NEAR signing key registration is not confirmed.\");\n }\n }\n },\n sign: async (data) => {\n assert(data.standard === \"nep413\", \"Expected NEP-413 payload.\");\n const result = await wallet.signMessage({ ...data.payload, nonce: decodeNep413Nonce(data.payload.nonce) });\n responseAssert(result.accountId === (await getIdentity()).accountId, \"NEAR wallet returned a different account.\");\n return { ...data, public_key: `ed25519:${base58.encode(decodeEd25519(result.publicKey, 32))}`, signature: `ed25519:${base58.encode(decodeEd25519(result.signature, 64))}` };\n },\n });\n}\nexport function createCheckedAdapter(adapter: SigningAdapter): SigningAdapter {\n return {\n getIdentity: () => adapter.getIdentity(),\n prepare: adapter.prepare?.bind(adapter),\n sign: async (data: UnsignedData, signal?: AbortSignal): Promise<SignedData> => {\n throwIfAborted(signal);\n const identity = await adapter.getIdentity();\n let signed: SignedData;\n try {\n signed = await adapter.sign(clone(data), signal);\n } catch (error) {\n if (isUserRejectedError(error)) throw userRejectedError();\n throw error;\n }\n throwIfAborted(signal);\n if (!sameLinking(identity, await adapter.getIdentity())) throw new ConfidentialSwapError(\"WALLET_CHANGED\", \"Wallet identity changed while signing.\");\n validateSigned(data, signed, identity);\n return signed;\n },\n };\n}\n","export type ErrorCode =\n | \"INVALID_INPUT\" | \"INVALID_RESPONSE\" | \"HTTP_ERROR\" | \"UNAUTHORIZED\"\n | \"WALLET_CHANGED\" | \"MISSING_WALLET\" | \"QUOTE_EXPIRED\" | \"BUSY\"\n | \"CONFIRMATION_REQUIRED\" | \"USER_REJECTED\" | \"TIMEOUT\" | \"ABORTED\" | \"TERMINAL_FAILURE\";\n\nexport class ConfidentialSwapError extends Error {\n readonly name = \"ConfidentialSwapError\";\n constructor(\n readonly code: ErrorCode,\n message: string,\n readonly status?: number,\n readonly executionId?: string,\n ) { super(message); }\n}\n\nexport function assert(condition: unknown, message: string): asserts condition {\n if (!condition) throw new ConfidentialSwapError(\"INVALID_INPUT\", message);\n}\n\nexport function responseAssert(condition: unknown, message: string): asserts condition {\n if (!condition) throw new ConfidentialSwapError(\"INVALID_RESPONSE\", message);\n}\n\nexport function isUserRejectedError(error: unknown): boolean {\n if (error instanceof ConfidentialSwapError) return error.code === \"USER_REJECTED\";\n if (!error || typeof error !== \"object\") return false;\n const value = error as { code?: unknown; message?: unknown; error?: unknown };\n const code = String(value.code ?? \"\").toUpperCase();\n const message = String(value.message ?? value.error ?? \"\");\n return (\n code === \"4001\" ||\n code === \"ACTION_REJECTED\" ||\n /user.*(reject|cancel|den(?:y|ied))|(?:reject|cancel|den(?:y|ied)).*user/i.test(message)\n );\n}\n\nexport function userRejectedError(executionId?: string): ConfidentialSwapError {\n return new ConfidentialSwapError(\n \"USER_REJECTED\",\n \"User rejected transaction.\",\n undefined,\n executionId,\n );\n}\n\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted)\n throw new ConfidentialSwapError(\"ABORTED\", \"Trade cancelled.\");\n}\n","import { base16, base58, base58check } from \"@scure/base\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { assert } from \"./errors\";\nimport { text } from \"./utils\";\nimport type { FundingIdentity, LinkingIdentity, LinkingKind, SigningStandard } from \"./types\";\n\nexport const CONFIDENTIAL_LINKING_KINDS = [\"near\", \"evm\", \"solana\", \"tron\"] as const;\nexport const standards: Record<LinkingKind, SigningStandard> = { near: \"nep413\", evm: \"erc191\", solana: \"raw_ed25519\", tron: \"tip191\" };\nexport function deriveConfidentialAccountId(kind: LinkingKind, address: string): string {\n const account = text(address, \"Wallet account\");\n if (kind === \"near\") {\n assert(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(account) && account.length >= 2 && account.length <= 64, \"Invalid NEAR account.\");\n return account;\n }\n if (kind === \"evm\") { assert(/^0x[0-9a-f]{40}$/i.test(account), \"Invalid EVM address.\"); return account.toLowerCase(); }\n if (kind === \"solana\") {\n const bytes = base58.decode(account);\n assert(bytes.length === 32, \"Invalid Solana public key.\");\n return base16.encode(bytes).toLowerCase();\n }\n assert(kind === \"tron\", \"Unsupported linking wallet kind.\");\n const bytes = base58check(sha256).decode(account);\n assert(bytes.length === 21 && bytes[0] === 0x41, \"Invalid Tron address.\");\n return `0x${base16.encode(bytes.slice(1)).toLowerCase()}`;\n}\nexport function identityKey(identity: LinkingIdentity): string {\n return `${identity.kind}:${deriveConfidentialAccountId(identity.kind, identity.accountId)}:${identity.publicKey ?? \"\"}`;\n}\nexport function sameLinking(a: LinkingIdentity, b: LinkingIdentity): boolean { return identityKey(a) === identityKey(b); }\nexport function sameFunding(a: FundingIdentity, b: FundingIdentity): boolean {\n return a.kind === b.kind && a.chain === b.chain && (a.kind === \"evm\" ? a.accountId.toLowerCase() === b.accountId.toLowerCase() : a.accountId === b.accountId);\n}\nexport function resolveLinkingWallet(input: { sourceKind?: string; wallets: readonly LinkingIdentity[]; preferred?: LinkingIdentity }): LinkingIdentity | null {\n const wallets = input.wallets.filter((w) => CONFIDENTIAL_LINKING_KINDS.includes(w.kind) && w.accountId.trim());\n if (input.preferred) return wallets.find((w) => sameLinking(w, input.preferred!)) ?? null;\n return wallets.find((w) => w.kind === input.sourceKind) ?? CONFIDENTIAL_LINKING_KINDS.map((kind) => wallets.find((w) => w.kind === kind)).find(Boolean) ?? null;\n}\n","import { sha256 } from \"@noble/hashes/sha256\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport { assert, throwIfAborted } from \"./errors\";\nimport type { Balance, QuotePlan } from \"./types\";\n\nexport function raw(value: unknown, field = \"Amount\", allowZero = false): bigint {\n assert(typeof value === \"string\" && /^(0|[1-9]\\d*)$/.test(value), `${field} must be a canonical integer string.`);\n const result = BigInt(value);\n assert(allowZero || result > 0n, `${field} must be positive.`);\n return result;\n}\n/** Rescale a raw integer between token decimals (floor when shrinking). */\nexport function rescaleRawAmount(amount: string, fromDecimals: number, toDecimals: number): string {\n assert(Number.isInteger(fromDecimals) && fromDecimals >= 0 && fromDecimals <= 255, \"Invalid source decimals.\");\n assert(Number.isInteger(toDecimals) && toDecimals >= 0 && toDecimals <= 255, \"Invalid target decimals.\");\n const value = raw(amount);\n if (fromDecimals === toDecimals) return value.toString();\n if (toDecimals > fromDecimals) {\n return (value * 10n ** BigInt(toDecimals - fromDecimals)).toString();\n }\n const scaled = value / 10n ** BigInt(fromDecimals - toDecimals);\n assert(scaled > 0n, \"Amount too small after decimal conversion.\");\n return scaled.toString();\n}\nexport function text(value: unknown, field: string): string {\n assert(typeof value === \"string\" && value.trim().length > 0, `${field} is required.`);\n return value.trim();\n}\nexport function clone<T>(value: T): T { return structuredClone(value); }\nfunction canonical(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonical).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value).filter(([, v]) => v !== undefined).sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(\",\")}}`;\n }\n assert(value === null || [\"string\", \"boolean\", \"number\"].includes(typeof value), \"Plan data must be JSON-serializable.\");\n if (typeof value === \"number\") assert(Number.isFinite(value), \"Plan numbers must be finite.\");\n return JSON.stringify(value);\n}\nexport function planDigest(plan: Omit<QuotePlan, \"digest\"> | QuotePlan): string {\n const { digest: _digest, ...body } = plan as QuotePlan;\n return bytesToHex(sha256(new TextEncoder().encode(canonical(body))));\n}\nexport function randomFraction(): number {\n return crypto.getRandomValues(new Uint32Array(1))[0]! / 0xffffffff;\n}\nexport function splitConfidentialRawAmount(totalRaw: string, count: number, values?: readonly number[]): string[] {\n assert(Number.isInteger(count) && count >= 1 && count <= 10, \"A batch requires 1 to 10 recipients.\");\n const total = raw(totalRaw);\n assert(total >= BigInt(count), \"Amount too small for every recipient.\");\n const minimum = total * 80n / (100n * BigInt(count));\n const lowBound = minimum > 0n ? minimum : 1n;\n const denominator = 100n * BigInt(count);\n const maximum = (total * 120n + denominator - 1n) / denominator;\n const highBound = maximum > lowBound ? maximum : lowBound;\n const shares: bigint[] = [];\n let remaining = total;\n for (let i = 0; i < count - 1; i++) {\n const left = BigInt(count - i - 1);\n const low = remaining - highBound * left > lowBound ? remaining - highBound * left : lowBound;\n const high = remaining - lowBound * left < highBound ? remaining - lowBound * left : highBound;\n assert(low <= high, \"Amount cannot be split safely.\");\n const fraction = values?.[i] ?? randomFraction();\n assert(Number.isFinite(fraction) && fraction >= 0 && fraction <= 1, \"Random fractions must be within [0, 1].\");\n const share = low + (high - low) * BigInt(Math.floor(fraction * 1_000_000)) / 1_000_000n;\n shares.push(share);\n remaining -= share;\n }\n assert(remaining >= lowBound && remaining <= highBound, \"Final share outside safe bounds.\");\n return [...shares, remaining].map(String);\n}\nexport function availableForAsset(balances: readonly Balance[], assetId: string): string {\n const exact = balances.find((b) => b.tokenId === assetId);\n const entry = exact ?? balances.find((b) => b.tokenId.startsWith(\"imt:\") && b.tokenId.endsWith(`:${assetId}`));\n return raw(entry?.available ?? \"0\", \"Available balance\", true).toString();\n}\nexport async function delay(ms: number, signal?: AbortSignal): Promise<void> {\n throwIfAborted(signal);\n await new Promise<void>((resolve, reject) => {\n const finish = () => { signal?.removeEventListener(\"abort\", abort); resolve(); };\n const timer = setTimeout(finish, ms);\n const abort = () => { clearTimeout(timer); signal?.removeEventListener(\"abort\", abort); try { throwIfAborted(signal); } catch (e) { reject(e); } };\n signal?.addEventListener(\"abort\", abort, { once: true });\n });\n}\n","import { ed25519 } from \"@noble/curves/ed25519\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { keccak_256 } from \"@noble/hashes/sha3\";\nimport { concatBytes } from \"@noble/hashes/utils\";\nimport { base16, base58, base64, base64nopad, base64urlnopad } from \"@scure/base\";\nimport { responseAssert } from \"./errors\";\nimport { deriveConfidentialAccountId } from \"./identity\";\nimport type { LinkingIdentity, SignedData, UnsignedData } from \"./types\";\n\nconst encode = (s: string) => new TextEncoder().encode(s);\nfunction u32(value: number): Uint8Array { const b = new Uint8Array(4); new DataView(b.buffer).setUint32(0, value, true); return b; }\nfunction borshString(s: string): Uint8Array { const b = encode(s); return concatBytes(u32(b.length), b); }\nexport function decodeNep413Nonce(value: string): Uint8Array {\n responseAssert(typeof value === \"string\", \"Invalid NEP-413 nonce.\");\n const normalized = value.trim();\n let nonce: Uint8Array;\n try { nonce = base64.decode(normalized); }\n catch {\n const unpadded = normalized.replace(/=+$/, \"\");\n try { nonce = base64urlnopad.decode(unpadded); }\n catch {\n try { nonce = base64nopad.decode(unpadded); }\n catch { responseAssert(false, \"Invalid NEP-413 nonce encoding.\"); }\n }\n }\n responseAssert(nonce.length === 32, \"The NEP-413 nonce must be 32 bytes.\");\n return nonce;\n}\nexport function nep413Hash(payload: Extract<UnsignedData, { standard: \"nep413\" }>[\"payload\"]): Uint8Array {\n const nonce = decodeNep413Nonce(payload.nonce);\n return sha256(concatBytes(u32(2 ** 31 + 413), borshString(payload.message), nonce, borshString(payload.recipient), payload.callbackUrl === undefined ? new Uint8Array([0]) : concatBytes(new Uint8Array([1]), borshString(payload.callbackUrl))));\n}\nexport function personalMessageHash(message: string, kind: \"evm\" | \"tron\"): Uint8Array {\n const bytes = encode(message);\n return keccak_256(concatBytes(encode(`\\x19${kind === \"evm\" ? \"Ethereum\" : \"TRON\"} Signed Message:\\n${bytes.length}`), bytes));\n}\nexport function toSignatureBytes(value: unknown): Uint8Array {\n if (value instanceof Uint8Array) return value;\n if (value && typeof value === \"object\" && \"signature\" in value) {\n return toSignatureBytes((value as { signature: unknown }).signature);\n }\n if (ArrayBuffer.isView(value)) {\n return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n }\n throw new Error(\"The wallet returned an invalid Ed25519 signature.\");\n}\nexport function decodeEd25519(value: string | Uint8Array, length: number): Uint8Array {\n if (value instanceof Uint8Array) { responseAssert(value.length === length, \"Invalid Ed25519 byte length.\"); return new Uint8Array(value); }\n const raw = value.replace(/^ed25519:/, \"\");\n for (const decoder of [() => base58.decode(raw), () => base64.decode(raw), () => base16.decode(raw.replace(/^0x/, \"\").toUpperCase())]) {\n try { const b = decoder(); if (b.length === length) return b; } catch { /* Wallets use base58, base64, or hex. */ }\n }\n responseAssert(false, \"Invalid Ed25519 encoding.\");\n}\nexport function normalizeSecpSignature(value: string): string {\n const hex = value.replace(/^0x/i, \"\");\n responseAssert(/^[0-9a-f]{130}$/i.test(hex), \"Expected a 65-byte signature.\");\n const bytes = base16.decode(hex.toUpperCase());\n const v = bytes[64]!;\n responseAssert([0, 1, 27, 28].includes(v), \"Invalid recovery id.\");\n bytes[64] = v < 27 ? v + 27 : v;\n return `0x${base16.encode(bytes).toLowerCase()}`;\n}\nexport function verifyWalletSignature(data: SignedData, identity: LinkingIdentity): void {\n let valid = false;\n try {\n if (identity.kind === \"near\" && data.standard === \"nep413\") {\n const key = decodeEd25519(identity.publicKey ?? \"\", 32);\n valid = ed25519.verify(decodeEd25519(data.signature, 64), nep413Hash(data.payload), key);\n if (/^[0-9a-f]{64}$/.test(identity.accountId)) valid = valid && base16.encode(key).toLowerCase() === identity.accountId;\n } else if (identity.kind === \"solana\" && data.standard === \"raw_ed25519\") {\n valid = ed25519.verify(decodeEd25519(data.signature, 64), encode(data.payload), base58.decode(identity.accountId));\n } else if ((identity.kind === \"evm\" || identity.kind === \"tron\") && typeof data.payload === \"string\") {\n const bytes = base16.decode(normalizeSecpSignature(data.signature).slice(2).toUpperCase());\n const sig = secp256k1.Signature.fromCompact(bytes.slice(0, 64)).addRecoveryBit(bytes[64]! - 27);\n const publicKey = sig.recoverPublicKey(personalMessageHash(data.payload, identity.kind)).toRawBytes(false);\n const address = `0x${base16.encode(keccak_256(publicKey.slice(1)).slice(-20)).toLowerCase()}`;\n valid = address === deriveConfidentialAccountId(identity.kind, identity.accountId);\n }\n } catch { valid = false; }\n responseAssert(valid, \"Signature does not verify against the frozen wallet identity.\");\n}\n","import { base58, base64 } from \"@scure/base\";\nimport { assert, responseAssert } from \"./errors\";\nimport { deriveConfidentialAccountId, standards } from \"./identity\";\nimport { normalizeChain } from \"./routing\";\nimport { raw, text } from \"./utils\";\nimport { decodeNep413Nonce, verifyWalletSignature } from \"./signatures\";\nimport type { LinkingIdentity, QuoteResponse, SignedData, Token, UnsignedData } from \"./types\";\n\nconst evmChains = new Set([\"eth\", \"arb\", \"avax\", \"op\", \"pol\", \"bsc\", \"base\", \"gnosis\", \"aurora\", \"bera\", \"berachain\", \"monad\", \"hypercore\", \"hyperliquid\", \"linea\", \"scroll\", \"zksync\", \"mantle\", \"celo\", \"sonic\"]);\nexport type AddressValidator = (address: string, token: Token) => boolean;\nexport function validateRecipients(addresses: readonly string[], token: Token, custom?: AddressValidator): string[] {\n assert(addresses.length >= 1 && addresses.length <= 10, \"A batch requires 1 to 10 recipients.\");\n const chain = normalizeChain(token.blockchain);\n const evm = evmChains.has(chain) || /^\\d+$/.test(token.swapChain ?? chain);\n const seen = new Set<string>();\n return addresses.map((value) => {\n const address = text(value, \"Recipient address\");\n let valid = false;\n try {\n if (custom) valid = custom(address, token);\n else if (evm) valid = /^0x[0-9a-f]{40}$/i.test(address);\n else if (chain === \"near\") { deriveConfidentialAccountId(\"near\", address); valid = true; }\n else if (chain === \"sol\") valid = base58.decode(address).length === 32;\n else if (chain === \"tron\") { deriveConfidentialAccountId(\"tron\", address); valid = true; }\n else if (chain === \"aptos\") valid = /^0x[0-9a-f]{1,64}$/i.test(address);\n else if (chain === \"sui\") valid = /^0x[0-9a-f]{64}$/i.test(address);\n else assert(false, \"Supply an address validator for this destination chain (including BTC/Zcash).\");\n } catch (error) { if (!custom && !evm && ![\"near\", \"sol\", \"tron\", \"aptos\", \"sui\"].includes(chain)) throw error; }\n assert(valid, \"Invalid recipient address for the destination chain.\");\n const key = evm || [\"aptos\", \"sui\"].includes(chain) ? address.toLowerCase() : address;\n assert(!seen.has(key), \"Duplicate recipient address.\");\n seen.add(key);\n return address;\n });\n}\nexport function validateToken(token: Token): void {\n text(token.assetId, \"Asset id\"); text(token.blockchain, \"Blockchain\"); text(token.symbol, \"Symbol\");\n assert(Number.isInteger(token.decimals) && token.decimals >= 0 && token.decimals <= 255, \"Invalid token decimals.\");\n}\nexport function validateQuote(q: QuoteResponse, requireDeposit: boolean): void {\n responseAssert(q && typeof q.quote === \"object\" && q.quote, \"Missing quote.\");\n raw(q.quote.amountIn, \"Quoted input\"); raw(q.quote.amountOut, \"Quoted output\");\n if (q.quote.minAmountOut !== undefined) assert(raw(q.quote.minAmountOut) <= raw(q.quote.amountOut), \"Minimum output exceeds quoted output.\");\n if (q.quote.minAmountIn !== undefined) raw(q.quote.minAmountIn);\n if (q.quoteRequest?.appFees !== undefined) {\n raw(q.quoteRequest.amount, \"Quote request amount\");\n responseAssert(Array.isArray(q.quoteRequest.appFees), \"Invalid quote app fees.\");\n for (const fee of q.quoteRequest.appFees) {\n responseAssert(fee && Number.isInteger(fee.fee) && fee.fee >= 0, \"Invalid quote app fee rate.\");\n }\n }\n if (requireDeposit) text(q.quote.depositAddress, \"Deposit address\");\n}\nexport function payloadMessage(data: UnsignedData): Record<string, unknown> {\n let message: unknown;\n try { message = JSON.parse(typeof data.payload === \"string\" ? data.payload : data.payload.message); }\n catch { responseAssert(false, \"Invalid intent JSON.\"); }\n responseAssert(message && typeof message === \"object\" && !Array.isArray(message), \"Invalid intent payload.\");\n return message as Record<string, unknown>;\n}\nexport function validatePayload(data: UnsignedData, identity: LinkingIdentity, signerId: string, contract: string, now: number, purpose: \"authorization\" | \"withdrawal\" = \"authorization\"): Record<string, unknown> {\n responseAssert(data.standard === standards[identity.kind], \"Signing standard does not match wallet.\");\n const msg = payloadMessage(data);\n responseAssert(msg.signer_id === signerId, \"Intent signer does not match the frozen identity.\");\n const deadline = Date.parse(String(msg.deadline ?? \"\"));\n responseAssert(Number.isFinite(deadline) && deadline > now, \"Intent deadline expired or invalid.\");\n responseAssert(Array.isArray(msg.intents), \"Intent list is missing.\");\n if (data.standard === \"nep413\") {\n text(data.payload.recipient, \"NEP-413 recipient\");\n // Generated withdrawals carry their own signed recipient; only login binds the configured verifier.\n if (purpose === \"authorization\") responseAssert(data.payload.recipient === contract, \"Invalid NEP-413 authorization verifier.\");\n decodeNep413Nonce(data.payload.nonce);\n } else if (purpose === \"authorization\") {\n responseAssert(msg.verifying_contract === contract && typeof msg.nonce === \"string\" && base64.decode(msg.nonce).length === 32, \"Invalid verifier or nonce.\");\n }\n return msg;\n}\nexport function validateSigned(unsigned: UnsignedData, signed: SignedData, identity: LinkingIdentity): void {\n responseAssert(signed.standard === unsigned.standard && JSON.stringify(signed.payload) === JSON.stringify(unsigned.payload), \"Wallet changed the payload.\");\n text(signed.signature, \"Signature\");\n if (identity.kind === \"near\" || identity.kind === \"solana\") {\n const expected = identity.publicKey ?? (identity.kind === \"solana\" ? `ed25519:${identity.accountId}` : \"\");\n responseAssert(expected && signed.public_key === expected, \"Signing public key changed.\");\n }\n verifyWalletSignature(signed, identity);\n}\n"],"mappings":"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","names":["import_base","import_sha256","bytes","import_sha256","import_utils","import_base","raw","import_base"]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/index.ts","../../src/errors.ts","../../src/identity.ts","../../src/utils.ts","../../src/signatures.ts","../../src/validation.ts"],"sourcesContent":["import { base58 } from \"@scure/base\";\nimport { assert, ConfidentialSwapError, isUserRejectedError, responseAssert, throwIfAborted, userRejectedError } from \"../errors\";\nimport { sameLinking } from \"../identity\";\nimport { decodeEd25519, decodeNep413Nonce, normalizeSecpSignature, toSignatureBytes } from \"../signatures\";\nimport { clone } from \"../utils\";\nimport { validateSigned } from \"../validation\";\nimport type { HttpConfidentialApi } from \"../api\";\nimport type { LinkingIdentity, SignedData, SigningAdapter, UnsignedData } from \"../types\";\n\nexport interface EvmMessageSigner { getAddress(): Promise<string>; signMessage(message: string): Promise<string> }\nexport function createEvmSigningAdapter(getSigner: () => Promise<EvmMessageSigner> | EvmMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"evm\", accountId: await (await getSigner()).getAddress() }),\n sign: async (data) => {\n assert(data.standard === \"erc191\", \"Expected ERC-191 payload.\");\n return { ...data, signature: normalizeSecpSignature(await (await getSigner()).signMessage(data.payload)) };\n },\n });\n}\nexport interface SolanaMessageSigner { publicKey: { toBase58(): string }; signMessage(message: Uint8Array): Promise<unknown> }\nexport function createSolanaSigningAdapter(getWallet: () => SolanaMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"solana\", accountId: getWallet().publicKey.toBase58(), publicKey: `ed25519:${getWallet().publicKey.toBase58()}` }),\n sign: async (data) => {\n assert(data.standard === \"raw_ed25519\", \"Expected raw Ed25519 payload.\");\n const wallet = getWallet();\n const signature = toSignatureBytes(await wallet.signMessage(new TextEncoder().encode(data.payload)));\n return { ...data, public_key: `ed25519:${wallet.publicKey.toBase58()}`, signature: `ed25519:${base58.encode(decodeEd25519(signature, 64))}` };\n },\n });\n}\nexport interface TronMessageSigner { getAddress(): Promise<string> | string; signMessageV2(message: string): Promise<string> }\nexport function createTronSigningAdapter(getWallet: () => TronMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"tron\", accountId: await getWallet().getAddress() }),\n sign: async (data) => {\n assert(data.standard === \"tip191\", \"Expected TIP-191 payload.\");\n return { ...data, signature: normalizeSecpSignature(await getWallet().signMessageV2(data.payload)) };\n },\n });\n}\nexport interface NearMessageSigner {\n getIdentity(): Promise<{ accountId: string; publicKey: string }>;\n signMessage(params: { message: string; recipient: string; nonce: Uint8Array; callbackUrl?: string }): Promise<{ accountId: string; publicKey: string; signature: string }>;\n /** Explicit user-approved NEAR transaction: add_public_key on the configured Intents contract. */\n registerPublicKey?(accountId: string, publicKey: string, contract: string, signal?: AbortSignal): Promise<void>;\n}\nexport function createNearSigningAdapter(wallet: NearMessageSigner, api: Pick<HttpConfidentialApi, \"viewIntents\" | \"intentsContract\">): SigningAdapter {\n const getIdentity = async (): Promise<LinkingIdentity> => {\n const identity = await wallet.getIdentity();\n return { kind: \"near\", accountId: identity.accountId, publicKey: `ed25519:${base58.encode(decodeEd25519(identity.publicKey, 32))}` };\n };\n return createCheckedAdapter({\n getIdentity,\n prepare: async (signal) => {\n const identity = await getIdentity();\n if (/^[0-9a-f]{64}$/.test(identity.accountId)) {\n const keyHex = Array.from(decodeEd25519(identity.publicKey!, 32), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n assert(keyHex === identity.accountId, \"Implicit NEAR account does not match its signing public key.\");\n } else {\n const registered = await api.viewIntents<boolean>(\"has_public_key\", { account_id: identity.accountId, public_key: identity.publicKey }, signal);\n responseAssert(typeof registered === \"boolean\", \"Invalid key registration response.\");\n if (!registered) {\n assert(wallet.registerPublicKey, \"NEAR key registration requires an explicit registerPublicKey callback.\");\n throwIfAborted(signal);\n await wallet.registerPublicKey(identity.accountId, identity.publicKey!, api.intentsContract, signal);\n responseAssert(await api.viewIntents<boolean>(\"has_public_key\", { account_id: identity.accountId, public_key: identity.publicKey }, signal) === true, \"NEAR signing key registration is not confirmed.\");\n }\n }\n },\n sign: async (data) => {\n assert(data.standard === \"nep413\", \"Expected NEP-413 payload.\");\n const result = await wallet.signMessage({ ...data.payload, nonce: decodeNep413Nonce(data.payload.nonce) });\n responseAssert(result.accountId === (await getIdentity()).accountId, \"NEAR wallet returned a different account.\");\n return { ...data, public_key: `ed25519:${base58.encode(decodeEd25519(result.publicKey, 32))}`, signature: `ed25519:${base58.encode(decodeEd25519(result.signature, 64))}` };\n },\n });\n}\nexport function createCheckedAdapter(adapter: SigningAdapter): SigningAdapter {\n return {\n getIdentity: () => adapter.getIdentity(),\n prepare: adapter.prepare?.bind(adapter),\n sign: async (data: UnsignedData, signal?: AbortSignal): Promise<SignedData> => {\n throwIfAborted(signal);\n const identity = await adapter.getIdentity();\n let signed: SignedData;\n try {\n signed = await adapter.sign(clone(data), signal);\n } catch (error) {\n if (isUserRejectedError(error)) throw userRejectedError();\n throw error;\n }\n throwIfAborted(signal);\n if (!sameLinking(identity, await adapter.getIdentity())) throw new ConfidentialSwapError(\"WALLET_CHANGED\", \"Wallet identity changed while signing.\");\n validateSigned(data, signed, identity);\n return signed;\n },\n };\n}\n","export type ErrorCode =\n | \"INVALID_INPUT\" | \"INVALID_RESPONSE\" | \"HTTP_ERROR\" | \"UNAUTHORIZED\"\n | \"WALLET_CHANGED\" | \"MISSING_WALLET\" | \"QUOTE_EXPIRED\" | \"BUSY\"\n | \"CONFIRMATION_REQUIRED\" | \"USER_REJECTED\" | \"TIMEOUT\" | \"ABORTED\" | \"TERMINAL_FAILURE\";\n\nexport class ConfidentialSwapError extends Error {\n readonly name = \"ConfidentialSwapError\";\n constructor(\n readonly code: ErrorCode,\n message: string,\n readonly status?: number,\n readonly executionId?: string,\n ) { super(message); }\n}\n\nexport function assert(condition: unknown, message: string): asserts condition {\n if (!condition) throw new ConfidentialSwapError(\"INVALID_INPUT\", message);\n}\n\nexport function responseAssert(condition: unknown, message: string): asserts condition {\n if (!condition) throw new ConfidentialSwapError(\"INVALID_RESPONSE\", message);\n}\n\nexport function isUserRejectedError(error: unknown): boolean {\n if (error instanceof ConfidentialSwapError) return error.code === \"USER_REJECTED\";\n if (!error || typeof error !== \"object\") return false;\n const value = error as { code?: unknown; message?: unknown; error?: unknown };\n const code = String(value.code ?? \"\").toUpperCase();\n const message = String(value.message ?? value.error ?? \"\");\n return (\n code === \"4001\" ||\n code === \"ACTION_REJECTED\" ||\n /user.*(reject|cancel|den(?:y|ied))|(?:reject|cancel|den(?:y|ied)).*user/i.test(message)\n );\n}\n\nexport function userRejectedError(executionId?: string): ConfidentialSwapError {\n return new ConfidentialSwapError(\n \"USER_REJECTED\",\n \"User rejected transaction.\",\n undefined,\n executionId,\n );\n}\n\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted)\n throw new ConfidentialSwapError(\"ABORTED\", \"Trade cancelled.\");\n}\n","import { base16, base58, base58check } from \"@scure/base\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { assert } from \"./errors\";\nimport { text } from \"./utils\";\nimport type { FundingIdentity, LinkingIdentity, LinkingKind, SigningStandard } from \"./types\";\n\nexport const CONFIDENTIAL_LINKING_KINDS = [\"near\", \"evm\", \"solana\", \"tron\"] as const;\nexport const standards: Record<LinkingKind, SigningStandard> = { near: \"nep413\", evm: \"erc191\", solana: \"raw_ed25519\", tron: \"tip191\" };\nexport function deriveConfidentialAccountId(kind: LinkingKind, address: string): string {\n const account = text(address, \"Wallet account\");\n if (kind === \"near\") {\n assert(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(account) && account.length >= 2 && account.length <= 64, \"Invalid NEAR account.\");\n return account;\n }\n if (kind === \"evm\") { assert(/^0x[0-9a-f]{40}$/i.test(account), \"Invalid EVM address.\"); return account.toLowerCase(); }\n if (kind === \"solana\") {\n const bytes = base58.decode(account);\n assert(bytes.length === 32, \"Invalid Solana public key.\");\n return base16.encode(bytes).toLowerCase();\n }\n assert(kind === \"tron\", \"Unsupported linking wallet kind.\");\n const bytes = base58check(sha256).decode(account);\n assert(bytes.length === 21 && bytes[0] === 0x41, \"Invalid Tron address.\");\n return `0x${base16.encode(bytes.slice(1)).toLowerCase()}`;\n}\nexport function identityKey(identity: LinkingIdentity): string {\n // Session / linking identity is account-scoped — do not include publicKey.\n return `${identity.kind}:${deriveConfidentialAccountId(identity.kind, identity.accountId)}`;\n}\nexport function sameLinking(a: LinkingIdentity, b: LinkingIdentity): boolean { return identityKey(a) === identityKey(b); }\nexport function sameFunding(a: FundingIdentity, b: FundingIdentity): boolean {\n return a.kind === b.kind && a.chain === b.chain && (a.kind === \"evm\" ? a.accountId.toLowerCase() === b.accountId.toLowerCase() : a.accountId === b.accountId);\n}\nexport function resolveLinkingWallet(input: { sourceKind?: string; wallets: readonly LinkingIdentity[]; preferred?: LinkingIdentity }): LinkingIdentity | null {\n const wallets = input.wallets.filter((w) => CONFIDENTIAL_LINKING_KINDS.includes(w.kind) && w.accountId.trim());\n if (input.preferred) return wallets.find((w) => sameLinking(w, input.preferred!)) ?? null;\n return wallets.find((w) => w.kind === input.sourceKind) ?? CONFIDENTIAL_LINKING_KINDS.map((kind) => wallets.find((w) => w.kind === kind)).find(Boolean) ?? null;\n}\n","import { sha256 } from \"@noble/hashes/sha256\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport { assert, throwIfAborted } from \"./errors\";\nimport type { Balance, QuotePlan } from \"./types\";\n\nexport function raw(value: unknown, field = \"Amount\", allowZero = false): bigint {\n assert(typeof value === \"string\" && /^(0|[1-9]\\d*)$/.test(value), `${field} must be a canonical integer string.`);\n const result = BigInt(value);\n assert(allowZero || result > 0n, `${field} must be positive.`);\n return result;\n}\n/** Rescale a raw integer between token decimals (floor when shrinking). */\nexport function rescaleRawAmount(amount: string, fromDecimals: number, toDecimals: number): string {\n assert(Number.isInteger(fromDecimals) && fromDecimals >= 0 && fromDecimals <= 255, \"Invalid source decimals.\");\n assert(Number.isInteger(toDecimals) && toDecimals >= 0 && toDecimals <= 255, \"Invalid target decimals.\");\n const value = raw(amount);\n if (fromDecimals === toDecimals) return value.toString();\n if (toDecimals > fromDecimals) {\n return (value * 10n ** BigInt(toDecimals - fromDecimals)).toString();\n }\n const scaled = value / 10n ** BigInt(fromDecimals - toDecimals);\n assert(scaled > 0n, \"Amount too small after decimal conversion.\");\n return scaled.toString();\n}\nexport function text(value: unknown, field: string): string {\n assert(typeof value === \"string\" && value.trim().length > 0, `${field} is required.`);\n return value.trim();\n}\nexport function clone<T>(value: T): T { return structuredClone(value); }\nfunction canonical(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonical).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value).filter(([, v]) => v !== undefined).sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(\",\")}}`;\n }\n assert(value === null || [\"string\", \"boolean\", \"number\"].includes(typeof value), \"Plan data must be JSON-serializable.\");\n if (typeof value === \"number\") assert(Number.isFinite(value), \"Plan numbers must be finite.\");\n return JSON.stringify(value);\n}\nexport function planDigest(plan: Omit<QuotePlan, \"digest\"> | QuotePlan): string {\n const { digest: _digest, ...body } = plan as QuotePlan;\n return bytesToHex(sha256(new TextEncoder().encode(canonical(body))));\n}\nexport function randomFraction(): number {\n return crypto.getRandomValues(new Uint32Array(1))[0]! / 0xffffffff;\n}\nexport function splitConfidentialRawAmount(totalRaw: string, count: number, values?: readonly number[]): string[] {\n assert(Number.isInteger(count) && count >= 1 && count <= 10, \"A batch requires 1 to 10 recipients.\");\n const total = raw(totalRaw);\n assert(total >= BigInt(count), \"Amount too small for every recipient.\");\n const minimum = total * 80n / (100n * BigInt(count));\n const lowBound = minimum > 0n ? minimum : 1n;\n const denominator = 100n * BigInt(count);\n const maximum = (total * 120n + denominator - 1n) / denominator;\n const highBound = maximum > lowBound ? maximum : lowBound;\n const shares: bigint[] = [];\n let remaining = total;\n for (let i = 0; i < count - 1; i++) {\n const left = BigInt(count - i - 1);\n const low = remaining - highBound * left > lowBound ? remaining - highBound * left : lowBound;\n const high = remaining - lowBound * left < highBound ? remaining - lowBound * left : highBound;\n assert(low <= high, \"Amount cannot be split safely.\");\n const fraction = values?.[i] ?? randomFraction();\n assert(Number.isFinite(fraction) && fraction >= 0 && fraction <= 1, \"Random fractions must be within [0, 1].\");\n const share = low + (high - low) * BigInt(Math.floor(fraction * 1_000_000)) / 1_000_000n;\n shares.push(share);\n remaining -= share;\n }\n assert(remaining >= lowBound && remaining <= highBound, \"Final share outside safe bounds.\");\n return [...shares, remaining].map(String);\n}\n/**\n * Quote / route assetIds (especially `1cs_v1:…`) may differ from the tokenId\n * returned by `/account/balances`. Native assets often settle as\n * `nep141:<symbol>.omft.near` (ZEC/BTC/XRP/SOL/…), not the per-chain 1cs id.\n */\nfunction balanceLookupIds(assetId: string, symbol?: string): string[] {\n const ids = [assetId];\n const near1cs = /^1cs_v1:near:(nep141:.+)$/i.exec(assetId);\n if (near1cs) ids.push(near1cs[1]!);\n const nep = /^(nep141:.+)$/i.exec(assetId);\n if (nep) ids.push(`1cs_v1:near:${nep[1]}`);\n const sym = symbol?.trim().toLowerCase();\n if (sym && /^[a-z0-9]+$/i.test(sym)) {\n const omft = `nep141:${sym}.omft.near`;\n ids.push(omft, `1cs_v1:near:${omft}`);\n }\n return [...new Set(ids)];\n}\n\nexport function availableForAsset(\n balances: readonly Balance[],\n assetId: string,\n symbol?: string,\n): string {\n for (const id of balanceLookupIds(assetId, symbol)) {\n const exact = balances.find((b) => b.tokenId === id);\n const entry =\n exact ??\n balances.find((b) => b.tokenId.startsWith(\"imt:\") && b.tokenId.endsWith(`:${id}`));\n if (entry) return raw(entry.available ?? \"0\", \"Available balance\", true).toString();\n }\n return \"0\";\n}\nexport async function delay(ms: number, signal?: AbortSignal): Promise<void> {\n throwIfAborted(signal);\n await new Promise<void>((resolve, reject) => {\n const finish = () => { signal?.removeEventListener(\"abort\", abort); resolve(); };\n const timer = setTimeout(finish, ms);\n const abort = () => { clearTimeout(timer); signal?.removeEventListener(\"abort\", abort); try { throwIfAborted(signal); } catch (e) { reject(e); } };\n signal?.addEventListener(\"abort\", abort, { once: true });\n });\n}\n","import { ed25519 } from \"@noble/curves/ed25519\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { keccak_256 } from \"@noble/hashes/sha3\";\nimport { concatBytes } from \"@noble/hashes/utils\";\nimport { base16, base58, base64, base64nopad, base64urlnopad } from \"@scure/base\";\nimport { responseAssert } from \"./errors\";\nimport { deriveConfidentialAccountId } from \"./identity\";\nimport type { LinkingIdentity, SignedData, UnsignedData } from \"./types\";\n\nconst encode = (s: string) => new TextEncoder().encode(s);\nfunction u32(value: number): Uint8Array { const b = new Uint8Array(4); new DataView(b.buffer).setUint32(0, value, true); return b; }\nfunction borshString(s: string): Uint8Array { const b = encode(s); return concatBytes(u32(b.length), b); }\nexport function decodeNep413Nonce(value: string): Uint8Array {\n responseAssert(typeof value === \"string\", \"Invalid NEP-413 nonce.\");\n const normalized = value.trim();\n let nonce: Uint8Array;\n try { nonce = base64.decode(normalized); }\n catch {\n const unpadded = normalized.replace(/=+$/, \"\");\n try { nonce = base64urlnopad.decode(unpadded); }\n catch {\n try { nonce = base64nopad.decode(unpadded); }\n catch { responseAssert(false, \"Invalid NEP-413 nonce encoding.\"); }\n }\n }\n responseAssert(nonce.length === 32, \"The NEP-413 nonce must be 32 bytes.\");\n return nonce;\n}\nexport function nep413Hash(payload: Extract<UnsignedData, { standard: \"nep413\" }>[\"payload\"]): Uint8Array {\n const nonce = decodeNep413Nonce(payload.nonce);\n return sha256(concatBytes(u32(2 ** 31 + 413), borshString(payload.message), nonce, borshString(payload.recipient), payload.callbackUrl === undefined ? new Uint8Array([0]) : concatBytes(new Uint8Array([1]), borshString(payload.callbackUrl))));\n}\nexport function personalMessageHash(message: string, kind: \"evm\" | \"tron\"): Uint8Array {\n const bytes = encode(message);\n return keccak_256(concatBytes(encode(`\\x19${kind === \"evm\" ? \"Ethereum\" : \"TRON\"} Signed Message:\\n${bytes.length}`), bytes));\n}\nexport function toSignatureBytes(value: unknown): Uint8Array {\n if (value instanceof Uint8Array) return value;\n if (value && typeof value === \"object\" && \"signature\" in value) {\n return toSignatureBytes((value as { signature: unknown }).signature);\n }\n if (ArrayBuffer.isView(value)) {\n return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n }\n throw new Error(\"The wallet returned an invalid Ed25519 signature.\");\n}\nexport function decodeEd25519(value: string | Uint8Array, length: number): Uint8Array {\n if (value instanceof Uint8Array) { responseAssert(value.length === length, \"Invalid Ed25519 byte length.\"); return new Uint8Array(value); }\n const raw = value.replace(/^ed25519:/, \"\");\n for (const decoder of [() => base58.decode(raw), () => base64.decode(raw), () => base16.decode(raw.replace(/^0x/, \"\").toUpperCase())]) {\n try { const b = decoder(); if (b.length === length) return b; } catch { /* Wallets use base58, base64, or hex. */ }\n }\n responseAssert(false, \"Invalid Ed25519 encoding.\");\n}\nexport function normalizeSecpSignature(value: string): string {\n const hex = value.replace(/^0x/i, \"\");\n responseAssert(/^[0-9a-f]{130}$/i.test(hex), \"Expected a 65-byte signature.\");\n const bytes = base16.decode(hex.toUpperCase());\n const v = bytes[64]!;\n responseAssert([0, 1, 27, 28].includes(v), \"Invalid recovery id.\");\n bytes[64] = v < 27 ? v + 27 : v;\n return `0x${base16.encode(bytes).toLowerCase()}`;\n}\nexport function verifyWalletSignature(data: SignedData, identity: LinkingIdentity): void {\n let valid = false;\n try {\n if (identity.kind === \"near\" && data.standard === \"nep413\") {\n const key = decodeEd25519(identity.publicKey ?? \"\", 32);\n valid = ed25519.verify(decodeEd25519(data.signature, 64), nep413Hash(data.payload), key);\n if (/^[0-9a-f]{64}$/.test(identity.accountId)) valid = valid && base16.encode(key).toLowerCase() === identity.accountId;\n } else if (identity.kind === \"solana\" && data.standard === \"raw_ed25519\") {\n valid = ed25519.verify(decodeEd25519(data.signature, 64), encode(data.payload), base58.decode(identity.accountId));\n } else if ((identity.kind === \"evm\" || identity.kind === \"tron\") && typeof data.payload === \"string\") {\n const bytes = base16.decode(normalizeSecpSignature(data.signature).slice(2).toUpperCase());\n const sig = secp256k1.Signature.fromCompact(bytes.slice(0, 64)).addRecoveryBit(bytes[64]! - 27);\n const publicKey = sig.recoverPublicKey(personalMessageHash(data.payload, identity.kind)).toRawBytes(false);\n const address = `0x${base16.encode(keccak_256(publicKey.slice(1)).slice(-20)).toLowerCase()}`;\n valid = address === deriveConfidentialAccountId(identity.kind, identity.accountId);\n }\n } catch { valid = false; }\n responseAssert(valid, \"Signature does not verify against the frozen wallet identity.\");\n}\n","import { base58, base64 } from \"@scure/base\";\nimport { assert, responseAssert } from \"./errors\";\nimport { deriveConfidentialAccountId, standards } from \"./identity\";\nimport { normalizeChain } from \"./routing\";\nimport { raw, text } from \"./utils\";\nimport { decodeNep413Nonce, verifyWalletSignature } from \"./signatures\";\nimport type { LinkingIdentity, QuoteResponse, SignedData, Token, UnsignedData } from \"./types\";\n\nconst evmChains = new Set([\"eth\", \"arb\", \"avax\", \"op\", \"pol\", \"bsc\", \"base\", \"gnosis\", \"aurora\", \"bera\", \"berachain\", \"monad\", \"hypercore\", \"hyperliquid\", \"linea\", \"scroll\", \"zksync\", \"mantle\", \"celo\", \"sonic\"]);\nexport type AddressValidator = (address: string, token: Token) => boolean;\nexport function validateRecipients(addresses: readonly string[], token: Token, custom?: AddressValidator): string[] {\n assert(addresses.length >= 1 && addresses.length <= 10, \"A batch requires 1 to 10 recipients.\");\n const chain = normalizeChain(token.blockchain);\n const evm = evmChains.has(chain) || /^\\d+$/.test(token.swapChain ?? chain);\n const seen = new Set<string>();\n return addresses.map((value) => {\n const address = text(value, \"Recipient address\");\n let valid = false;\n try {\n if (custom) valid = custom(address, token);\n else if (evm) valid = /^0x[0-9a-f]{40}$/i.test(address);\n else if (chain === \"near\") { deriveConfidentialAccountId(\"near\", address); valid = true; }\n else if (chain === \"sol\") valid = base58.decode(address).length === 32;\n else if (chain === \"tron\") { deriveConfidentialAccountId(\"tron\", address); valid = true; }\n else if (chain === \"aptos\") valid = /^0x[0-9a-f]{1,64}$/i.test(address);\n else if (chain === \"sui\") valid = /^0x[0-9a-f]{64}$/i.test(address);\n else assert(false, \"Supply an address validator for this destination chain (including BTC/Zcash).\");\n } catch (error) { if (!custom && !evm && ![\"near\", \"sol\", \"tron\", \"aptos\", \"sui\"].includes(chain)) throw error; }\n assert(valid, \"Invalid recipient address for the destination chain.\");\n const key = evm || [\"aptos\", \"sui\"].includes(chain) ? address.toLowerCase() : address;\n assert(!seen.has(key), \"Duplicate recipient address.\");\n seen.add(key);\n return address;\n });\n}\nexport function validateToken(token: Token): void {\n text(token.assetId, \"Asset id\"); text(token.blockchain, \"Blockchain\"); text(token.symbol, \"Symbol\");\n assert(Number.isInteger(token.decimals) && token.decimals >= 0 && token.decimals <= 255, \"Invalid token decimals.\");\n}\nexport function validateQuote(q: QuoteResponse, requireDeposit: boolean): void {\n responseAssert(q && typeof q.quote === \"object\" && q.quote, \"Missing quote.\");\n raw(q.quote.amountIn, \"Quoted input\"); raw(q.quote.amountOut, \"Quoted output\");\n if (q.quote.minAmountOut !== undefined) assert(raw(q.quote.minAmountOut) <= raw(q.quote.amountOut), \"Minimum output exceeds quoted output.\");\n if (q.quote.minAmountIn !== undefined) raw(q.quote.minAmountIn);\n if (q.quoteRequest?.appFees !== undefined) {\n raw(q.quoteRequest.amount, \"Quote request amount\");\n responseAssert(Array.isArray(q.quoteRequest.appFees), \"Invalid quote app fees.\");\n for (const fee of q.quoteRequest.appFees) {\n responseAssert(fee && Number.isInteger(fee.fee) && fee.fee >= 0, \"Invalid quote app fee rate.\");\n }\n }\n if (requireDeposit) text(q.quote.depositAddress, \"Deposit address\");\n}\nexport function payloadMessage(data: UnsignedData): Record<string, unknown> {\n let message: unknown;\n try { message = JSON.parse(typeof data.payload === \"string\" ? data.payload : data.payload.message); }\n catch { responseAssert(false, \"Invalid intent JSON.\"); }\n responseAssert(message && typeof message === \"object\" && !Array.isArray(message), \"Invalid intent payload.\");\n return message as Record<string, unknown>;\n}\nexport function validatePayload(data: UnsignedData, identity: LinkingIdentity, signerId: string, contract: string, now: number, purpose: \"authorization\" | \"withdrawal\" = \"authorization\"): Record<string, unknown> {\n responseAssert(data.standard === standards[identity.kind], \"Signing standard does not match wallet.\");\n const msg = payloadMessage(data);\n responseAssert(msg.signer_id === signerId, \"Intent signer does not match the frozen identity.\");\n const deadline = Date.parse(String(msg.deadline ?? \"\"));\n responseAssert(Number.isFinite(deadline) && deadline > now, \"Intent deadline expired or invalid.\");\n responseAssert(Array.isArray(msg.intents), \"Intent list is missing.\");\n if (data.standard === \"nep413\") {\n text(data.payload.recipient, \"NEP-413 recipient\");\n // Generated withdrawals carry their own signed recipient; only login binds the configured verifier.\n if (purpose === \"authorization\") responseAssert(data.payload.recipient === contract, \"Invalid NEP-413 authorization verifier.\");\n decodeNep413Nonce(data.payload.nonce);\n } else if (purpose === \"authorization\") {\n responseAssert(msg.verifying_contract === contract && typeof msg.nonce === \"string\" && base64.decode(msg.nonce).length === 32, \"Invalid verifier or nonce.\");\n }\n return msg;\n}\nexport function validateSigned(unsigned: UnsignedData, signed: SignedData, identity: LinkingIdentity): void {\n responseAssert(signed.standard === unsigned.standard && JSON.stringify(signed.payload) === JSON.stringify(unsigned.payload), \"Wallet changed the payload.\");\n text(signed.signature, \"Signature\");\n if (identity.kind === \"near\" || identity.kind === \"solana\") {\n const expected = identity.publicKey ?? (identity.kind === \"solana\" ? `ed25519:${identity.accountId}` : \"\");\n responseAssert(expected && signed.public_key === expected, \"Signing public key changed.\");\n }\n verifyWalletSignature(signed, identity);\n}\n"],"mappings":"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","names":["import_base","import_sha256","bytes","import_sha256","import_utils","import_base","raw","import_base"]}
|
package/dist/adapters/index.d.ts
CHANGED
package/dist/adapters/index.js
CHANGED
|
@@ -78,7 +78,7 @@ function deriveConfidentialAccountId(kind, address) {
|
|
|
78
78
|
return `0x${base16.encode(bytes.slice(1)).toLowerCase()}`;
|
|
79
79
|
}
|
|
80
80
|
function identityKey(identity) {
|
|
81
|
-
return `${identity.kind}:${deriveConfidentialAccountId(identity.kind, identity.accountId)}
|
|
81
|
+
return `${identity.kind}:${deriveConfidentialAccountId(identity.kind, identity.accountId)}`;
|
|
82
82
|
}
|
|
83
83
|
function sameLinking(a, b) {
|
|
84
84
|
return identityKey(a) === identityKey(b);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/index.ts","../../src/errors.ts","../../src/identity.ts","../../src/utils.ts","../../src/signatures.ts","../../src/validation.ts"],"sourcesContent":["import { base58 } from \"@scure/base\";\nimport { assert, ConfidentialSwapError, isUserRejectedError, responseAssert, throwIfAborted, userRejectedError } from \"../errors\";\nimport { sameLinking } from \"../identity\";\nimport { decodeEd25519, decodeNep413Nonce, normalizeSecpSignature, toSignatureBytes } from \"../signatures\";\nimport { clone } from \"../utils\";\nimport { validateSigned } from \"../validation\";\nimport type { HttpConfidentialApi } from \"../api\";\nimport type { LinkingIdentity, SignedData, SigningAdapter, UnsignedData } from \"../types\";\n\nexport interface EvmMessageSigner { getAddress(): Promise<string>; signMessage(message: string): Promise<string> }\nexport function createEvmSigningAdapter(getSigner: () => Promise<EvmMessageSigner> | EvmMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"evm\", accountId: await (await getSigner()).getAddress() }),\n sign: async (data) => {\n assert(data.standard === \"erc191\", \"Expected ERC-191 payload.\");\n return { ...data, signature: normalizeSecpSignature(await (await getSigner()).signMessage(data.payload)) };\n },\n });\n}\nexport interface SolanaMessageSigner { publicKey: { toBase58(): string }; signMessage(message: Uint8Array): Promise<unknown> }\nexport function createSolanaSigningAdapter(getWallet: () => SolanaMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"solana\", accountId: getWallet().publicKey.toBase58(), publicKey: `ed25519:${getWallet().publicKey.toBase58()}` }),\n sign: async (data) => {\n assert(data.standard === \"raw_ed25519\", \"Expected raw Ed25519 payload.\");\n const wallet = getWallet();\n const signature = toSignatureBytes(await wallet.signMessage(new TextEncoder().encode(data.payload)));\n return { ...data, public_key: `ed25519:${wallet.publicKey.toBase58()}`, signature: `ed25519:${base58.encode(decodeEd25519(signature, 64))}` };\n },\n });\n}\nexport interface TronMessageSigner { getAddress(): Promise<string> | string; signMessageV2(message: string): Promise<string> }\nexport function createTronSigningAdapter(getWallet: () => TronMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"tron\", accountId: await getWallet().getAddress() }),\n sign: async (data) => {\n assert(data.standard === \"tip191\", \"Expected TIP-191 payload.\");\n return { ...data, signature: normalizeSecpSignature(await getWallet().signMessageV2(data.payload)) };\n },\n });\n}\nexport interface NearMessageSigner {\n getIdentity(): Promise<{ accountId: string; publicKey: string }>;\n signMessage(params: { message: string; recipient: string; nonce: Uint8Array; callbackUrl?: string }): Promise<{ accountId: string; publicKey: string; signature: string }>;\n /** Explicit user-approved NEAR transaction: add_public_key on the configured Intents contract. */\n registerPublicKey?(accountId: string, publicKey: string, contract: string, signal?: AbortSignal): Promise<void>;\n}\nexport function createNearSigningAdapter(wallet: NearMessageSigner, api: Pick<HttpConfidentialApi, \"viewIntents\" | \"intentsContract\">): SigningAdapter {\n const getIdentity = async (): Promise<LinkingIdentity> => {\n const identity = await wallet.getIdentity();\n return { kind: \"near\", accountId: identity.accountId, publicKey: `ed25519:${base58.encode(decodeEd25519(identity.publicKey, 32))}` };\n };\n return createCheckedAdapter({\n getIdentity,\n prepare: async (signal) => {\n const identity = await getIdentity();\n if (/^[0-9a-f]{64}$/.test(identity.accountId)) {\n const keyHex = Array.from(decodeEd25519(identity.publicKey!, 32), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n assert(keyHex === identity.accountId, \"Implicit NEAR account does not match its signing public key.\");\n } else {\n const registered = await api.viewIntents<boolean>(\"has_public_key\", { account_id: identity.accountId, public_key: identity.publicKey }, signal);\n responseAssert(typeof registered === \"boolean\", \"Invalid key registration response.\");\n if (!registered) {\n assert(wallet.registerPublicKey, \"NEAR key registration requires an explicit registerPublicKey callback.\");\n throwIfAborted(signal);\n await wallet.registerPublicKey(identity.accountId, identity.publicKey!, api.intentsContract, signal);\n responseAssert(await api.viewIntents<boolean>(\"has_public_key\", { account_id: identity.accountId, public_key: identity.publicKey }, signal) === true, \"NEAR signing key registration is not confirmed.\");\n }\n }\n },\n sign: async (data) => {\n assert(data.standard === \"nep413\", \"Expected NEP-413 payload.\");\n const result = await wallet.signMessage({ ...data.payload, nonce: decodeNep413Nonce(data.payload.nonce) });\n responseAssert(result.accountId === (await getIdentity()).accountId, \"NEAR wallet returned a different account.\");\n return { ...data, public_key: `ed25519:${base58.encode(decodeEd25519(result.publicKey, 32))}`, signature: `ed25519:${base58.encode(decodeEd25519(result.signature, 64))}` };\n },\n });\n}\nexport function createCheckedAdapter(adapter: SigningAdapter): SigningAdapter {\n return {\n getIdentity: () => adapter.getIdentity(),\n prepare: adapter.prepare?.bind(adapter),\n sign: async (data: UnsignedData, signal?: AbortSignal): Promise<SignedData> => {\n throwIfAborted(signal);\n const identity = await adapter.getIdentity();\n let signed: SignedData;\n try {\n signed = await adapter.sign(clone(data), signal);\n } catch (error) {\n if (isUserRejectedError(error)) throw userRejectedError();\n throw error;\n }\n throwIfAborted(signal);\n if (!sameLinking(identity, await adapter.getIdentity())) throw new ConfidentialSwapError(\"WALLET_CHANGED\", \"Wallet identity changed while signing.\");\n validateSigned(data, signed, identity);\n return signed;\n },\n };\n}\n","export type ErrorCode =\n | \"INVALID_INPUT\" | \"INVALID_RESPONSE\" | \"HTTP_ERROR\" | \"UNAUTHORIZED\"\n | \"WALLET_CHANGED\" | \"MISSING_WALLET\" | \"QUOTE_EXPIRED\" | \"BUSY\"\n | \"CONFIRMATION_REQUIRED\" | \"USER_REJECTED\" | \"TIMEOUT\" | \"ABORTED\" | \"TERMINAL_FAILURE\";\n\nexport class ConfidentialSwapError extends Error {\n readonly name = \"ConfidentialSwapError\";\n constructor(\n readonly code: ErrorCode,\n message: string,\n readonly status?: number,\n readonly executionId?: string,\n ) { super(message); }\n}\n\nexport function assert(condition: unknown, message: string): asserts condition {\n if (!condition) throw new ConfidentialSwapError(\"INVALID_INPUT\", message);\n}\n\nexport function responseAssert(condition: unknown, message: string): asserts condition {\n if (!condition) throw new ConfidentialSwapError(\"INVALID_RESPONSE\", message);\n}\n\nexport function isUserRejectedError(error: unknown): boolean {\n if (error instanceof ConfidentialSwapError) return error.code === \"USER_REJECTED\";\n if (!error || typeof error !== \"object\") return false;\n const value = error as { code?: unknown; message?: unknown; error?: unknown };\n const code = String(value.code ?? \"\").toUpperCase();\n const message = String(value.message ?? value.error ?? \"\");\n return (\n code === \"4001\" ||\n code === \"ACTION_REJECTED\" ||\n /user.*(reject|cancel|den(?:y|ied))|(?:reject|cancel|den(?:y|ied)).*user/i.test(message)\n );\n}\n\nexport function userRejectedError(executionId?: string): ConfidentialSwapError {\n return new ConfidentialSwapError(\n \"USER_REJECTED\",\n \"User rejected transaction.\",\n undefined,\n executionId,\n );\n}\n\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted)\n throw new ConfidentialSwapError(\"ABORTED\", \"Trade cancelled.\");\n}\n","import { base16, base58, base58check } from \"@scure/base\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { assert } from \"./errors\";\nimport { text } from \"./utils\";\nimport type { FundingIdentity, LinkingIdentity, LinkingKind, SigningStandard } from \"./types\";\n\nexport const CONFIDENTIAL_LINKING_KINDS = [\"near\", \"evm\", \"solana\", \"tron\"] as const;\nexport const standards: Record<LinkingKind, SigningStandard> = { near: \"nep413\", evm: \"erc191\", solana: \"raw_ed25519\", tron: \"tip191\" };\nexport function deriveConfidentialAccountId(kind: LinkingKind, address: string): string {\n const account = text(address, \"Wallet account\");\n if (kind === \"near\") {\n assert(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(account) && account.length >= 2 && account.length <= 64, \"Invalid NEAR account.\");\n return account;\n }\n if (kind === \"evm\") { assert(/^0x[0-9a-f]{40}$/i.test(account), \"Invalid EVM address.\"); return account.toLowerCase(); }\n if (kind === \"solana\") {\n const bytes = base58.decode(account);\n assert(bytes.length === 32, \"Invalid Solana public key.\");\n return base16.encode(bytes).toLowerCase();\n }\n assert(kind === \"tron\", \"Unsupported linking wallet kind.\");\n const bytes = base58check(sha256).decode(account);\n assert(bytes.length === 21 && bytes[0] === 0x41, \"Invalid Tron address.\");\n return `0x${base16.encode(bytes.slice(1)).toLowerCase()}`;\n}\nexport function identityKey(identity: LinkingIdentity): string {\n return `${identity.kind}:${deriveConfidentialAccountId(identity.kind, identity.accountId)}:${identity.publicKey ?? \"\"}`;\n}\nexport function sameLinking(a: LinkingIdentity, b: LinkingIdentity): boolean { return identityKey(a) === identityKey(b); }\nexport function sameFunding(a: FundingIdentity, b: FundingIdentity): boolean {\n return a.kind === b.kind && a.chain === b.chain && (a.kind === \"evm\" ? a.accountId.toLowerCase() === b.accountId.toLowerCase() : a.accountId === b.accountId);\n}\nexport function resolveLinkingWallet(input: { sourceKind?: string; wallets: readonly LinkingIdentity[]; preferred?: LinkingIdentity }): LinkingIdentity | null {\n const wallets = input.wallets.filter((w) => CONFIDENTIAL_LINKING_KINDS.includes(w.kind) && w.accountId.trim());\n if (input.preferred) return wallets.find((w) => sameLinking(w, input.preferred!)) ?? null;\n return wallets.find((w) => w.kind === input.sourceKind) ?? CONFIDENTIAL_LINKING_KINDS.map((kind) => wallets.find((w) => w.kind === kind)).find(Boolean) ?? null;\n}\n","import { sha256 } from \"@noble/hashes/sha256\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport { assert, throwIfAborted } from \"./errors\";\nimport type { Balance, QuotePlan } from \"./types\";\n\nexport function raw(value: unknown, field = \"Amount\", allowZero = false): bigint {\n assert(typeof value === \"string\" && /^(0|[1-9]\\d*)$/.test(value), `${field} must be a canonical integer string.`);\n const result = BigInt(value);\n assert(allowZero || result > 0n, `${field} must be positive.`);\n return result;\n}\n/** Rescale a raw integer between token decimals (floor when shrinking). */\nexport function rescaleRawAmount(amount: string, fromDecimals: number, toDecimals: number): string {\n assert(Number.isInteger(fromDecimals) && fromDecimals >= 0 && fromDecimals <= 255, \"Invalid source decimals.\");\n assert(Number.isInteger(toDecimals) && toDecimals >= 0 && toDecimals <= 255, \"Invalid target decimals.\");\n const value = raw(amount);\n if (fromDecimals === toDecimals) return value.toString();\n if (toDecimals > fromDecimals) {\n return (value * 10n ** BigInt(toDecimals - fromDecimals)).toString();\n }\n const scaled = value / 10n ** BigInt(fromDecimals - toDecimals);\n assert(scaled > 0n, \"Amount too small after decimal conversion.\");\n return scaled.toString();\n}\nexport function text(value: unknown, field: string): string {\n assert(typeof value === \"string\" && value.trim().length > 0, `${field} is required.`);\n return value.trim();\n}\nexport function clone<T>(value: T): T { return structuredClone(value); }\nfunction canonical(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonical).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value).filter(([, v]) => v !== undefined).sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(\",\")}}`;\n }\n assert(value === null || [\"string\", \"boolean\", \"number\"].includes(typeof value), \"Plan data must be JSON-serializable.\");\n if (typeof value === \"number\") assert(Number.isFinite(value), \"Plan numbers must be finite.\");\n return JSON.stringify(value);\n}\nexport function planDigest(plan: Omit<QuotePlan, \"digest\"> | QuotePlan): string {\n const { digest: _digest, ...body } = plan as QuotePlan;\n return bytesToHex(sha256(new TextEncoder().encode(canonical(body))));\n}\nexport function randomFraction(): number {\n return crypto.getRandomValues(new Uint32Array(1))[0]! / 0xffffffff;\n}\nexport function splitConfidentialRawAmount(totalRaw: string, count: number, values?: readonly number[]): string[] {\n assert(Number.isInteger(count) && count >= 1 && count <= 10, \"A batch requires 1 to 10 recipients.\");\n const total = raw(totalRaw);\n assert(total >= BigInt(count), \"Amount too small for every recipient.\");\n const minimum = total * 80n / (100n * BigInt(count));\n const lowBound = minimum > 0n ? minimum : 1n;\n const denominator = 100n * BigInt(count);\n const maximum = (total * 120n + denominator - 1n) / denominator;\n const highBound = maximum > lowBound ? maximum : lowBound;\n const shares: bigint[] = [];\n let remaining = total;\n for (let i = 0; i < count - 1; i++) {\n const left = BigInt(count - i - 1);\n const low = remaining - highBound * left > lowBound ? remaining - highBound * left : lowBound;\n const high = remaining - lowBound * left < highBound ? remaining - lowBound * left : highBound;\n assert(low <= high, \"Amount cannot be split safely.\");\n const fraction = values?.[i] ?? randomFraction();\n assert(Number.isFinite(fraction) && fraction >= 0 && fraction <= 1, \"Random fractions must be within [0, 1].\");\n const share = low + (high - low) * BigInt(Math.floor(fraction * 1_000_000)) / 1_000_000n;\n shares.push(share);\n remaining -= share;\n }\n assert(remaining >= lowBound && remaining <= highBound, \"Final share outside safe bounds.\");\n return [...shares, remaining].map(String);\n}\nexport function availableForAsset(balances: readonly Balance[], assetId: string): string {\n const exact = balances.find((b) => b.tokenId === assetId);\n const entry = exact ?? balances.find((b) => b.tokenId.startsWith(\"imt:\") && b.tokenId.endsWith(`:${assetId}`));\n return raw(entry?.available ?? \"0\", \"Available balance\", true).toString();\n}\nexport async function delay(ms: number, signal?: AbortSignal): Promise<void> {\n throwIfAborted(signal);\n await new Promise<void>((resolve, reject) => {\n const finish = () => { signal?.removeEventListener(\"abort\", abort); resolve(); };\n const timer = setTimeout(finish, ms);\n const abort = () => { clearTimeout(timer); signal?.removeEventListener(\"abort\", abort); try { throwIfAborted(signal); } catch (e) { reject(e); } };\n signal?.addEventListener(\"abort\", abort, { once: true });\n });\n}\n","import { ed25519 } from \"@noble/curves/ed25519\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { keccak_256 } from \"@noble/hashes/sha3\";\nimport { concatBytes } from \"@noble/hashes/utils\";\nimport { base16, base58, base64, base64nopad, base64urlnopad } from \"@scure/base\";\nimport { responseAssert } from \"./errors\";\nimport { deriveConfidentialAccountId } from \"./identity\";\nimport type { LinkingIdentity, SignedData, UnsignedData } from \"./types\";\n\nconst encode = (s: string) => new TextEncoder().encode(s);\nfunction u32(value: number): Uint8Array { const b = new Uint8Array(4); new DataView(b.buffer).setUint32(0, value, true); return b; }\nfunction borshString(s: string): Uint8Array { const b = encode(s); return concatBytes(u32(b.length), b); }\nexport function decodeNep413Nonce(value: string): Uint8Array {\n responseAssert(typeof value === \"string\", \"Invalid NEP-413 nonce.\");\n const normalized = value.trim();\n let nonce: Uint8Array;\n try { nonce = base64.decode(normalized); }\n catch {\n const unpadded = normalized.replace(/=+$/, \"\");\n try { nonce = base64urlnopad.decode(unpadded); }\n catch {\n try { nonce = base64nopad.decode(unpadded); }\n catch { responseAssert(false, \"Invalid NEP-413 nonce encoding.\"); }\n }\n }\n responseAssert(nonce.length === 32, \"The NEP-413 nonce must be 32 bytes.\");\n return nonce;\n}\nexport function nep413Hash(payload: Extract<UnsignedData, { standard: \"nep413\" }>[\"payload\"]): Uint8Array {\n const nonce = decodeNep413Nonce(payload.nonce);\n return sha256(concatBytes(u32(2 ** 31 + 413), borshString(payload.message), nonce, borshString(payload.recipient), payload.callbackUrl === undefined ? new Uint8Array([0]) : concatBytes(new Uint8Array([1]), borshString(payload.callbackUrl))));\n}\nexport function personalMessageHash(message: string, kind: \"evm\" | \"tron\"): Uint8Array {\n const bytes = encode(message);\n return keccak_256(concatBytes(encode(`\\x19${kind === \"evm\" ? \"Ethereum\" : \"TRON\"} Signed Message:\\n${bytes.length}`), bytes));\n}\nexport function toSignatureBytes(value: unknown): Uint8Array {\n if (value instanceof Uint8Array) return value;\n if (value && typeof value === \"object\" && \"signature\" in value) {\n return toSignatureBytes((value as { signature: unknown }).signature);\n }\n if (ArrayBuffer.isView(value)) {\n return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n }\n throw new Error(\"The wallet returned an invalid Ed25519 signature.\");\n}\nexport function decodeEd25519(value: string | Uint8Array, length: number): Uint8Array {\n if (value instanceof Uint8Array) { responseAssert(value.length === length, \"Invalid Ed25519 byte length.\"); return new Uint8Array(value); }\n const raw = value.replace(/^ed25519:/, \"\");\n for (const decoder of [() => base58.decode(raw), () => base64.decode(raw), () => base16.decode(raw.replace(/^0x/, \"\").toUpperCase())]) {\n try { const b = decoder(); if (b.length === length) return b; } catch { /* Wallets use base58, base64, or hex. */ }\n }\n responseAssert(false, \"Invalid Ed25519 encoding.\");\n}\nexport function normalizeSecpSignature(value: string): string {\n const hex = value.replace(/^0x/i, \"\");\n responseAssert(/^[0-9a-f]{130}$/i.test(hex), \"Expected a 65-byte signature.\");\n const bytes = base16.decode(hex.toUpperCase());\n const v = bytes[64]!;\n responseAssert([0, 1, 27, 28].includes(v), \"Invalid recovery id.\");\n bytes[64] = v < 27 ? v + 27 : v;\n return `0x${base16.encode(bytes).toLowerCase()}`;\n}\nexport function verifyWalletSignature(data: SignedData, identity: LinkingIdentity): void {\n let valid = false;\n try {\n if (identity.kind === \"near\" && data.standard === \"nep413\") {\n const key = decodeEd25519(identity.publicKey ?? \"\", 32);\n valid = ed25519.verify(decodeEd25519(data.signature, 64), nep413Hash(data.payload), key);\n if (/^[0-9a-f]{64}$/.test(identity.accountId)) valid = valid && base16.encode(key).toLowerCase() === identity.accountId;\n } else if (identity.kind === \"solana\" && data.standard === \"raw_ed25519\") {\n valid = ed25519.verify(decodeEd25519(data.signature, 64), encode(data.payload), base58.decode(identity.accountId));\n } else if ((identity.kind === \"evm\" || identity.kind === \"tron\") && typeof data.payload === \"string\") {\n const bytes = base16.decode(normalizeSecpSignature(data.signature).slice(2).toUpperCase());\n const sig = secp256k1.Signature.fromCompact(bytes.slice(0, 64)).addRecoveryBit(bytes[64]! - 27);\n const publicKey = sig.recoverPublicKey(personalMessageHash(data.payload, identity.kind)).toRawBytes(false);\n const address = `0x${base16.encode(keccak_256(publicKey.slice(1)).slice(-20)).toLowerCase()}`;\n valid = address === deriveConfidentialAccountId(identity.kind, identity.accountId);\n }\n } catch { valid = false; }\n responseAssert(valid, \"Signature does not verify against the frozen wallet identity.\");\n}\n","import { base58, base64 } from \"@scure/base\";\nimport { assert, responseAssert } from \"./errors\";\nimport { deriveConfidentialAccountId, standards } from \"./identity\";\nimport { normalizeChain } from \"./routing\";\nimport { raw, text } from \"./utils\";\nimport { decodeNep413Nonce, verifyWalletSignature } from \"./signatures\";\nimport type { LinkingIdentity, QuoteResponse, SignedData, Token, UnsignedData } from \"./types\";\n\nconst evmChains = new Set([\"eth\", \"arb\", \"avax\", \"op\", \"pol\", \"bsc\", \"base\", \"gnosis\", \"aurora\", \"bera\", \"berachain\", \"monad\", \"hypercore\", \"hyperliquid\", \"linea\", \"scroll\", \"zksync\", \"mantle\", \"celo\", \"sonic\"]);\nexport type AddressValidator = (address: string, token: Token) => boolean;\nexport function validateRecipients(addresses: readonly string[], token: Token, custom?: AddressValidator): string[] {\n assert(addresses.length >= 1 && addresses.length <= 10, \"A batch requires 1 to 10 recipients.\");\n const chain = normalizeChain(token.blockchain);\n const evm = evmChains.has(chain) || /^\\d+$/.test(token.swapChain ?? chain);\n const seen = new Set<string>();\n return addresses.map((value) => {\n const address = text(value, \"Recipient address\");\n let valid = false;\n try {\n if (custom) valid = custom(address, token);\n else if (evm) valid = /^0x[0-9a-f]{40}$/i.test(address);\n else if (chain === \"near\") { deriveConfidentialAccountId(\"near\", address); valid = true; }\n else if (chain === \"sol\") valid = base58.decode(address).length === 32;\n else if (chain === \"tron\") { deriveConfidentialAccountId(\"tron\", address); valid = true; }\n else if (chain === \"aptos\") valid = /^0x[0-9a-f]{1,64}$/i.test(address);\n else if (chain === \"sui\") valid = /^0x[0-9a-f]{64}$/i.test(address);\n else assert(false, \"Supply an address validator for this destination chain (including BTC/Zcash).\");\n } catch (error) { if (!custom && !evm && ![\"near\", \"sol\", \"tron\", \"aptos\", \"sui\"].includes(chain)) throw error; }\n assert(valid, \"Invalid recipient address for the destination chain.\");\n const key = evm || [\"aptos\", \"sui\"].includes(chain) ? address.toLowerCase() : address;\n assert(!seen.has(key), \"Duplicate recipient address.\");\n seen.add(key);\n return address;\n });\n}\nexport function validateToken(token: Token): void {\n text(token.assetId, \"Asset id\"); text(token.blockchain, \"Blockchain\"); text(token.symbol, \"Symbol\");\n assert(Number.isInteger(token.decimals) && token.decimals >= 0 && token.decimals <= 255, \"Invalid token decimals.\");\n}\nexport function validateQuote(q: QuoteResponse, requireDeposit: boolean): void {\n responseAssert(q && typeof q.quote === \"object\" && q.quote, \"Missing quote.\");\n raw(q.quote.amountIn, \"Quoted input\"); raw(q.quote.amountOut, \"Quoted output\");\n if (q.quote.minAmountOut !== undefined) assert(raw(q.quote.minAmountOut) <= raw(q.quote.amountOut), \"Minimum output exceeds quoted output.\");\n if (q.quote.minAmountIn !== undefined) raw(q.quote.minAmountIn);\n if (q.quoteRequest?.appFees !== undefined) {\n raw(q.quoteRequest.amount, \"Quote request amount\");\n responseAssert(Array.isArray(q.quoteRequest.appFees), \"Invalid quote app fees.\");\n for (const fee of q.quoteRequest.appFees) {\n responseAssert(fee && Number.isInteger(fee.fee) && fee.fee >= 0, \"Invalid quote app fee rate.\");\n }\n }\n if (requireDeposit) text(q.quote.depositAddress, \"Deposit address\");\n}\nexport function payloadMessage(data: UnsignedData): Record<string, unknown> {\n let message: unknown;\n try { message = JSON.parse(typeof data.payload === \"string\" ? data.payload : data.payload.message); }\n catch { responseAssert(false, \"Invalid intent JSON.\"); }\n responseAssert(message && typeof message === \"object\" && !Array.isArray(message), \"Invalid intent payload.\");\n return message as Record<string, unknown>;\n}\nexport function validatePayload(data: UnsignedData, identity: LinkingIdentity, signerId: string, contract: string, now: number, purpose: \"authorization\" | \"withdrawal\" = \"authorization\"): Record<string, unknown> {\n responseAssert(data.standard === standards[identity.kind], \"Signing standard does not match wallet.\");\n const msg = payloadMessage(data);\n responseAssert(msg.signer_id === signerId, \"Intent signer does not match the frozen identity.\");\n const deadline = Date.parse(String(msg.deadline ?? \"\"));\n responseAssert(Number.isFinite(deadline) && deadline > now, \"Intent deadline expired or invalid.\");\n responseAssert(Array.isArray(msg.intents), \"Intent list is missing.\");\n if (data.standard === \"nep413\") {\n text(data.payload.recipient, \"NEP-413 recipient\");\n // Generated withdrawals carry their own signed recipient; only login binds the configured verifier.\n if (purpose === \"authorization\") responseAssert(data.payload.recipient === contract, \"Invalid NEP-413 authorization verifier.\");\n decodeNep413Nonce(data.payload.nonce);\n } else if (purpose === \"authorization\") {\n responseAssert(msg.verifying_contract === contract && typeof msg.nonce === \"string\" && base64.decode(msg.nonce).length === 32, \"Invalid verifier or nonce.\");\n }\n return msg;\n}\nexport function validateSigned(unsigned: UnsignedData, signed: SignedData, identity: LinkingIdentity): void {\n responseAssert(signed.standard === unsigned.standard && JSON.stringify(signed.payload) === JSON.stringify(unsigned.payload), \"Wallet changed the payload.\");\n text(signed.signature, \"Signature\");\n if (identity.kind === \"near\" || identity.kind === \"solana\") {\n const expected = identity.publicKey ?? (identity.kind === \"solana\" ? `ed25519:${identity.accountId}` : \"\");\n responseAssert(expected && signed.public_key === expected, \"Signing public key changed.\");\n }\n verifyWalletSignature(signed, identity);\n}\n"],"mappings":"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","names":["base58","sha256","bytes","sha256","sha256","base16","base58","sha256","raw","base58","base16","base58","base64","base58"]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/index.ts","../../src/errors.ts","../../src/identity.ts","../../src/utils.ts","../../src/signatures.ts","../../src/validation.ts"],"sourcesContent":["import { base58 } from \"@scure/base\";\nimport { assert, ConfidentialSwapError, isUserRejectedError, responseAssert, throwIfAborted, userRejectedError } from \"../errors\";\nimport { sameLinking } from \"../identity\";\nimport { decodeEd25519, decodeNep413Nonce, normalizeSecpSignature, toSignatureBytes } from \"../signatures\";\nimport { clone } from \"../utils\";\nimport { validateSigned } from \"../validation\";\nimport type { HttpConfidentialApi } from \"../api\";\nimport type { LinkingIdentity, SignedData, SigningAdapter, UnsignedData } from \"../types\";\n\nexport interface EvmMessageSigner { getAddress(): Promise<string>; signMessage(message: string): Promise<string> }\nexport function createEvmSigningAdapter(getSigner: () => Promise<EvmMessageSigner> | EvmMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"evm\", accountId: await (await getSigner()).getAddress() }),\n sign: async (data) => {\n assert(data.standard === \"erc191\", \"Expected ERC-191 payload.\");\n return { ...data, signature: normalizeSecpSignature(await (await getSigner()).signMessage(data.payload)) };\n },\n });\n}\nexport interface SolanaMessageSigner { publicKey: { toBase58(): string }; signMessage(message: Uint8Array): Promise<unknown> }\nexport function createSolanaSigningAdapter(getWallet: () => SolanaMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"solana\", accountId: getWallet().publicKey.toBase58(), publicKey: `ed25519:${getWallet().publicKey.toBase58()}` }),\n sign: async (data) => {\n assert(data.standard === \"raw_ed25519\", \"Expected raw Ed25519 payload.\");\n const wallet = getWallet();\n const signature = toSignatureBytes(await wallet.signMessage(new TextEncoder().encode(data.payload)));\n return { ...data, public_key: `ed25519:${wallet.publicKey.toBase58()}`, signature: `ed25519:${base58.encode(decodeEd25519(signature, 64))}` };\n },\n });\n}\nexport interface TronMessageSigner { getAddress(): Promise<string> | string; signMessageV2(message: string): Promise<string> }\nexport function createTronSigningAdapter(getWallet: () => TronMessageSigner): SigningAdapter {\n return createCheckedAdapter({\n getIdentity: async () => ({ kind: \"tron\", accountId: await getWallet().getAddress() }),\n sign: async (data) => {\n assert(data.standard === \"tip191\", \"Expected TIP-191 payload.\");\n return { ...data, signature: normalizeSecpSignature(await getWallet().signMessageV2(data.payload)) };\n },\n });\n}\nexport interface NearMessageSigner {\n getIdentity(): Promise<{ accountId: string; publicKey: string }>;\n signMessage(params: { message: string; recipient: string; nonce: Uint8Array; callbackUrl?: string }): Promise<{ accountId: string; publicKey: string; signature: string }>;\n /** Explicit user-approved NEAR transaction: add_public_key on the configured Intents contract. */\n registerPublicKey?(accountId: string, publicKey: string, contract: string, signal?: AbortSignal): Promise<void>;\n}\nexport function createNearSigningAdapter(wallet: NearMessageSigner, api: Pick<HttpConfidentialApi, \"viewIntents\" | \"intentsContract\">): SigningAdapter {\n const getIdentity = async (): Promise<LinkingIdentity> => {\n const identity = await wallet.getIdentity();\n return { kind: \"near\", accountId: identity.accountId, publicKey: `ed25519:${base58.encode(decodeEd25519(identity.publicKey, 32))}` };\n };\n return createCheckedAdapter({\n getIdentity,\n prepare: async (signal) => {\n const identity = await getIdentity();\n if (/^[0-9a-f]{64}$/.test(identity.accountId)) {\n const keyHex = Array.from(decodeEd25519(identity.publicKey!, 32), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n assert(keyHex === identity.accountId, \"Implicit NEAR account does not match its signing public key.\");\n } else {\n const registered = await api.viewIntents<boolean>(\"has_public_key\", { account_id: identity.accountId, public_key: identity.publicKey }, signal);\n responseAssert(typeof registered === \"boolean\", \"Invalid key registration response.\");\n if (!registered) {\n assert(wallet.registerPublicKey, \"NEAR key registration requires an explicit registerPublicKey callback.\");\n throwIfAborted(signal);\n await wallet.registerPublicKey(identity.accountId, identity.publicKey!, api.intentsContract, signal);\n responseAssert(await api.viewIntents<boolean>(\"has_public_key\", { account_id: identity.accountId, public_key: identity.publicKey }, signal) === true, \"NEAR signing key registration is not confirmed.\");\n }\n }\n },\n sign: async (data) => {\n assert(data.standard === \"nep413\", \"Expected NEP-413 payload.\");\n const result = await wallet.signMessage({ ...data.payload, nonce: decodeNep413Nonce(data.payload.nonce) });\n responseAssert(result.accountId === (await getIdentity()).accountId, \"NEAR wallet returned a different account.\");\n return { ...data, public_key: `ed25519:${base58.encode(decodeEd25519(result.publicKey, 32))}`, signature: `ed25519:${base58.encode(decodeEd25519(result.signature, 64))}` };\n },\n });\n}\nexport function createCheckedAdapter(adapter: SigningAdapter): SigningAdapter {\n return {\n getIdentity: () => adapter.getIdentity(),\n prepare: adapter.prepare?.bind(adapter),\n sign: async (data: UnsignedData, signal?: AbortSignal): Promise<SignedData> => {\n throwIfAborted(signal);\n const identity = await adapter.getIdentity();\n let signed: SignedData;\n try {\n signed = await adapter.sign(clone(data), signal);\n } catch (error) {\n if (isUserRejectedError(error)) throw userRejectedError();\n throw error;\n }\n throwIfAborted(signal);\n if (!sameLinking(identity, await adapter.getIdentity())) throw new ConfidentialSwapError(\"WALLET_CHANGED\", \"Wallet identity changed while signing.\");\n validateSigned(data, signed, identity);\n return signed;\n },\n };\n}\n","export type ErrorCode =\n | \"INVALID_INPUT\" | \"INVALID_RESPONSE\" | \"HTTP_ERROR\" | \"UNAUTHORIZED\"\n | \"WALLET_CHANGED\" | \"MISSING_WALLET\" | \"QUOTE_EXPIRED\" | \"BUSY\"\n | \"CONFIRMATION_REQUIRED\" | \"USER_REJECTED\" | \"TIMEOUT\" | \"ABORTED\" | \"TERMINAL_FAILURE\";\n\nexport class ConfidentialSwapError extends Error {\n readonly name = \"ConfidentialSwapError\";\n constructor(\n readonly code: ErrorCode,\n message: string,\n readonly status?: number,\n readonly executionId?: string,\n ) { super(message); }\n}\n\nexport function assert(condition: unknown, message: string): asserts condition {\n if (!condition) throw new ConfidentialSwapError(\"INVALID_INPUT\", message);\n}\n\nexport function responseAssert(condition: unknown, message: string): asserts condition {\n if (!condition) throw new ConfidentialSwapError(\"INVALID_RESPONSE\", message);\n}\n\nexport function isUserRejectedError(error: unknown): boolean {\n if (error instanceof ConfidentialSwapError) return error.code === \"USER_REJECTED\";\n if (!error || typeof error !== \"object\") return false;\n const value = error as { code?: unknown; message?: unknown; error?: unknown };\n const code = String(value.code ?? \"\").toUpperCase();\n const message = String(value.message ?? value.error ?? \"\");\n return (\n code === \"4001\" ||\n code === \"ACTION_REJECTED\" ||\n /user.*(reject|cancel|den(?:y|ied))|(?:reject|cancel|den(?:y|ied)).*user/i.test(message)\n );\n}\n\nexport function userRejectedError(executionId?: string): ConfidentialSwapError {\n return new ConfidentialSwapError(\n \"USER_REJECTED\",\n \"User rejected transaction.\",\n undefined,\n executionId,\n );\n}\n\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted)\n throw new ConfidentialSwapError(\"ABORTED\", \"Trade cancelled.\");\n}\n","import { base16, base58, base58check } from \"@scure/base\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { assert } from \"./errors\";\nimport { text } from \"./utils\";\nimport type { FundingIdentity, LinkingIdentity, LinkingKind, SigningStandard } from \"./types\";\n\nexport const CONFIDENTIAL_LINKING_KINDS = [\"near\", \"evm\", \"solana\", \"tron\"] as const;\nexport const standards: Record<LinkingKind, SigningStandard> = { near: \"nep413\", evm: \"erc191\", solana: \"raw_ed25519\", tron: \"tip191\" };\nexport function deriveConfidentialAccountId(kind: LinkingKind, address: string): string {\n const account = text(address, \"Wallet account\");\n if (kind === \"near\") {\n assert(/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(account) && account.length >= 2 && account.length <= 64, \"Invalid NEAR account.\");\n return account;\n }\n if (kind === \"evm\") { assert(/^0x[0-9a-f]{40}$/i.test(account), \"Invalid EVM address.\"); return account.toLowerCase(); }\n if (kind === \"solana\") {\n const bytes = base58.decode(account);\n assert(bytes.length === 32, \"Invalid Solana public key.\");\n return base16.encode(bytes).toLowerCase();\n }\n assert(kind === \"tron\", \"Unsupported linking wallet kind.\");\n const bytes = base58check(sha256).decode(account);\n assert(bytes.length === 21 && bytes[0] === 0x41, \"Invalid Tron address.\");\n return `0x${base16.encode(bytes.slice(1)).toLowerCase()}`;\n}\nexport function identityKey(identity: LinkingIdentity): string {\n // Session / linking identity is account-scoped — do not include publicKey.\n return `${identity.kind}:${deriveConfidentialAccountId(identity.kind, identity.accountId)}`;\n}\nexport function sameLinking(a: LinkingIdentity, b: LinkingIdentity): boolean { return identityKey(a) === identityKey(b); }\nexport function sameFunding(a: FundingIdentity, b: FundingIdentity): boolean {\n return a.kind === b.kind && a.chain === b.chain && (a.kind === \"evm\" ? a.accountId.toLowerCase() === b.accountId.toLowerCase() : a.accountId === b.accountId);\n}\nexport function resolveLinkingWallet(input: { sourceKind?: string; wallets: readonly LinkingIdentity[]; preferred?: LinkingIdentity }): LinkingIdentity | null {\n const wallets = input.wallets.filter((w) => CONFIDENTIAL_LINKING_KINDS.includes(w.kind) && w.accountId.trim());\n if (input.preferred) return wallets.find((w) => sameLinking(w, input.preferred!)) ?? null;\n return wallets.find((w) => w.kind === input.sourceKind) ?? CONFIDENTIAL_LINKING_KINDS.map((kind) => wallets.find((w) => w.kind === kind)).find(Boolean) ?? null;\n}\n","import { sha256 } from \"@noble/hashes/sha256\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport { assert, throwIfAborted } from \"./errors\";\nimport type { Balance, QuotePlan } from \"./types\";\n\nexport function raw(value: unknown, field = \"Amount\", allowZero = false): bigint {\n assert(typeof value === \"string\" && /^(0|[1-9]\\d*)$/.test(value), `${field} must be a canonical integer string.`);\n const result = BigInt(value);\n assert(allowZero || result > 0n, `${field} must be positive.`);\n return result;\n}\n/** Rescale a raw integer between token decimals (floor when shrinking). */\nexport function rescaleRawAmount(amount: string, fromDecimals: number, toDecimals: number): string {\n assert(Number.isInteger(fromDecimals) && fromDecimals >= 0 && fromDecimals <= 255, \"Invalid source decimals.\");\n assert(Number.isInteger(toDecimals) && toDecimals >= 0 && toDecimals <= 255, \"Invalid target decimals.\");\n const value = raw(amount);\n if (fromDecimals === toDecimals) return value.toString();\n if (toDecimals > fromDecimals) {\n return (value * 10n ** BigInt(toDecimals - fromDecimals)).toString();\n }\n const scaled = value / 10n ** BigInt(fromDecimals - toDecimals);\n assert(scaled > 0n, \"Amount too small after decimal conversion.\");\n return scaled.toString();\n}\nexport function text(value: unknown, field: string): string {\n assert(typeof value === \"string\" && value.trim().length > 0, `${field} is required.`);\n return value.trim();\n}\nexport function clone<T>(value: T): T { return structuredClone(value); }\nfunction canonical(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonical).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value).filter(([, v]) => v !== undefined).sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(\",\")}}`;\n }\n assert(value === null || [\"string\", \"boolean\", \"number\"].includes(typeof value), \"Plan data must be JSON-serializable.\");\n if (typeof value === \"number\") assert(Number.isFinite(value), \"Plan numbers must be finite.\");\n return JSON.stringify(value);\n}\nexport function planDigest(plan: Omit<QuotePlan, \"digest\"> | QuotePlan): string {\n const { digest: _digest, ...body } = plan as QuotePlan;\n return bytesToHex(sha256(new TextEncoder().encode(canonical(body))));\n}\nexport function randomFraction(): number {\n return crypto.getRandomValues(new Uint32Array(1))[0]! / 0xffffffff;\n}\nexport function splitConfidentialRawAmount(totalRaw: string, count: number, values?: readonly number[]): string[] {\n assert(Number.isInteger(count) && count >= 1 && count <= 10, \"A batch requires 1 to 10 recipients.\");\n const total = raw(totalRaw);\n assert(total >= BigInt(count), \"Amount too small for every recipient.\");\n const minimum = total * 80n / (100n * BigInt(count));\n const lowBound = minimum > 0n ? minimum : 1n;\n const denominator = 100n * BigInt(count);\n const maximum = (total * 120n + denominator - 1n) / denominator;\n const highBound = maximum > lowBound ? maximum : lowBound;\n const shares: bigint[] = [];\n let remaining = total;\n for (let i = 0; i < count - 1; i++) {\n const left = BigInt(count - i - 1);\n const low = remaining - highBound * left > lowBound ? remaining - highBound * left : lowBound;\n const high = remaining - lowBound * left < highBound ? remaining - lowBound * left : highBound;\n assert(low <= high, \"Amount cannot be split safely.\");\n const fraction = values?.[i] ?? randomFraction();\n assert(Number.isFinite(fraction) && fraction >= 0 && fraction <= 1, \"Random fractions must be within [0, 1].\");\n const share = low + (high - low) * BigInt(Math.floor(fraction * 1_000_000)) / 1_000_000n;\n shares.push(share);\n remaining -= share;\n }\n assert(remaining >= lowBound && remaining <= highBound, \"Final share outside safe bounds.\");\n return [...shares, remaining].map(String);\n}\n/**\n * Quote / route assetIds (especially `1cs_v1:…`) may differ from the tokenId\n * returned by `/account/balances`. Native assets often settle as\n * `nep141:<symbol>.omft.near` (ZEC/BTC/XRP/SOL/…), not the per-chain 1cs id.\n */\nfunction balanceLookupIds(assetId: string, symbol?: string): string[] {\n const ids = [assetId];\n const near1cs = /^1cs_v1:near:(nep141:.+)$/i.exec(assetId);\n if (near1cs) ids.push(near1cs[1]!);\n const nep = /^(nep141:.+)$/i.exec(assetId);\n if (nep) ids.push(`1cs_v1:near:${nep[1]}`);\n const sym = symbol?.trim().toLowerCase();\n if (sym && /^[a-z0-9]+$/i.test(sym)) {\n const omft = `nep141:${sym}.omft.near`;\n ids.push(omft, `1cs_v1:near:${omft}`);\n }\n return [...new Set(ids)];\n}\n\nexport function availableForAsset(\n balances: readonly Balance[],\n assetId: string,\n symbol?: string,\n): string {\n for (const id of balanceLookupIds(assetId, symbol)) {\n const exact = balances.find((b) => b.tokenId === id);\n const entry =\n exact ??\n balances.find((b) => b.tokenId.startsWith(\"imt:\") && b.tokenId.endsWith(`:${id}`));\n if (entry) return raw(entry.available ?? \"0\", \"Available balance\", true).toString();\n }\n return \"0\";\n}\nexport async function delay(ms: number, signal?: AbortSignal): Promise<void> {\n throwIfAborted(signal);\n await new Promise<void>((resolve, reject) => {\n const finish = () => { signal?.removeEventListener(\"abort\", abort); resolve(); };\n const timer = setTimeout(finish, ms);\n const abort = () => { clearTimeout(timer); signal?.removeEventListener(\"abort\", abort); try { throwIfAborted(signal); } catch (e) { reject(e); } };\n signal?.addEventListener(\"abort\", abort, { once: true });\n });\n}\n","import { ed25519 } from \"@noble/curves/ed25519\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { keccak_256 } from \"@noble/hashes/sha3\";\nimport { concatBytes } from \"@noble/hashes/utils\";\nimport { base16, base58, base64, base64nopad, base64urlnopad } from \"@scure/base\";\nimport { responseAssert } from \"./errors\";\nimport { deriveConfidentialAccountId } from \"./identity\";\nimport type { LinkingIdentity, SignedData, UnsignedData } from \"./types\";\n\nconst encode = (s: string) => new TextEncoder().encode(s);\nfunction u32(value: number): Uint8Array { const b = new Uint8Array(4); new DataView(b.buffer).setUint32(0, value, true); return b; }\nfunction borshString(s: string): Uint8Array { const b = encode(s); return concatBytes(u32(b.length), b); }\nexport function decodeNep413Nonce(value: string): Uint8Array {\n responseAssert(typeof value === \"string\", \"Invalid NEP-413 nonce.\");\n const normalized = value.trim();\n let nonce: Uint8Array;\n try { nonce = base64.decode(normalized); }\n catch {\n const unpadded = normalized.replace(/=+$/, \"\");\n try { nonce = base64urlnopad.decode(unpadded); }\n catch {\n try { nonce = base64nopad.decode(unpadded); }\n catch { responseAssert(false, \"Invalid NEP-413 nonce encoding.\"); }\n }\n }\n responseAssert(nonce.length === 32, \"The NEP-413 nonce must be 32 bytes.\");\n return nonce;\n}\nexport function nep413Hash(payload: Extract<UnsignedData, { standard: \"nep413\" }>[\"payload\"]): Uint8Array {\n const nonce = decodeNep413Nonce(payload.nonce);\n return sha256(concatBytes(u32(2 ** 31 + 413), borshString(payload.message), nonce, borshString(payload.recipient), payload.callbackUrl === undefined ? new Uint8Array([0]) : concatBytes(new Uint8Array([1]), borshString(payload.callbackUrl))));\n}\nexport function personalMessageHash(message: string, kind: \"evm\" | \"tron\"): Uint8Array {\n const bytes = encode(message);\n return keccak_256(concatBytes(encode(`\\x19${kind === \"evm\" ? \"Ethereum\" : \"TRON\"} Signed Message:\\n${bytes.length}`), bytes));\n}\nexport function toSignatureBytes(value: unknown): Uint8Array {\n if (value instanceof Uint8Array) return value;\n if (value && typeof value === \"object\" && \"signature\" in value) {\n return toSignatureBytes((value as { signature: unknown }).signature);\n }\n if (ArrayBuffer.isView(value)) {\n return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n }\n throw new Error(\"The wallet returned an invalid Ed25519 signature.\");\n}\nexport function decodeEd25519(value: string | Uint8Array, length: number): Uint8Array {\n if (value instanceof Uint8Array) { responseAssert(value.length === length, \"Invalid Ed25519 byte length.\"); return new Uint8Array(value); }\n const raw = value.replace(/^ed25519:/, \"\");\n for (const decoder of [() => base58.decode(raw), () => base64.decode(raw), () => base16.decode(raw.replace(/^0x/, \"\").toUpperCase())]) {\n try { const b = decoder(); if (b.length === length) return b; } catch { /* Wallets use base58, base64, or hex. */ }\n }\n responseAssert(false, \"Invalid Ed25519 encoding.\");\n}\nexport function normalizeSecpSignature(value: string): string {\n const hex = value.replace(/^0x/i, \"\");\n responseAssert(/^[0-9a-f]{130}$/i.test(hex), \"Expected a 65-byte signature.\");\n const bytes = base16.decode(hex.toUpperCase());\n const v = bytes[64]!;\n responseAssert([0, 1, 27, 28].includes(v), \"Invalid recovery id.\");\n bytes[64] = v < 27 ? v + 27 : v;\n return `0x${base16.encode(bytes).toLowerCase()}`;\n}\nexport function verifyWalletSignature(data: SignedData, identity: LinkingIdentity): void {\n let valid = false;\n try {\n if (identity.kind === \"near\" && data.standard === \"nep413\") {\n const key = decodeEd25519(identity.publicKey ?? \"\", 32);\n valid = ed25519.verify(decodeEd25519(data.signature, 64), nep413Hash(data.payload), key);\n if (/^[0-9a-f]{64}$/.test(identity.accountId)) valid = valid && base16.encode(key).toLowerCase() === identity.accountId;\n } else if (identity.kind === \"solana\" && data.standard === \"raw_ed25519\") {\n valid = ed25519.verify(decodeEd25519(data.signature, 64), encode(data.payload), base58.decode(identity.accountId));\n } else if ((identity.kind === \"evm\" || identity.kind === \"tron\") && typeof data.payload === \"string\") {\n const bytes = base16.decode(normalizeSecpSignature(data.signature).slice(2).toUpperCase());\n const sig = secp256k1.Signature.fromCompact(bytes.slice(0, 64)).addRecoveryBit(bytes[64]! - 27);\n const publicKey = sig.recoverPublicKey(personalMessageHash(data.payload, identity.kind)).toRawBytes(false);\n const address = `0x${base16.encode(keccak_256(publicKey.slice(1)).slice(-20)).toLowerCase()}`;\n valid = address === deriveConfidentialAccountId(identity.kind, identity.accountId);\n }\n } catch { valid = false; }\n responseAssert(valid, \"Signature does not verify against the frozen wallet identity.\");\n}\n","import { base58, base64 } from \"@scure/base\";\nimport { assert, responseAssert } from \"./errors\";\nimport { deriveConfidentialAccountId, standards } from \"./identity\";\nimport { normalizeChain } from \"./routing\";\nimport { raw, text } from \"./utils\";\nimport { decodeNep413Nonce, verifyWalletSignature } from \"./signatures\";\nimport type { LinkingIdentity, QuoteResponse, SignedData, Token, UnsignedData } from \"./types\";\n\nconst evmChains = new Set([\"eth\", \"arb\", \"avax\", \"op\", \"pol\", \"bsc\", \"base\", \"gnosis\", \"aurora\", \"bera\", \"berachain\", \"monad\", \"hypercore\", \"hyperliquid\", \"linea\", \"scroll\", \"zksync\", \"mantle\", \"celo\", \"sonic\"]);\nexport type AddressValidator = (address: string, token: Token) => boolean;\nexport function validateRecipients(addresses: readonly string[], token: Token, custom?: AddressValidator): string[] {\n assert(addresses.length >= 1 && addresses.length <= 10, \"A batch requires 1 to 10 recipients.\");\n const chain = normalizeChain(token.blockchain);\n const evm = evmChains.has(chain) || /^\\d+$/.test(token.swapChain ?? chain);\n const seen = new Set<string>();\n return addresses.map((value) => {\n const address = text(value, \"Recipient address\");\n let valid = false;\n try {\n if (custom) valid = custom(address, token);\n else if (evm) valid = /^0x[0-9a-f]{40}$/i.test(address);\n else if (chain === \"near\") { deriveConfidentialAccountId(\"near\", address); valid = true; }\n else if (chain === \"sol\") valid = base58.decode(address).length === 32;\n else if (chain === \"tron\") { deriveConfidentialAccountId(\"tron\", address); valid = true; }\n else if (chain === \"aptos\") valid = /^0x[0-9a-f]{1,64}$/i.test(address);\n else if (chain === \"sui\") valid = /^0x[0-9a-f]{64}$/i.test(address);\n else assert(false, \"Supply an address validator for this destination chain (including BTC/Zcash).\");\n } catch (error) { if (!custom && !evm && ![\"near\", \"sol\", \"tron\", \"aptos\", \"sui\"].includes(chain)) throw error; }\n assert(valid, \"Invalid recipient address for the destination chain.\");\n const key = evm || [\"aptos\", \"sui\"].includes(chain) ? address.toLowerCase() : address;\n assert(!seen.has(key), \"Duplicate recipient address.\");\n seen.add(key);\n return address;\n });\n}\nexport function validateToken(token: Token): void {\n text(token.assetId, \"Asset id\"); text(token.blockchain, \"Blockchain\"); text(token.symbol, \"Symbol\");\n assert(Number.isInteger(token.decimals) && token.decimals >= 0 && token.decimals <= 255, \"Invalid token decimals.\");\n}\nexport function validateQuote(q: QuoteResponse, requireDeposit: boolean): void {\n responseAssert(q && typeof q.quote === \"object\" && q.quote, \"Missing quote.\");\n raw(q.quote.amountIn, \"Quoted input\"); raw(q.quote.amountOut, \"Quoted output\");\n if (q.quote.minAmountOut !== undefined) assert(raw(q.quote.minAmountOut) <= raw(q.quote.amountOut), \"Minimum output exceeds quoted output.\");\n if (q.quote.minAmountIn !== undefined) raw(q.quote.minAmountIn);\n if (q.quoteRequest?.appFees !== undefined) {\n raw(q.quoteRequest.amount, \"Quote request amount\");\n responseAssert(Array.isArray(q.quoteRequest.appFees), \"Invalid quote app fees.\");\n for (const fee of q.quoteRequest.appFees) {\n responseAssert(fee && Number.isInteger(fee.fee) && fee.fee >= 0, \"Invalid quote app fee rate.\");\n }\n }\n if (requireDeposit) text(q.quote.depositAddress, \"Deposit address\");\n}\nexport function payloadMessage(data: UnsignedData): Record<string, unknown> {\n let message: unknown;\n try { message = JSON.parse(typeof data.payload === \"string\" ? data.payload : data.payload.message); }\n catch { responseAssert(false, \"Invalid intent JSON.\"); }\n responseAssert(message && typeof message === \"object\" && !Array.isArray(message), \"Invalid intent payload.\");\n return message as Record<string, unknown>;\n}\nexport function validatePayload(data: UnsignedData, identity: LinkingIdentity, signerId: string, contract: string, now: number, purpose: \"authorization\" | \"withdrawal\" = \"authorization\"): Record<string, unknown> {\n responseAssert(data.standard === standards[identity.kind], \"Signing standard does not match wallet.\");\n const msg = payloadMessage(data);\n responseAssert(msg.signer_id === signerId, \"Intent signer does not match the frozen identity.\");\n const deadline = Date.parse(String(msg.deadline ?? \"\"));\n responseAssert(Number.isFinite(deadline) && deadline > now, \"Intent deadline expired or invalid.\");\n responseAssert(Array.isArray(msg.intents), \"Intent list is missing.\");\n if (data.standard === \"nep413\") {\n text(data.payload.recipient, \"NEP-413 recipient\");\n // Generated withdrawals carry their own signed recipient; only login binds the configured verifier.\n if (purpose === \"authorization\") responseAssert(data.payload.recipient === contract, \"Invalid NEP-413 authorization verifier.\");\n decodeNep413Nonce(data.payload.nonce);\n } else if (purpose === \"authorization\") {\n responseAssert(msg.verifying_contract === contract && typeof msg.nonce === \"string\" && base64.decode(msg.nonce).length === 32, \"Invalid verifier or nonce.\");\n }\n return msg;\n}\nexport function validateSigned(unsigned: UnsignedData, signed: SignedData, identity: LinkingIdentity): void {\n responseAssert(signed.standard === unsigned.standard && JSON.stringify(signed.payload) === JSON.stringify(unsigned.payload), \"Wallet changed the payload.\");\n text(signed.signature, \"Signature\");\n if (identity.kind === \"near\" || identity.kind === \"solana\") {\n const expected = identity.publicKey ?? (identity.kind === \"solana\" ? `ed25519:${identity.accountId}` : \"\");\n responseAssert(expected && signed.public_key === expected, \"Signing public key changed.\");\n }\n verifyWalletSignature(signed, identity);\n}\n"],"mappings":"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","names":["base58","sha256","bytes","sha256","sha256","base16","base58","sha256","raw","base58","base16","base58","base64","base58"]}
|
|
@@ -66,6 +66,10 @@ interface QuoteResponse {
|
|
|
66
66
|
timeWhenInactive?: string;
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
+
interface AppFee {
|
|
70
|
+
recipient: string;
|
|
71
|
+
fee: number;
|
|
72
|
+
}
|
|
69
73
|
interface AppFeeEstimate {
|
|
70
74
|
/** Effective total fee in basis points. */
|
|
71
75
|
basisPoints: number;
|
|
@@ -145,6 +149,8 @@ interface QuoteParams {
|
|
|
145
149
|
recipientType: "CONFIDENTIAL_INTENTS" | "DESTINATION_CHAIN";
|
|
146
150
|
swapType?: "EXACT_INPUT" | "FLEX_INPUT";
|
|
147
151
|
dry: boolean;
|
|
152
|
+
/** Per-plan app fees. An empty list explicitly disables app fees. */
|
|
153
|
+
appFees?: AppFee[];
|
|
148
154
|
}
|
|
149
155
|
interface ConfidentialApi {
|
|
150
156
|
environment: string;
|
|
@@ -240,6 +246,8 @@ interface QuotePlan {
|
|
|
240
246
|
swapQuote?: FundingSwapQuote;
|
|
241
247
|
swapRequest?: SwapFundingRequest;
|
|
242
248
|
fundingAmount: string;
|
|
249
|
+
/** App fees frozen when the plan was quoted and reused for formal withdrawals. */
|
|
250
|
+
appFees?: AppFee[];
|
|
243
251
|
/** Frozen split rule reused when credited funds are re-quoted or execution is restored. */
|
|
244
252
|
randomValues: number[];
|
|
245
253
|
previews: WithdrawPreview[];
|
|
@@ -292,10 +300,6 @@ interface ExecuteOptions {
|
|
|
292
300
|
onSignatureProgress?: (progress: SignatureProgress) => void;
|
|
293
301
|
}
|
|
294
302
|
|
|
295
|
-
interface AppFee {
|
|
296
|
-
recipient: string;
|
|
297
|
-
fee: number;
|
|
298
|
-
}
|
|
299
303
|
interface HttpApiConfig {
|
|
300
304
|
/** Full /v0 URL; balances are sent here, never to the proxy. */
|
|
301
305
|
directUrl: string;
|
|
@@ -66,6 +66,10 @@ interface QuoteResponse {
|
|
|
66
66
|
timeWhenInactive?: string;
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
+
interface AppFee {
|
|
70
|
+
recipient: string;
|
|
71
|
+
fee: number;
|
|
72
|
+
}
|
|
69
73
|
interface AppFeeEstimate {
|
|
70
74
|
/** Effective total fee in basis points. */
|
|
71
75
|
basisPoints: number;
|
|
@@ -145,6 +149,8 @@ interface QuoteParams {
|
|
|
145
149
|
recipientType: "CONFIDENTIAL_INTENTS" | "DESTINATION_CHAIN";
|
|
146
150
|
swapType?: "EXACT_INPUT" | "FLEX_INPUT";
|
|
147
151
|
dry: boolean;
|
|
152
|
+
/** Per-plan app fees. An empty list explicitly disables app fees. */
|
|
153
|
+
appFees?: AppFee[];
|
|
148
154
|
}
|
|
149
155
|
interface ConfidentialApi {
|
|
150
156
|
environment: string;
|
|
@@ -240,6 +246,8 @@ interface QuotePlan {
|
|
|
240
246
|
swapQuote?: FundingSwapQuote;
|
|
241
247
|
swapRequest?: SwapFundingRequest;
|
|
242
248
|
fundingAmount: string;
|
|
249
|
+
/** App fees frozen when the plan was quoted and reused for formal withdrawals. */
|
|
250
|
+
appFees?: AppFee[];
|
|
243
251
|
/** Frozen split rule reused when credited funds are re-quoted or execution is restored. */
|
|
244
252
|
randomValues: number[];
|
|
245
253
|
previews: WithdrawPreview[];
|
|
@@ -292,10 +300,6 @@ interface ExecuteOptions {
|
|
|
292
300
|
onSignatureProgress?: (progress: SignatureProgress) => void;
|
|
293
301
|
}
|
|
294
302
|
|
|
295
|
-
interface AppFee {
|
|
296
|
-
recipient: string;
|
|
297
|
-
fee: number;
|
|
298
|
-
}
|
|
299
303
|
interface HttpApiConfig {
|
|
300
304
|
/** Full /v0 URL; balances are sent here, never to the proxy. */
|
|
301
305
|
directUrl: string;
|