@rhea-finance/confidential-swap 0.2.0 → 0.2.2

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 CHANGED
@@ -69,7 +69,7 @@ const result = await client.execute(plan, {
69
69
 
70
70
  The example assumes the application's existing wallet, asset, funding adapter and progress objects. The SDK does not connect wallets or manage UI. Connect and choose those objects in the application; do not pass private keys into the SDK. Quote plans have a default maximum age of 10 minutes, bounded by any earlier service-provided deadline; configure `maxQuoteAgeMs` when a different review window is required.
71
71
 
72
- `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 aggregation SDK's token address/native sentinel. No arbitrary EVM chain is guessed from `evm` kind alone.
72
+ `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
73
 
74
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. Configured `appFees` must contain at least one valid NEAR recipient and an integer fee from 1 to 10000.
75
75
 
@@ -77,7 +77,7 @@ The example assumes the application's existing wallet, asset, funding adapter an
77
77
 
78
78
  `FundingAdapter.getIdentity` returns `{ kind, accountId, chain }`. Its `validate` checks capability and the source chain without sending funds. Its `transfer` receives the real source token, smallest-unit amount, deposit address, optional memo and stable execution ID. Return a source transaction hash. Implement chain-specific transfer logic with the application's existing wallet SDK; an adapter must check identity again immediately before signing/broadcasting.
79
79
 
80
- For non-DIRECT funding, the funding adapter still validates the source wallet and chain, while `FundingSwapAdapter` performs quote/build/execute/report/status. Its quote data must be public and JSON-serializable. Its executor must use exactly the supplied source identity, chain and deposit recipient.
80
+ For non-DIRECT funding, the funding adapter still validates the source wallet and chain, while `FundingSwapAdapter` performs quote/build/execute/report/status. Its quote data must be public and JSON-serializable. Its executor must use exactly the supplied source identity, chain and deposit recipient. The application owns the swap/bridge implementation; this package does not depend on a specific aggregation SDK.
81
81
 
82
82
  | Path | Waiting sequence |
83
83
  | --- | --- |
@@ -85,38 +85,12 @@ For non-DIRECT funding, the funding adapter still validates the source wallet an
85
85
  | SAME_CHAIN_SWAP | Swap to FLEX_INPUT, report, wait only for confidential deposit and balance |
86
86
  | CROSS_CHAIN_SWAP | Swap to FLEX_INPUT, report, wait for swap record success, then deposit and balance |
87
87
 
88
- All paths authorize the linking identity before sending source funds. Routing prefers registered direct assets, then same-chain USDC/USDT/USDT0, then a NEAR asset or another registry asset. Native NEAR maps to wNEAR. FLEX minimum input is checked against the final aggregation minimum output, with bounded requotes.
88
+ All paths authorize the linking identity before sending source funds. Routing prefers registered direct assets, then same-chain USDC/USDT/USDT0, then a NEAR asset or another registry asset. Native NEAR maps to wNEAR. FLEX minimum input is checked against the final funding-swap minimum output, with bounded requotes.
89
89
 
90
90
  `fundingSource: 'balance'` spends only the requested amount from an existing registered confidential balance, without a source transaction. These balance withdrawals are checkpointed in process memory only and are never written to the configured `executionStore`, so they are not recoverable after reload. `mode: 'TRANSFER'` requires the same funding and destination asset; otherwise mode defaults to `SWAP`. A batch has 1-10 unique recipients on one destination chain and asset. Randomized positive shares preserve the exact raw total with roughly 80-120 percent equal-share bounds, allowing integer rounding. The frozen random fractions are stored in the plan and reused after re-quoting or recovery against the actual credited amount.
91
91
 
92
92
  Supply `addressValidator` for BTC/Zcash or unsupported destination chains, using an established chain library. Known EVM, NEAR, Solana, Tron, Aptos and Sui address formats are checked by default. Case-sensitive addresses are never lowercased or silently truncated.
93
93
 
94
- ## Aggregation bridge
95
-
96
- The optional `@rhea-finance/confidential-swap/aggregation` entry exposes `createAggregationFundingAdapter` and `HttpFundingReporter`. Install the optional peer `@rhea-finance/cross-chain-aggregation-dex@^2.0.6` when using its bridge/types.
97
-
98
- ```ts
99
- import { SwapClient } from '@rhea-finance/cross-chain-aggregation-dex';
100
- import {
101
- createAggregationFundingAdapter,
102
- HttpFundingReporter,
103
- } from '@rhea-finance/confidential-swap/aggregation';
104
-
105
- const swapClient = new SwapClient({
106
- baseUrl: indexerUrl,
107
- apiKey: configuredApiToken,
108
- reportMode: 'manual', // no duplicate automatic reports
109
- executors: sourceChainExecutors,
110
- });
111
- const fundingSwapAdapter = createAggregationFundingAdapter({
112
- client: swapClient,
113
- reporter: new HttpFundingReporter({ baseUrl: indexerUrl, bearerToken: configuredApiToken }),
114
- mapAsset: token => ({ chain: mapSwapChain(token), address: mapSwapAddress(token) }),
115
- });
116
- ```
117
-
118
- Unlike the aggregation SDK's ordinary order query, advanced funding status uses `/api/swap/order-status?recordId=...`. The reporter sends `confidentiality: 'advanced'`. API credentials must be provided by the application, not copied from the reference repository.
119
-
120
94
  ## Intent protocol boundary
121
95
 
122
96
  Withdrawal payloads are signed as returned by `/generate-intent`, matching the reference implementation. The SDK does not enforce a local transfer format or compare intent tokens and amounts against the quote. Signers, standards, nonces, deadlines and signatures are independently validated. NEP-413 withdrawal recipients are signed exactly as returned; authorization still requires the configured verifier. NEP-413 nonces accept Base64 or Base64URL and must decode to 32 bytes.
@@ -143,7 +117,7 @@ This is confidential-account orchestration, not a guarantee of end-to-end anonym
143
117
 
144
118
  ## Verification status
145
119
 
146
- Local tests exercise actual cryptographic signatures, the three-path/four-linking-kind matrix, source wallet separation, randomized integer bounds, HTTP fixtures, session recovery, ambiguous writes, partial statuses, price re-review and the published aggregation SDK client with mocked transport/execution. Packed ESM/CJS and consumer declarations are checked without React, wallet SDKs or the aggregation peer installed.
120
+ Local tests exercise actual cryptographic signatures, the three-path/four-linking-kind matrix, source wallet separation, randomized integer bounds, HTTP fixtures, session recovery, ambiguous writes, partial statuses and price re-review. Packed ESM/CJS and consumer declarations are checked without React or wallet SDKs installed.
147
121
 
148
122
  Signature domain implementations follow the primary [NEAR NEP-413 specification](https://github.com/near/NEPs/blob/master/neps/nep-0413.md) and [TronWeb message implementation](https://github.com/tronprotocol/tronweb/blob/master/src/utils/message.ts).
149
123
 
@@ -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 (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 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,5 +1,4 @@
1
- import { H as HttpConfidentialApi } from '../api-BCWokYGu.cjs';
2
- import { S as SigningAdapter } from '../types-BoxIObtE.cjs';
1
+ import { S as SigningAdapter, H as HttpConfidentialApi } from '../api-QSK8yzxM.cjs';
3
2
 
4
3
  interface EvmMessageSigner {
5
4
  getAddress(): Promise<string>;
@@ -1,5 +1,4 @@
1
- import { H as HttpConfidentialApi } from '../api-3laTMxjX.js';
2
- import { S as SigningAdapter } from '../types-BoxIObtE.js';
1
+ import { S as SigningAdapter, H as HttpConfidentialApi } from '../api-QSK8yzxM.js';
3
2
 
4
3
  interface EvmMessageSigner {
5
4
  getAddress(): Promise<string>;
@@ -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 (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 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"]}
@@ -47,6 +47,14 @@ interface SigningAdapter {
47
47
  }
48
48
  interface QuoteResponse {
49
49
  correlationId?: string;
50
+ quoteRequest?: {
51
+ amount?: string;
52
+ appFees?: Array<{
53
+ fee: number;
54
+ recipient?: string;
55
+ limitOrderId?: string | null;
56
+ }>;
57
+ };
50
58
  quote: {
51
59
  amountIn: string;
52
60
  amountOut: string;
@@ -59,7 +67,7 @@ interface QuoteResponse {
59
67
  };
60
68
  }
61
69
  interface AppFeeEstimate {
62
- /** Total configured fee in basis points. */
70
+ /** Effective total fee in basis points. */
63
71
  basisPoints: number;
64
72
  /** Fee amount in raw units of assetId. */
65
73
  amount: string;
@@ -284,4 +292,55 @@ interface ExecuteOptions {
284
292
  onSignatureProgress?: (progress: SignatureProgress) => void;
285
293
  }
286
294
 
287
- export type { AppFeeEstimate as A, Balance as B, ConfidentialApi as C, ExecutionSnapshot as E, FundingSwapAdapter as F, LinkingKind as L, OrderPage as O, PollingOptions as P, QuotePlan as Q, SigningAdapter as S, Token as T, UnsignedData as U, WalletKind as W, LinkingIdentity as a, FundingIdentity as b, SigningStandard as c, FundingRoute as d, FundingAdapter as e, SessionStore as f, ExecutionStore as g, QuoteResponse as h, QuoteRequest as i, Login as j, ExecuteOptions as k, ExecutionStage as l, FundingReceipt as m, FundingRouteKind as n, FundingSwapQuote as o, PrivateOrder as p, PrivateWithdrawRequest as q, PrivateWithdrawRow as r, QuoteParams as s, Session as t, SignatureProgress as u, SignedData as v, StatusResponse as w, SwapFundingRequest as x, WithdrawPreview as y, WithdrawProgress as z };
295
+ interface AppFee {
296
+ recipient: string;
297
+ fee: number;
298
+ }
299
+ interface HttpApiConfig {
300
+ /** Full /v0 URL; balances are sent here, never to the proxy. */
301
+ directUrl: string;
302
+ /** Full /proxy/1click/v0 URL. */
303
+ proxyUrl: string;
304
+ /** Indexer root URL. */
305
+ indexerUrl: string;
306
+ nearRpcUrl: string;
307
+ intentsContract?: string;
308
+ fetch?: typeof fetch;
309
+ now?: () => number;
310
+ randomBytes?: (length: number) => Uint8Array;
311
+ /** Reference implementation submits signed payloads without Bearer. */
312
+ withdrawAuthorization?: "signed-payload" | "bearer";
313
+ referral?: string;
314
+ appFees?: AppFee[];
315
+ requestTimeoutMs?: number;
316
+ }
317
+ declare class HttpConfidentialApi implements ConfidentialApi {
318
+ private readonly config;
319
+ readonly environment: string;
320
+ readonly intentsContract: string;
321
+ private readonly fetcher;
322
+ private readonly now;
323
+ private readonly direct;
324
+ private readonly proxy;
325
+ private readonly indexer;
326
+ private readonly appFees;
327
+ constructor(config: HttpApiConfig);
328
+ private request;
329
+ viewIntents<T>(method: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<T>;
330
+ createAuthPayload(signerId: string, standard: SigningStandard, signal?: AbortSignal): Promise<UnsignedData>;
331
+ private session;
332
+ authenticate(data: SignedData, signal?: AbortSignal): Promise<Session>;
333
+ refresh(session: Session, signal?: AbortSignal): Promise<Session>;
334
+ getTokens(signal?: AbortSignal): Promise<Token[]>;
335
+ getBalances(token: string, signal?: AbortSignal): Promise<Balance[]>;
336
+ estimateWithdrawalAppFee(amount: string, assetId: string): AppFeeEstimate;
337
+ quote(params: QuoteParams, signal?: AbortSignal): Promise<QuoteResponse>;
338
+ generateIntent(signerId: string, address: string, standard: SigningStandard, signal?: AbortSignal): Promise<UnsignedData>;
339
+ submitIntent(signedData: SignedData, signal?: AbortSignal): Promise<string>;
340
+ private privateRequest;
341
+ submitWithdraws(request: PrivateWithdrawRequest, token: string, signal?: AbortSignal): Promise<string>;
342
+ getStatus(address: string, memo?: string, signal?: AbortSignal): Promise<StatusResponse>;
343
+ getOrders(token: string, signerId: string, limit: number, offset: number, signal?: AbortSignal): Promise<OrderPage>;
344
+ }
345
+
346
+ export { type AppFee as A, type Balance as B, type ConfidentialApi as C, type WithdrawPreview as D, type ExecutionStore as E, type FundingIdentity as F, type WithdrawProgress as G, HttpConfidentialApi as H, type LinkingKind as L, type OrderPage as O, type PollingOptions as P, type QuotePlan as Q, type SigningAdapter as S, type Token as T, type UnsignedData as U, type WalletKind as W, type LinkingIdentity as a, type SigningStandard as b, type FundingRoute as c, type FundingAdapter as d, type FundingSwapAdapter as e, type SessionStore as f, type QuoteResponse as g, type QuoteRequest as h, type Login as i, type ExecutionSnapshot as j, type ExecuteOptions as k, type AppFeeEstimate as l, type ExecutionStage as m, type FundingReceipt as n, type FundingRouteKind as o, type FundingSwapQuote as p, type HttpApiConfig as q, type PrivateOrder as r, type PrivateWithdrawRequest as s, type PrivateWithdrawRow as t, type QuoteParams as u, type Session as v, type SignatureProgress as w, type SignedData as x, type StatusResponse as y, type SwapFundingRequest as z };
@@ -47,6 +47,14 @@ interface SigningAdapter {
47
47
  }
48
48
  interface QuoteResponse {
49
49
  correlationId?: string;
50
+ quoteRequest?: {
51
+ amount?: string;
52
+ appFees?: Array<{
53
+ fee: number;
54
+ recipient?: string;
55
+ limitOrderId?: string | null;
56
+ }>;
57
+ };
50
58
  quote: {
51
59
  amountIn: string;
52
60
  amountOut: string;
@@ -59,7 +67,7 @@ interface QuoteResponse {
59
67
  };
60
68
  }
61
69
  interface AppFeeEstimate {
62
- /** Total configured fee in basis points. */
70
+ /** Effective total fee in basis points. */
63
71
  basisPoints: number;
64
72
  /** Fee amount in raw units of assetId. */
65
73
  amount: string;
@@ -284,4 +292,55 @@ interface ExecuteOptions {
284
292
  onSignatureProgress?: (progress: SignatureProgress) => void;
285
293
  }
286
294
 
287
- export type { AppFeeEstimate as A, Balance as B, ConfidentialApi as C, ExecutionSnapshot as E, FundingSwapAdapter as F, LinkingKind as L, OrderPage as O, PollingOptions as P, QuotePlan as Q, SigningAdapter as S, Token as T, UnsignedData as U, WalletKind as W, LinkingIdentity as a, FundingIdentity as b, SigningStandard as c, FundingRoute as d, FundingAdapter as e, SessionStore as f, ExecutionStore as g, QuoteResponse as h, QuoteRequest as i, Login as j, ExecuteOptions as k, ExecutionStage as l, FundingReceipt as m, FundingRouteKind as n, FundingSwapQuote as o, PrivateOrder as p, PrivateWithdrawRequest as q, PrivateWithdrawRow as r, QuoteParams as s, Session as t, SignatureProgress as u, SignedData as v, StatusResponse as w, SwapFundingRequest as x, WithdrawPreview as y, WithdrawProgress as z };
295
+ interface AppFee {
296
+ recipient: string;
297
+ fee: number;
298
+ }
299
+ interface HttpApiConfig {
300
+ /** Full /v0 URL; balances are sent here, never to the proxy. */
301
+ directUrl: string;
302
+ /** Full /proxy/1click/v0 URL. */
303
+ proxyUrl: string;
304
+ /** Indexer root URL. */
305
+ indexerUrl: string;
306
+ nearRpcUrl: string;
307
+ intentsContract?: string;
308
+ fetch?: typeof fetch;
309
+ now?: () => number;
310
+ randomBytes?: (length: number) => Uint8Array;
311
+ /** Reference implementation submits signed payloads without Bearer. */
312
+ withdrawAuthorization?: "signed-payload" | "bearer";
313
+ referral?: string;
314
+ appFees?: AppFee[];
315
+ requestTimeoutMs?: number;
316
+ }
317
+ declare class HttpConfidentialApi implements ConfidentialApi {
318
+ private readonly config;
319
+ readonly environment: string;
320
+ readonly intentsContract: string;
321
+ private readonly fetcher;
322
+ private readonly now;
323
+ private readonly direct;
324
+ private readonly proxy;
325
+ private readonly indexer;
326
+ private readonly appFees;
327
+ constructor(config: HttpApiConfig);
328
+ private request;
329
+ viewIntents<T>(method: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<T>;
330
+ createAuthPayload(signerId: string, standard: SigningStandard, signal?: AbortSignal): Promise<UnsignedData>;
331
+ private session;
332
+ authenticate(data: SignedData, signal?: AbortSignal): Promise<Session>;
333
+ refresh(session: Session, signal?: AbortSignal): Promise<Session>;
334
+ getTokens(signal?: AbortSignal): Promise<Token[]>;
335
+ getBalances(token: string, signal?: AbortSignal): Promise<Balance[]>;
336
+ estimateWithdrawalAppFee(amount: string, assetId: string): AppFeeEstimate;
337
+ quote(params: QuoteParams, signal?: AbortSignal): Promise<QuoteResponse>;
338
+ generateIntent(signerId: string, address: string, standard: SigningStandard, signal?: AbortSignal): Promise<UnsignedData>;
339
+ submitIntent(signedData: SignedData, signal?: AbortSignal): Promise<string>;
340
+ private privateRequest;
341
+ submitWithdraws(request: PrivateWithdrawRequest, token: string, signal?: AbortSignal): Promise<string>;
342
+ getStatus(address: string, memo?: string, signal?: AbortSignal): Promise<StatusResponse>;
343
+ getOrders(token: string, signerId: string, limit: number, offset: number, signal?: AbortSignal): Promise<OrderPage>;
344
+ }
345
+
346
+ export { type AppFee as A, type Balance as B, type ConfidentialApi as C, type WithdrawPreview as D, type ExecutionStore as E, type FundingIdentity as F, type WithdrawProgress as G, HttpConfidentialApi as H, type LinkingKind as L, type OrderPage as O, type PollingOptions as P, type QuotePlan as Q, type SigningAdapter as S, type Token as T, type UnsignedData as U, type WalletKind as W, type LinkingIdentity as a, type SigningStandard as b, type FundingRoute as c, type FundingAdapter as d, type FundingSwapAdapter as e, type SessionStore as f, type QuoteResponse as g, type QuoteRequest as h, type Login as i, type ExecutionSnapshot as j, type ExecuteOptions as k, type AppFeeEstimate as l, type ExecutionStage as m, type FundingReceipt as n, type FundingRouteKind as o, type FundingSwapQuote as p, type HttpApiConfig as q, type PrivateOrder as r, type PrivateWithdrawRequest as s, type PrivateWithdrawRow as t, type QuoteParams as u, type Session as v, type SignatureProgress as w, type SignedData as x, type StatusResponse as y, type SwapFundingRequest as z };