@hwlt/era-connect 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"verify.js","names":[],"sources":["../src/verify/psbt-reader.ts","../src/verify/result.ts","../src/verify/btc.ts","../src/verify/evm.ts","../src/verify/solana.ts","../src/verify/tron.ts"],"sourcesContent":["import { EraSdkError } from '../core/errors';\n\n/**\n * Minimal PSBT v0 (BIP-174) reader — just enough structure for the\n * verification guard: the global unsigned transaction (verbatim slice, never\n * re-serialized), the PSBT version, and per-input key/value maps.\n *\n * Hardened: compact-size lengths bounds-checked before slicing, duplicate\n * keys within one map refused (a hostile PSBT carrying two final scriptSigs\n * for one input must not survive parsing).\n */\n\nexport interface PsbtKeyValue {\n readonly keyType: number;\n readonly keyData: Uint8Array;\n readonly value: Uint8Array;\n}\n\nexport interface ParsedPsbt {\n /** The global UNSIGNED_TX value, verbatim. */\n readonly unsignedTx: Uint8Array;\n /** Global PSBT_GLOBAL_VERSION (0xFB) if present; v0 files normally omit it. */\n readonly version: number;\n readonly inputs: readonly (readonly PsbtKeyValue[])[];\n readonly outputs: readonly (readonly PsbtKeyValue[])[];\n}\n\nconst MAGIC = [0x70, 0x73, 0x62, 0x74, 0xff]; // \"psbt\\xff\"\n\nexport const PsbtInputType = {\n partialSig: 0x02,\n finalScriptSig: 0x07,\n finalScriptWitness: 0x08,\n taprootKeySpendSignature: 0x13,\n taprootScriptSpendSignature: 0x14,\n} as const;\n\nfunction err(message: string): EraSdkError {\n return new EraSdkError('malformed-reply', `psbt: ${message}`);\n}\n\nclass Reader {\n offset = 0;\n constructor(readonly bytes: Uint8Array) {}\n\n get remaining(): number {\n return this.bytes.length - this.offset;\n }\n\n u8(): number {\n const b = this.bytes[this.offset];\n if (b === undefined) throw err('truncated');\n this.offset += 1;\n return b;\n }\n\n /** Bitcoin compact-size integer, MINIMAL encoding required (as consensus does). */\n compactSize(): number {\n const first = this.u8();\n if (first < 0xfd) return first;\n let width: number;\n let minimum: bigint;\n if (first === 0xfd) {\n width = 2;\n minimum = 0xfdn;\n } else if (first === 0xfe) {\n width = 4;\n minimum = 0x10000n;\n } else {\n width = 8;\n minimum = 0x100000000n;\n }\n let value = 0n;\n for (let i = 0; i < width; i++) value |= BigInt(this.u8()) << BigInt(8 * i);\n if (value < minimum) throw err('non-minimal compact-size encoding');\n if (value > BigInt(Number.MAX_SAFE_INTEGER)) throw err('length exceeds safe range');\n return Number(value);\n }\n\n take(length: number): Uint8Array {\n if (length > this.remaining) throw err('length exceeds input');\n const out = this.bytes.slice(this.offset, this.offset + length);\n this.offset += length;\n return out;\n }\n}\n\n/** Read one key/value map (ends at the 0x00 separator). */\nfunction readMap(reader: Reader): PsbtKeyValue[] {\n const entries: PsbtKeyValue[] = [];\n const seen = new Set<string>();\n for (;;) {\n const keyLength = reader.compactSize();\n if (keyLength === 0) return entries;\n const key = reader.take(keyLength);\n const value = reader.take(reader.compactSize());\n const keyId = Array.from(key)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n if (seen.has(keyId)) throw err('duplicate key within one map');\n seen.add(keyId);\n entries.push({ keyType: key[0]!, keyData: key.slice(1), value });\n }\n}\n\n/** Count of inputs/outputs in a (non-witness) unsigned transaction. */\nfunction countTxInputsOutputs(tx: Uint8Array): { inputs: number; outputs: number } {\n const reader = new Reader(tx);\n reader.take(4); // version\n const inputs = reader.compactSize();\n if (inputs === 0) {\n // A zero here would be a segwit marker — the PSBT unsigned tx must not\n // carry witness data, so this is not a transaction we can count.\n throw err('unsigned transaction has zero inputs (or carries witness data)');\n }\n for (let i = 0; i < inputs; i++) {\n reader.take(32 + 4); // prevout\n reader.take(reader.compactSize()); // scriptSig (empty in a PSBT)\n reader.take(4); // sequence\n }\n const outputs = reader.compactSize();\n for (let i = 0; i < outputs; i++) {\n reader.take(8); // amount\n reader.take(reader.compactSize()); // scriptPubKey\n }\n reader.take(4); // locktime\n if (reader.remaining !== 0) throw err('trailing bytes after the unsigned transaction');\n return { inputs, outputs };\n}\n\nexport function parsePsbt(bytes: Uint8Array): ParsedPsbt {\n const reader = new Reader(bytes);\n for (const expected of MAGIC) {\n if (reader.u8() !== expected) throw err('bad magic');\n }\n const globalMap = readMap(reader);\n\n let unsignedTx: Uint8Array | null = null;\n let version = 0;\n for (const entry of globalMap) {\n if (entry.keyType === 0x00 && entry.keyData.length === 0) unsignedTx = entry.value;\n if (entry.keyType === 0xfb && entry.keyData.length === 0) {\n if (entry.value.length !== 4) throw err('bad version field');\n version =\n (entry.value[0]! |\n (entry.value[1]! << 8) |\n (entry.value[2]! << 16) |\n (entry.value[3]! << 24)) >>>\n 0;\n }\n }\n if (unsignedTx === null) {\n // The device's signer relies on the global UNSIGNED_TX that only PSBT v0\n // carries; its absence means v2 (or not a PSBT at all).\n throw err('no global unsigned transaction — not a PSBT v0');\n }\n if (version !== 0) throw err(`unsupported PSBT version ${version}`);\n\n const counts = countTxInputsOutputs(unsignedTx);\n const inputs: PsbtKeyValue[][] = [];\n for (let i = 0; i < counts.inputs; i++) inputs.push(readMap(reader));\n const outputs: PsbtKeyValue[][] = [];\n for (let i = 0; i < counts.outputs; i++) outputs.push(readMap(reader));\n if (reader.remaining !== 0) throw err('trailing bytes after the output maps');\n\n return { unsignedTx, version, inputs, outputs };\n}\n\nexport function inputEntries(\n psbt: ParsedPsbt,\n index: number,\n keyType: number,\n): readonly PsbtKeyValue[] {\n return (psbt.inputs[index] ?? []).filter((e) => e.keyType === keyType);\n}\n\nexport function inputHas(psbt: ParsedPsbt, index: number, keyType: number): boolean {\n return inputEntries(psbt, index, keyType).length > 0;\n}\n","/** Outcome of a verification helper. Helpers return, never throw, on mismatches. */\nexport type VerifyResult =\n /** Verified cryptographically / byte-for-byte. */\n | { readonly ok: true; readonly checked: true }\n /**\n * Nothing client-side is verifiable for this input (EIP-712 typed data:\n * the digest is the hash of the structure, which only the device computes).\n * The UR-type pin and the request-id echo are the whole binding.\n */\n | { readonly ok: true; readonly checked: false; readonly reason: string }\n | { readonly ok: false; readonly reason: string };\n\nexport const verified: VerifyResult = { ok: true, checked: true };\nexport const failed = (reason: string): VerifyResult => ({ ok: false, reason });\nexport const unverifiable = (reason: string): VerifyResult => ({\n ok: true,\n checked: false,\n reason,\n});\n","import { equalBytes } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\nimport type { ParsedPsbt } from './psbt-reader';\nimport { inputEntries, inputHas, PsbtInputType, parsePsbt } from './psbt-reader';\nimport type { VerifyResult } from './result';\nimport { failed, verified } from './result';\n\nexport interface VerifySignedPsbtArgs {\n /** The PSBT you sent to the device. */\n readonly sentPsbt: Uint8Array;\n /** The PSBT the device returned. */\n readonly signedPsbt: Uint8Array;\n /**\n * true (default) on flows where every input is yours (a plain send): a\n * reply that signed only part of the transaction is refused here with a\n * reason instead of failing later inside a finalizer. Set false for dApp\n * `signPsbt` hand-backs, where a PSBT legitimately carries inputs you\n * cannot sign.\n */\n readonly requireEveryInputSigned?: boolean;\n}\n\n/**\n * The `crypto-psbt` reply carries NO request id — this comparison IS the\n * anti-replay binding for Bitcoin. It is not optional.\n *\n * The unsigned transaction is compared byte for byte, which pins the input\n * set and order, the outputs, their amounts, the version and the locktime in\n * one shot — and therefore the txid. The device only ADDS per-input\n * signature fields, so a legitimate reply always matches.\n */\nexport function verifySignedPsbt(args: VerifySignedPsbtArgs): VerifyResult {\n let sent: ParsedPsbt;\n let signed: ParsedPsbt;\n try {\n sent = parsePsbt(args.sentPsbt);\n } catch (e) {\n return failed(`the PSBT we sent is not readable: ${message(e)}`);\n }\n try {\n signed = parsePsbt(args.signedPsbt);\n } catch (e) {\n return failed(`the PSBT the device returned is not readable: ${message(e)}`);\n }\n\n if (!equalBytes(sent.unsignedTx, signed.unsignedTx)) {\n return failed('the returned PSBT is a different transaction from the one approved');\n }\n\n // A finalized field carries the COMPLETE scriptSig/witness that will be\n // broadcast, and the unsigned-tx comparison above does not cover it (it\n // lives per input, the unsigned tx in the global map). An input that comes\n // back finalized must have been SENT that way, with byte-identical\n // values — the device echoes these fields, it never authors them.\n const finalizedTypes = [PsbtInputType.finalScriptSig, PsbtInputType.finalScriptWitness];\n for (let i = 0; i < signed.inputs.length; i++) {\n for (const type of finalizedTypes) {\n if (!inputHas(signed, i, type)) continue;\n if (!inputHas(sent, i, type)) {\n return failed(\n `input ${i} came back finalized (type 0x${type.toString(16)}) and was not sent that way — the script it would broadcast is not ours`,\n );\n }\n const a = inputEntries(sent, i, type);\n const b = inputEntries(signed, i, type);\n if (a.length !== b.length || !a.every((entry, k) => equalBytes(entry.value, b[k]!.value))) {\n return failed(\n `input ${i} came back with a different finalized script than the one we sent`,\n );\n }\n }\n }\n\n const isSigned = (i: number): boolean =>\n inputHas(signed, i, PsbtInputType.partialSig) ||\n inputHas(signed, i, PsbtInputType.taprootKeySpendSignature) ||\n inputHas(signed, i, PsbtInputType.taprootScriptSpendSignature);\n\n const indexes = signed.inputs.map((_, i) => i);\n if (args.requireEveryInputSigned ?? true) {\n if (!indexes.every(isSigned)) {\n return failed('the device signed only part of the transaction');\n }\n } else if (!indexes.some(isSigned)) {\n return failed('the returned PSBT carries no signature');\n }\n return verified;\n}\n\nexport interface VerifyBtcMessageHeaderArgs {\n /** The address the request asked the device to sign with. */\n readonly address: string;\n /** The raw 65-byte BIP-137 signature. */\n readonly signature: Uint8Array;\n}\n\n/**\n * BIP-137: the recovery header names the address type a verifier derives\n * before comparing. A header of the wrong range produces a signature that\n * LOOKS fine (65 bytes, valid base64) but fails every verifier downstream —\n * this check is the only place that difference is visible.\n */\nexport function verifyBtcMessageHeader(args: VerifyBtcMessageHeaderArgs): VerifyResult {\n if (args.signature.length === 0) {\n return failed('empty signature');\n }\n const header = args.signature[0]!;\n const range = headerRangeFor(args.address);\n if (range === null) {\n return {\n ok: true,\n checked: false,\n reason: 'address kind has no BIP-137 header range to check against',\n };\n }\n if (header >= range.low && header <= range.high) return verified;\n return failed(\n `recovery header ${header} does not match a ${range.label} address ` +\n `(BIP-137 expects ${range.low}..${range.high}); this signature would not verify against the address it was asked to sign for`,\n );\n}\n\ninterface HeaderRange {\n readonly low: number;\n readonly high: number;\n readonly label: string;\n}\n\nfunction headerRangeFor(address: string): HeaderRange | null {\n const a = address.toLowerCase();\n if (a.startsWith('bc1q') || a.startsWith('tb1q') || a.startsWith('bcrt1q')) {\n return { low: 39, high: 42, label: 'native segwit (P2WPKH)' };\n }\n if (a.startsWith('bc1p') || a.startsWith('tb1p') || a.startsWith('bcrt1p')) {\n return null; // Taproot: BIP-137 does not cover it (BIP-322 is the scheme).\n }\n // Base58 kinds matched on SHAPE, not first character alone — a guard that\n // invents a range for a string it does not understand is worse than one\n // that declines to judge.\n if (looksBase58(address)) {\n if (address.startsWith('3') || address.startsWith('2')) {\n return { low: 35, high: 38, label: 'P2SH (nested segwit)' };\n }\n if (address.startsWith('1') || address.startsWith('m') || address.startsWith('n')) {\n // Both the uncompressed and compressed P2PKH ranges — which one is\n // right depends on the key the device used, which we do not know.\n return { low: 27, high: 34, label: 'legacy P2PKH' };\n }\n }\n return null;\n}\n\nfunction looksBase58(address: string): boolean {\n return address.length >= 26 && address.length <= 35 && /^[1-9A-HJ-NP-Za-km-z]+$/.test(address);\n}\n\nfunction message(e: unknown): string {\n return e instanceof EraSdkError || e instanceof Error ? e.message : String(e);\n}\n","import { secp256k1 } from '@noble/curves/secp256k1';\nimport { keccak_256 } from '@noble/hashes/sha3';\nimport { EvmDataType, foldRecoveryId } from '../chains/evm';\nimport { bytesToHex, concatBytes, equalBytes, hexToBytes, utf8Encode } from '../core/bytes';\nimport type { VerifyResult } from './result';\nimport { failed, unverifiable, verified } from './result';\n\nexport interface VerifyEvmSignatureArgs {\n /** The exact bytes the request carried in `signData`. */\n readonly signData: Uint8Array;\n readonly dataType: EvmDataType;\n /** Raw `r || s || v` from the reply (65+ bytes; multi-byte legacy `v` handled). */\n readonly signature: Uint8Array;\n /** The signer address the request was built for. */\n readonly address: Uint8Array | `0x${string}`;\n /**\n * Optional: the signing payload re-derived from the transaction you are\n * ABOUT TO BROADCAST. Recovering against `signData` alone proves the device\n * signed something you asked for — this closes the second half: that it is\n * the transaction still in your hands (payloads can legitimately change\n * between build and send, e.g. a blockhash refresh in your own state).\n */\n readonly reEncodedSignData?: Uint8Array;\n}\n\n/** EIP-191: 0x19 || \"Ethereum Signed Message:\\n\" || len(message). */\nfunction personalSignDigest(message: Uint8Array): Uint8Array {\n const prefix = utf8Encode(`Ethereum Signed Message:\\n${message.length}`);\n return keccak_256(concatBytes(new Uint8Array([0x19]), prefix, message));\n}\n\n/**\n * \"Did the device sign exactly what I sent, with the key I expected?\"\n * keccak digest + public-key recovery; the recovered address must equal the\n * request's. Run it before broadcasting.\n */\nexport function verifyEvmSignature(args: VerifyEvmSignatureArgs): VerifyResult {\n if (args.signature.length < 65) {\n return failed('signature is shorter than 65 bytes');\n }\n if (args.reEncodedSignData !== undefined && !equalBytes(args.reEncodedSignData, args.signData)) {\n return failed('the transaction to broadcast is not the one the device signed');\n }\n\n let digest: Uint8Array;\n switch (args.dataType) {\n case EvmDataType.transaction:\n case EvmDataType.typedTransaction:\n digest = keccak_256(args.signData);\n break;\n case EvmDataType.personalMessage:\n digest = personalSignDigest(args.signData);\n break;\n case EvmDataType.typedData:\n return unverifiable(\n 'EIP-712: the digest is the hash of the structure, computed only on the device',\n );\n default:\n return failed(`unknown dataType ${args.dataType satisfies never}`);\n }\n\n let vBig = 0n;\n for (const b of args.signature.slice(64)) vBig = (vBig << 8n) | BigInt(b);\n const recoveryId = foldRecoveryId(vBig);\n if (recoveryId !== 0 && recoveryId !== 1) {\n return failed('implausible recovery value');\n }\n\n let recovered: Uint8Array;\n try {\n const point = secp256k1.Signature.fromCompact(args.signature.slice(0, 64))\n .addRecoveryBit(recoveryId)\n .recoverPublicKey(digest);\n recovered = keccak_256(point.toRawBytes(false).slice(1)).slice(12);\n } catch (e) {\n return failed(`signature could not be checked: ${(e as Error).message}`);\n }\n\n const expected = args.address instanceof Uint8Array ? args.address : hexToBytes(args.address);\n if (expected.length !== 20) {\n return failed(`expected address must be 20 bytes, got ${expected.length}`);\n }\n if (!equalBytes(recovered, expected)) {\n return failed(\n `the signature does not belong to this account (recovered 0x${bytesToHex(recovered)})`,\n );\n }\n return verified;\n}\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { equalBytes } from '../core/bytes';\nimport type { VerifyResult } from './result';\nimport { failed, verified } from './result';\n\nexport interface VerifySolanaSignatureArgs {\n /** The exact bytes the request carried in `signData` (the compiled message). */\n readonly signData: Uint8Array;\n /** 64-byte Ed25519 signature from the reply. */\n readonly signature: Uint8Array;\n /** The 32-byte signer public key the request was built for. */\n readonly publicKey: Uint8Array;\n /**\n * Optional: the message bytes you are ABOUT TO BROADCAST. Matters most on\n * Solana — a blockhash refresh between build and send makes \"what was\n * signed\" and \"what will be sent\" two different objects that must agree.\n */\n readonly broadcastMessageBytes?: Uint8Array;\n}\n\nexport function verifySolanaSignature(args: VerifySolanaSignatureArgs): VerifyResult {\n if (\n args.broadcastMessageBytes !== undefined &&\n !equalBytes(args.broadcastMessageBytes, args.signData)\n ) {\n return failed('the message to broadcast is not the one the device signed');\n }\n let ok: boolean;\n try {\n ok = ed25519.verify(args.signature, args.signData, args.publicKey);\n } catch (e) {\n return failed(`Solana signature could not be checked: ${(e as Error).message}`);\n }\n return ok ? verified : failed('the signature does not belong to this account');\n}\n","import { secp256k1 } from '@noble/curves/secp256k1';\nimport { sha256 } from '@noble/hashes/sha2';\nimport { keccak_256 } from '@noble/hashes/sha3';\nimport { createBase58check } from '@scure/base';\nimport { concatBytes, equalBytes, utf8Decode } from '../core/bytes';\nimport type { SignedTronTx, TronLatestBlock } from '../tron-proto/messages';\nimport { splitSignedTronTx } from '../tron-proto/messages';\nimport { firstBytes, firstVarint, readFields } from '../tron-proto/wire';\nimport type { VerifyResult } from './result';\nimport { failed, verified } from './result';\n\nconst base58check = createBase58check(sha256);\n\nexport interface VerifyTronSignatureArgs {\n /** The `raw_data` bytes the request carried. */\n readonly rawData: Uint8Array;\n /** The base58 owner address the transaction spends from. */\n readonly from: string;\n /** The reference block the request carried (enables the rebuild-path window check). */\n readonly latestBlock?: TronLatestBlock;\n /** The reply: either the split frame from `TronSignatureResult.signedTx`, or the raw hex. */\n readonly signedTx: SignedTronTx | string;\n}\n\n/**\n * The Tron reply is a fully signed transaction broadcast VERBATIM, so it is\n * checked on both counts: every signature must recover to the owner address,\n * and the transaction must move what the user approved.\n *\n * Byte equality with the request's `rawData` is the strong form. When the\n * bytes differ (a firmware that rebuilds `raw_data` from the semantic\n * fields), the fallback compares the fields that decide where the money goes,\n * plus the validity window against the reference block.\n */\nexport function verifyTronSignature(args: VerifyTronSignatureArgs): VerifyResult {\n let signedTx: SignedTronTx;\n try {\n signedTx = typeof args.signedTx === 'string' ? splitSignedTronTx(args.signedTx) : args.signedTx;\n } catch (e) {\n return failed(`the returned Tron transaction is not readable: ${(e as Error).message}`);\n }\n if (signedTx.signatures.length === 0) {\n return failed('the returned Tron transaction carries no signature');\n }\n if (!args.from) {\n return failed('no owner address to check the signature against');\n }\n\n const digest = sha256(signedTx.rawData);\n for (const signature of signedTx.signatures) {\n const recovered = recoverTronAddress(digest, signature);\n if (recovered === null) return failed('signature could not be checked');\n if (recovered !== args.from) {\n return failed('the signature does not belong to this account');\n }\n }\n\n if (equalBytes(signedTx.rawData, args.rawData)) return verified;\n\n // Rebuild path: the firmware built its own raw_data from the semantic\n // fields. Compare the operation, then the validity window.\n const contractResult = compareContracts(args.rawData, signedTx.rawData);\n if (contractResult !== null) return contractResult;\n if (args.latestBlock === undefined) {\n return failed(\n 'the returned raw_data differs from the request and no latestBlock was provided to check the validity window',\n );\n }\n return compareWindow(signedTx.rawData, args.latestBlock);\n}\n\nfunction recoverTronAddress(digest: Uint8Array, signature: Uint8Array): string | null {\n if (signature.length < 65) return null;\n const recovery = signature[64]!;\n const recoveryId = recovery >= 27 ? (recovery - 27) & 1 : recovery & 1;\n try {\n const point = secp256k1.Signature.fromCompact(signature.slice(0, 64))\n .addRecoveryBit(recoveryId)\n .recoverPublicKey(digest);\n const hash = keccak_256(point.toRawBytes(false).slice(1));\n return base58check.encode(concatBytes(new Uint8Array([0x41]), hash.slice(12)));\n } catch {\n return null;\n }\n}\n\n// --- raw_data structural comparison ---------------------------------------\n\ninterface TronContract {\n readonly typeUrl: string;\n readonly type: bigint;\n readonly parameter: Uint8Array;\n}\n\ninterface TronRawData {\n readonly contracts: readonly TronContract[];\n readonly expiration: bigint;\n readonly timestamp: bigint;\n}\n\nfunction parseRawData(bytes: Uint8Array): TronRawData | null {\n try {\n const fields = readFields(bytes);\n const contracts: TronContract[] = [];\n for (const f of fields) {\n if (f.field === 11 && f.wireType === 2) {\n const c = readFields(f.bytes);\n const anyBytes = firstBytes(c, 2);\n const any = anyBytes ? readFields(anyBytes) : [];\n const typeUrlBytes = anyBytes ? firstBytes(any, 1) : null;\n contracts.push({\n typeUrl: typeUrlBytes ? utf8Decode(typeUrlBytes) : '',\n type: firstVarint(c, 1) ?? 0n,\n parameter: (anyBytes && firstBytes(any, 2)) || new Uint8Array(0),\n });\n }\n }\n return {\n contracts,\n expiration: firstVarint(fields, 8) ?? 0n,\n timestamp: firstVarint(fields, 14) ?? 0n,\n };\n } catch {\n return null;\n }\n}\n\n/** null = contracts match (continue to the window check); a result = verdict. */\nfunction compareContracts(askedBytes: Uint8Array, repliedBytes: Uint8Array): VerifyResult | null {\n const asked = parseRawData(askedBytes);\n const replied = parseRawData(repliedBytes);\n if (!asked) return failed('the Tron transaction built by the app could not be read');\n if (!replied) return failed('the Tron transaction built by the device could not be read');\n if (asked.contracts.length !== 1 || replied.contracts.length !== 1) {\n return failed('the Tron transaction does not carry exactly one contract');\n }\n const a = asked.contracts[0]!;\n const b = replied.contracts[0]!;\n const kindA = contractKind(a);\n const kindB = contractKind(b);\n if (kindA !== kindB) {\n return failed('the returned Tron transaction is a different kind of operation');\n }\n\n const pa = readFieldsSafe(a.parameter);\n const pb = readFieldsSafe(b.parameter);\n if (!pa || !pb) return failed('the Tron contract parameters could not be read');\n\n switch (kindA) {\n case 'transfer':\n // TransferContract {1: owner, 2: to, 3: amount}\n if (\n sameBytesField(pa, pb, 1) &&\n sameBytesField(pa, pb, 2) &&\n (firstVarint(pa, 3) ?? 0n) === (firstVarint(pb, 3) ?? 0n)\n ) {\n return null;\n }\n break;\n case 'transferAsset':\n // TransferAssetContract {1: asset, 2: owner, 3: to, 4: amount}\n if (\n sameBytesField(pa, pb, 1) &&\n sameBytesField(pa, pb, 2) &&\n sameBytesField(pa, pb, 3) &&\n (firstVarint(pa, 4) ?? 0n) === (firstVarint(pb, 4) ?? 0n)\n ) {\n return null;\n }\n break;\n case 'triggerSmartContract':\n // TriggerSmartContract {1: owner, 2: contract, 3: call_value, 4: data}.\n // call_value: zero and absent are the same thing — the two sides are\n // serialized by different protobuf writers that disagree on whether a\n // zero scalar is written, and every TRC-20 transfer carries call_value 0.\n if (\n sameBytesField(pa, pb, 1) &&\n sameBytesField(pa, pb, 2) &&\n (firstVarint(pa, 3) ?? 0n) === (firstVarint(pb, 3) ?? 0n) &&\n sameBytesField(pa, pb, 4)\n ) {\n return null;\n }\n break;\n default:\n // An operation this gate cannot compare field by field is not waved\n // through: without byte equality there is nothing left to bind it to\n // what the user approved.\n return failed('the returned Tron transaction carries an operation this check cannot compare');\n }\n return failed('the returned Tron transaction does not match the one approved');\n}\n\ntype ContractKind = 'transfer' | 'transferAsset' | 'triggerSmartContract' | 'other';\n\nfunction contractKind(contract: TronContract): ContractKind {\n if (contract.typeUrl.endsWith('.TransferContract') || contract.type === 1n) return 'transfer';\n if (contract.typeUrl.endsWith('.TransferAssetContract') || contract.type === 2n) {\n return 'transferAsset';\n }\n if (contract.typeUrl.endsWith('.TriggerSmartContract') || contract.type === 31n) {\n return 'triggerSmartContract';\n }\n return 'other';\n}\n\nfunction readFieldsSafe(bytes: Uint8Array): ReturnType<typeof readFields> | null {\n try {\n return readFields(bytes);\n } catch {\n return null;\n }\n}\n\nfunction sameBytesField(\n a: ReturnType<typeof readFields>,\n b: ReturnType<typeof readFields>,\n field: number,\n): boolean {\n const x = firstBytes(a, field) ?? new Uint8Array(0);\n const y = firstBytes(b, field) ?? new Uint8Array(0);\n return equalBytes(x, y);\n}\n\n/**\n * Firmware formulas, not policy: the two device-side transaction builders\n * stamp `timestamp` with the request's reference-block timestamp verbatim and\n * set `expiration` to +10 minutes (one builder) or +10 hours (the other). A\n * range spanning both cannot refuse a reply the live fleet produces.\n */\nconst MIN_EXPIRY_MS = 10n * 60n * 1000n;\nconst MAX_EXPIRY_MS = 10n * 60n * 60n * 1000n;\n\nfunction compareWindow(repliedBytes: Uint8Array, latestBlock: TronLatestBlock): VerifyResult {\n const replied = parseRawData(repliedBytes);\n if (!replied) return failed('the Tron transaction built by the device could not be read');\n const block = BigInt(latestBlock.timestamp);\n if (replied.timestamp !== block) {\n return failed(\n 'the returned Tron transaction is stamped against a different reference block than the one we sent',\n );\n }\n const validFor = replied.expiration - block;\n if (validFor < MIN_EXPIRY_MS || validFor > MAX_EXPIRY_MS) {\n return failed(\n `the returned Tron transaction is valid for ${validFor} ms after the reference block, outside the firmware's ${MIN_EXPIRY_MS}-${MAX_EXPIRY_MS} ms window`,\n );\n }\n return verified;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"verify.js","names":["err"],"sources":["../src/verify/psbt-reader.ts","../src/verify/result.ts","../src/verify/btc.ts","../src/verify/cardano.ts","../src/verify/evm.ts","../src/verify/solana.ts","../src/verify/ton-boc.ts","../src/verify/ton.ts","../src/verify/tron.ts"],"sourcesContent":["import { EraSdkError } from '../core/errors';\n\n/**\n * Minimal PSBT v0 (BIP-174) reader — just enough structure for the\n * verification guard: the global unsigned transaction (verbatim slice, never\n * re-serialized), the PSBT version, and per-input key/value maps.\n *\n * Hardened: compact-size lengths bounds-checked before slicing, duplicate\n * keys within one map refused (a hostile PSBT carrying two final scriptSigs\n * for one input must not survive parsing).\n */\n\nexport interface PsbtKeyValue {\n readonly keyType: number;\n readonly keyData: Uint8Array;\n readonly value: Uint8Array;\n}\n\nexport interface ParsedPsbt {\n /** The global UNSIGNED_TX value, verbatim. */\n readonly unsignedTx: Uint8Array;\n /** Global PSBT_GLOBAL_VERSION (0xFB) if present; v0 files normally omit it. */\n readonly version: number;\n readonly inputs: readonly (readonly PsbtKeyValue[])[];\n readonly outputs: readonly (readonly PsbtKeyValue[])[];\n}\n\nconst MAGIC = [0x70, 0x73, 0x62, 0x74, 0xff]; // \"psbt\\xff\"\n\nexport const PsbtInputType = {\n partialSig: 0x02,\n finalScriptSig: 0x07,\n finalScriptWitness: 0x08,\n taprootKeySpendSignature: 0x13,\n taprootScriptSpendSignature: 0x14,\n} as const;\n\nfunction err(message: string): EraSdkError {\n return new EraSdkError('malformed-reply', `psbt: ${message}`);\n}\n\nclass Reader {\n offset = 0;\n constructor(readonly bytes: Uint8Array) {}\n\n get remaining(): number {\n return this.bytes.length - this.offset;\n }\n\n u8(): number {\n const b = this.bytes[this.offset];\n if (b === undefined) throw err('truncated');\n this.offset += 1;\n return b;\n }\n\n /** Bitcoin compact-size integer, MINIMAL encoding required (as consensus does). */\n compactSize(): number {\n const first = this.u8();\n if (first < 0xfd) return first;\n let width: number;\n let minimum: bigint;\n if (first === 0xfd) {\n width = 2;\n minimum = 0xfdn;\n } else if (first === 0xfe) {\n width = 4;\n minimum = 0x10000n;\n } else {\n width = 8;\n minimum = 0x100000000n;\n }\n let value = 0n;\n for (let i = 0; i < width; i++) value |= BigInt(this.u8()) << BigInt(8 * i);\n if (value < minimum) throw err('non-minimal compact-size encoding');\n if (value > BigInt(Number.MAX_SAFE_INTEGER)) throw err('length exceeds safe range');\n return Number(value);\n }\n\n take(length: number): Uint8Array {\n if (length > this.remaining) throw err('length exceeds input');\n const out = this.bytes.slice(this.offset, this.offset + length);\n this.offset += length;\n return out;\n }\n}\n\n/** Read one key/value map (ends at the 0x00 separator). */\nfunction readMap(reader: Reader): PsbtKeyValue[] {\n const entries: PsbtKeyValue[] = [];\n const seen = new Set<string>();\n for (;;) {\n const keyLength = reader.compactSize();\n if (keyLength === 0) return entries;\n const key = reader.take(keyLength);\n const value = reader.take(reader.compactSize());\n const keyId = Array.from(key)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n if (seen.has(keyId)) throw err('duplicate key within one map');\n seen.add(keyId);\n entries.push({ keyType: key[0]!, keyData: key.slice(1), value });\n }\n}\n\n/** Count of inputs/outputs in a (non-witness) unsigned transaction. */\nfunction countTxInputsOutputs(tx: Uint8Array): { inputs: number; outputs: number } {\n const reader = new Reader(tx);\n reader.take(4); // version\n const inputs = reader.compactSize();\n if (inputs === 0) {\n // A zero here would be a segwit marker — the PSBT unsigned tx must not\n // carry witness data, so this is not a transaction we can count.\n throw err('unsigned transaction has zero inputs (or carries witness data)');\n }\n for (let i = 0; i < inputs; i++) {\n reader.take(32 + 4); // prevout\n reader.take(reader.compactSize()); // scriptSig (empty in a PSBT)\n reader.take(4); // sequence\n }\n const outputs = reader.compactSize();\n for (let i = 0; i < outputs; i++) {\n reader.take(8); // amount\n reader.take(reader.compactSize()); // scriptPubKey\n }\n reader.take(4); // locktime\n if (reader.remaining !== 0) throw err('trailing bytes after the unsigned transaction');\n return { inputs, outputs };\n}\n\nexport function parsePsbt(bytes: Uint8Array): ParsedPsbt {\n const reader = new Reader(bytes);\n for (const expected of MAGIC) {\n if (reader.u8() !== expected) throw err('bad magic');\n }\n const globalMap = readMap(reader);\n\n let unsignedTx: Uint8Array | null = null;\n let version = 0;\n for (const entry of globalMap) {\n if (entry.keyType === 0x00 && entry.keyData.length === 0) unsignedTx = entry.value;\n if (entry.keyType === 0xfb && entry.keyData.length === 0) {\n if (entry.value.length !== 4) throw err('bad version field');\n version =\n (entry.value[0]! |\n (entry.value[1]! << 8) |\n (entry.value[2]! << 16) |\n (entry.value[3]! << 24)) >>>\n 0;\n }\n }\n if (unsignedTx === null) {\n // The device's signer relies on the global UNSIGNED_TX that only PSBT v0\n // carries; its absence means v2 (or not a PSBT at all).\n throw err('no global unsigned transaction — not a PSBT v0');\n }\n if (version !== 0) throw err(`unsupported PSBT version ${version}`);\n\n const counts = countTxInputsOutputs(unsignedTx);\n const inputs: PsbtKeyValue[][] = [];\n for (let i = 0; i < counts.inputs; i++) inputs.push(readMap(reader));\n const outputs: PsbtKeyValue[][] = [];\n for (let i = 0; i < counts.outputs; i++) outputs.push(readMap(reader));\n if (reader.remaining !== 0) throw err('trailing bytes after the output maps');\n\n return { unsignedTx, version, inputs, outputs };\n}\n\nexport function inputEntries(\n psbt: ParsedPsbt,\n index: number,\n keyType: number,\n): readonly PsbtKeyValue[] {\n return (psbt.inputs[index] ?? []).filter((e) => e.keyType === keyType);\n}\n\nexport function inputHas(psbt: ParsedPsbt, index: number, keyType: number): boolean {\n return inputEntries(psbt, index, keyType).length > 0;\n}\n","/** Outcome of a verification helper. Helpers return, never throw, on mismatches. */\nexport type VerifyResult =\n /** Verified cryptographically / byte-for-byte. */\n | { readonly ok: true; readonly checked: true }\n /**\n * Nothing client-side is verifiable for this input (EIP-712 typed data:\n * the digest is the hash of the structure, which only the device computes).\n * The UR-type pin and the request-id echo are the whole binding.\n */\n | { readonly ok: true; readonly checked: false; readonly reason: string }\n | { readonly ok: false; readonly reason: string };\n\nexport const verified: VerifyResult = { ok: true, checked: true };\nexport const failed = (reason: string): VerifyResult => ({ ok: false, reason });\nexport const unverifiable = (reason: string): VerifyResult => ({\n ok: true,\n checked: false,\n reason,\n});\n","import { equalBytes } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\nimport type { ParsedPsbt } from './psbt-reader';\nimport { inputEntries, inputHas, PsbtInputType, parsePsbt } from './psbt-reader';\nimport type { VerifyResult } from './result';\nimport { failed, verified } from './result';\n\nexport interface VerifySignedPsbtArgs {\n /** The PSBT you sent to the device. */\n readonly sentPsbt: Uint8Array;\n /** The PSBT the device returned. */\n readonly signedPsbt: Uint8Array;\n /**\n * true (default) on flows where every input is yours (a plain send): a\n * reply that signed only part of the transaction is refused here with a\n * reason instead of failing later inside a finalizer. Set false for dApp\n * `signPsbt` hand-backs, where a PSBT legitimately carries inputs you\n * cannot sign.\n */\n readonly requireEveryInputSigned?: boolean;\n}\n\n/**\n * The `crypto-psbt` reply carries NO request id — this comparison IS the\n * anti-replay binding for Bitcoin. It is not optional.\n *\n * The unsigned transaction is compared byte for byte, which pins the input\n * set and order, the outputs, their amounts, the version and the locktime in\n * one shot — and therefore the txid. The device only ADDS per-input\n * signature fields, so a legitimate reply always matches.\n */\nexport function verifySignedPsbt(args: VerifySignedPsbtArgs): VerifyResult {\n let sent: ParsedPsbt;\n let signed: ParsedPsbt;\n try {\n sent = parsePsbt(args.sentPsbt);\n } catch (e) {\n return failed(`the PSBT we sent is not readable: ${message(e)}`);\n }\n try {\n signed = parsePsbt(args.signedPsbt);\n } catch (e) {\n return failed(`the PSBT the device returned is not readable: ${message(e)}`);\n }\n\n if (!equalBytes(sent.unsignedTx, signed.unsignedTx)) {\n return failed('the returned PSBT is a different transaction from the one approved');\n }\n\n // A finalized field carries the COMPLETE scriptSig/witness that will be\n // broadcast, and the unsigned-tx comparison above does not cover it (it\n // lives per input, the unsigned tx in the global map). An input that comes\n // back finalized must have been SENT that way, with byte-identical\n // values — the device echoes these fields, it never authors them.\n const finalizedTypes = [PsbtInputType.finalScriptSig, PsbtInputType.finalScriptWitness];\n for (let i = 0; i < signed.inputs.length; i++) {\n for (const type of finalizedTypes) {\n if (!inputHas(signed, i, type)) continue;\n if (!inputHas(sent, i, type)) {\n return failed(\n `input ${i} came back finalized (type 0x${type.toString(16)}) and was not sent that way — the script it would broadcast is not ours`,\n );\n }\n const a = inputEntries(sent, i, type);\n const b = inputEntries(signed, i, type);\n if (a.length !== b.length || !a.every((entry, k) => equalBytes(entry.value, b[k]!.value))) {\n return failed(\n `input ${i} came back with a different finalized script than the one we sent`,\n );\n }\n }\n }\n\n const isSigned = (i: number): boolean =>\n inputHas(signed, i, PsbtInputType.partialSig) ||\n inputHas(signed, i, PsbtInputType.taprootKeySpendSignature) ||\n inputHas(signed, i, PsbtInputType.taprootScriptSpendSignature);\n\n const indexes = signed.inputs.map((_, i) => i);\n if (args.requireEveryInputSigned ?? true) {\n if (!indexes.every(isSigned)) {\n return failed('the device signed only part of the transaction');\n }\n } else if (!indexes.some(isSigned)) {\n return failed('the returned PSBT carries no signature');\n }\n return verified;\n}\n\nexport interface VerifyBtcMessageHeaderArgs {\n /** The address the request asked the device to sign with. */\n readonly address: string;\n /** The raw 65-byte BIP-137 signature. */\n readonly signature: Uint8Array;\n}\n\n/**\n * BIP-137: the recovery header names the address type a verifier derives\n * before comparing. A header of the wrong range produces a signature that\n * LOOKS fine (65 bytes, valid base64) but fails every verifier downstream —\n * this check is the only place that difference is visible.\n */\nexport function verifyBtcMessageHeader(args: VerifyBtcMessageHeaderArgs): VerifyResult {\n if (args.signature.length === 0) {\n return failed('empty signature');\n }\n const header = args.signature[0]!;\n const range = headerRangeFor(args.address);\n if (range === null) {\n return {\n ok: true,\n checked: false,\n reason: 'address kind has no BIP-137 header range to check against',\n };\n }\n if (header >= range.low && header <= range.high) return verified;\n return failed(\n `recovery header ${header} does not match a ${range.label} address ` +\n `(BIP-137 expects ${range.low}..${range.high}); this signature would not verify against the address it was asked to sign for`,\n );\n}\n\ninterface HeaderRange {\n readonly low: number;\n readonly high: number;\n readonly label: string;\n}\n\nfunction headerRangeFor(address: string): HeaderRange | null {\n const a = address.toLowerCase();\n if (a.startsWith('bc1q') || a.startsWith('tb1q') || a.startsWith('bcrt1q')) {\n return { low: 39, high: 42, label: 'native segwit (P2WPKH)' };\n }\n if (a.startsWith('bc1p') || a.startsWith('tb1p') || a.startsWith('bcrt1p')) {\n return null; // Taproot: BIP-137 does not cover it (BIP-322 is the scheme).\n }\n // Base58 kinds matched on SHAPE, not first character alone — a guard that\n // invents a range for a string it does not understand is worse than one\n // that declines to judge.\n if (looksBase58(address)) {\n if (address.startsWith('3') || address.startsWith('2')) {\n return { low: 35, high: 38, label: 'P2SH (nested segwit)' };\n }\n if (address.startsWith('1') || address.startsWith('m') || address.startsWith('n')) {\n // Both the uncompressed and compressed P2PKH ranges — which one is\n // right depends on the key the device used, which we do not know.\n return { low: 27, high: 34, label: 'legacy P2PKH' };\n }\n }\n return null;\n}\n\nfunction looksBase58(address: string): boolean {\n return address.length >= 26 && address.length <= 35 && /^[1-9A-HJ-NP-Za-km-z]+$/.test(address);\n}\n\nfunction message(e: unknown): string {\n return e instanceof EraSdkError || e instanceof Error ? e.message : String(e);\n}\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { blake2b } from '@noble/hashes/blake2b';\nimport { cardanoSoftDerivePath } from '../accounts/derive';\nimport type { CardanoWitness } from '../chains/cardano';\nimport { parseWitnessSet } from '../chains/cardano';\nimport { bytesToHex, equalBytes } from '../core/bytes';\nimport { parsePath } from '../registry/keypath';\nimport type { VerifyResult } from './result';\nimport { failed, verified } from './result';\n\nexport interface VerifyCardanoSignatureArgs {\n /** The exact bytes the request carried in `signData` (the full tx CBOR array). */\n readonly signData: Uint8Array;\n /** The reply's witness set (or already-parsed witnesses). */\n readonly witnessSet?: Uint8Array;\n readonly witnesses?: readonly CardanoWitness[];\n /**\n * Optional but STRONGLY recommended — binds the witnesses to YOUR wallet:\n * the linked account's key material plus the signing paths your request\n * carried. Without it the check only proves internal consistency of the\n * reply (any key could have produced a matching pair).\n */\n readonly account?: {\n /** From `accounts.cardano()`: the account xpub halves and its path. */\n readonly publicKey: Uint8Array;\n readonly chainCode: Uint8Array;\n readonly accountPath: string;\n };\n /** The unique signing paths of the request (utxos + certKeys), e.g. `m/1852'/1815'/0'/0/0`. */\n readonly signerPaths?: readonly string[];\n}\n\n/**\n * Recompute the digest the device signs — BLAKE2b-256 of the ENCODED FIRST\n * ELEMENT of the transaction CBOR array (the tx body) — and verify every\n * `[vkey, signature]` pair against it. With `account` + `signerPaths`, the\n * vkeys are additionally required to be exactly the soft-derived children of\n * YOUR linked account at the request's own paths.\n */\nexport function verifyCardanoSignature(args: VerifyCardanoSignatureArgs): VerifyResult {\n let witnesses: readonly CardanoWitness[];\n try {\n witnesses = args.witnesses ?? (args.witnessSet ? parseWitnessSet(args.witnessSet) : []);\n } catch (e) {\n return failed(`witness set is not readable: ${(e as Error).message}`);\n }\n if (witnesses.length === 0) return failed('no witnesses to verify');\n\n let digest: Uint8Array;\n try {\n digest = blake2b(firstArrayItemBytes(args.signData), { dkLen: 32 });\n } catch (e) {\n return failed(`signData is not a readable transaction array: ${(e as Error).message}`);\n }\n\n for (const witness of witnesses) {\n let ok: boolean;\n try {\n ok = ed25519.verify(witness.signature, digest, witness.vkey);\n } catch (e) {\n return failed(`Cardano signature could not be checked: ${(e as Error).message}`);\n }\n if (!ok) return failed('a witness signature does not verify against its own vkey');\n }\n\n if (args.account && args.signerPaths && args.signerPaths.length > 0) {\n const accountLevels = parsePath(args.account.accountPath);\n const expected = new Map<string, string>(); // vkey hex -> path\n for (const path of new Set(args.signerPaths)) {\n const levels = parsePath(path);\n if (\n levels.length !== accountLevels.length + 2 ||\n !accountLevels.every(\n (l, i) => levels[i]!.index === l.index && levels[i]!.hardened === l.hardened,\n ) ||\n levels.slice(accountLevels.length).some((l) => l.hardened)\n ) {\n return failed(\n `signer path ${path} does not extend the account path with two soft components`,\n );\n }\n const tail = levels.slice(accountLevels.length).map((l) => l.index);\n const vkey = cardanoSoftDerivePath(args.account.publicKey, args.account.chainCode, tail);\n expected.set(bytesToHex(vkey), path);\n }\n // Every requested path must have produced a witness…\n for (const [vkeyHex, path] of expected) {\n if (!witnesses.some((w) => bytesToHex(w.vkey) === vkeyHex)) {\n return failed(`no witness for the requested signer path ${path}`);\n }\n }\n // …and every witness must belong to a requested path (no foreign keys).\n for (const witness of witnesses) {\n if (!expected.has(bytesToHex(witness.vkey))) {\n return failed('the witness set carries a key your request did not ask for');\n }\n }\n }\n return verified;\n}\n\n// ---------------------------------------------------------------------------\n// CBOR item walker: the encoded extent of the first element of a CBOR array.\n// Supports definite AND indefinite lengths (wallet-produced tx CBOR may use\n// either), with depth/size hardening.\n// ---------------------------------------------------------------------------\n\nexport function firstArrayItemBytes(bytes: Uint8Array): Uint8Array {\n const top = bytes[0];\n if (top === undefined) throw new Error('empty input');\n const major = top >> 5;\n if (major !== 4) throw new Error('not a CBOR array');\n let start: number;\n if ((top & 0x1f) === 31) {\n start = 1; // indefinite array\n } else {\n const head = readHead(bytes, 0);\n if (head.value === 0n) throw new Error('transaction array is empty');\n start = head.next;\n }\n const end = skipItem(bytes, start, 0);\n return bytes.slice(start, end);\n}\n\nfunction readHead(bytes: Uint8Array, offset: number): { value: bigint; next: number } {\n const initial = bytes[offset];\n if (initial === undefined) throw new Error('truncated');\n const info = initial & 0x1f;\n if (info < 24) return { value: BigInt(info), next: offset + 1 };\n if (info === 31) return { value: -1n, next: offset + 1 }; // indefinite marker\n let width: number;\n if (info === 24) width = 1;\n else if (info === 25) width = 2;\n else if (info === 26) width = 4;\n else if (info === 27) width = 8;\n else throw new Error('reserved length encoding');\n let value = 0n;\n for (let i = 0; i < width; i++) {\n const b = bytes[offset + 1 + i];\n if (b === undefined) throw new Error('truncated');\n value = (value << 8n) | BigInt(b);\n }\n return { value, next: offset + 1 + width };\n}\n\n/** Returns the offset just past the item starting at `offset`. */\nfunction skipItem(bytes: Uint8Array, offset: number, depth: number): number {\n if (depth > 32) throw new Error('nesting too deep');\n const initial = bytes[offset];\n if (initial === undefined) throw new Error('truncated');\n const major = initial >> 5;\n const head = readHead(bytes, offset);\n\n switch (major) {\n case 0:\n case 1:\n if (head.value === -1n) throw new Error('malformed integer');\n return head.next;\n case 2:\n case 3: {\n if (head.value === -1n) {\n // Indefinite string: chunks until 0xFF.\n let pos = head.next;\n while (bytes[pos] !== 0xff) {\n const chunk = readHead(bytes, pos);\n if (chunk.value < 0n) throw new Error('malformed chunk');\n pos = chunk.next + Number(chunk.value);\n if (pos > bytes.length) throw new Error('truncated string');\n }\n return pos + 1;\n }\n const end = head.next + Number(head.value);\n if (end > bytes.length) throw new Error('truncated string');\n return end;\n }\n case 4:\n case 5: {\n const perEntry = major === 5 ? 2 : 1;\n if (head.value === -1n) {\n let pos = head.next;\n while (bytes[pos] !== 0xff) {\n for (let i = 0; i < perEntry; i++) pos = skipItem(bytes, pos, depth + 1);\n }\n return pos + 1;\n }\n let pos = head.next;\n const count = Number(head.value) * perEntry;\n if (count > 1_000_000) throw new Error('container too large');\n for (let i = 0; i < count; i++) pos = skipItem(bytes, pos, depth + 1);\n return pos;\n }\n case 6:\n if (head.value === -1n) throw new Error('malformed tag');\n return skipItem(bytes, head.next, depth + 1);\n case 7:\n if ((initial & 0x1f) === 31) throw new Error('unexpected break');\n if ((initial & 0x1f) === 25) return offset + 3;\n if ((initial & 0x1f) === 26) return offset + 5;\n if ((initial & 0x1f) === 27) return offset + 9;\n return head.next;\n default:\n throw new Error('unreachable');\n }\n}\n","import { secp256k1 } from '@noble/curves/secp256k1';\nimport { keccak_256 } from '@noble/hashes/sha3';\nimport { EvmDataType, foldRecoveryId } from '../chains/evm';\nimport { bytesToHex, concatBytes, equalBytes, hexToBytes, utf8Encode } from '../core/bytes';\nimport type { VerifyResult } from './result';\nimport { failed, unverifiable, verified } from './result';\n\nexport interface VerifyEvmSignatureArgs {\n /** The exact bytes the request carried in `signData`. */\n readonly signData: Uint8Array;\n readonly dataType: EvmDataType;\n /** Raw `r || s || v` from the reply (65+ bytes; multi-byte legacy `v` handled). */\n readonly signature: Uint8Array;\n /** The signer address the request was built for. */\n readonly address: Uint8Array | `0x${string}`;\n /**\n * Optional: the signing payload re-derived from the transaction you are\n * ABOUT TO BROADCAST. Recovering against `signData` alone proves the device\n * signed something you asked for — this closes the second half: that it is\n * the transaction still in your hands (payloads can legitimately change\n * between build and send, e.g. a blockhash refresh in your own state).\n */\n readonly reEncodedSignData?: Uint8Array;\n}\n\n/** EIP-191: 0x19 || \"Ethereum Signed Message:\\n\" || len(message). */\nfunction personalSignDigest(message: Uint8Array): Uint8Array {\n const prefix = utf8Encode(`Ethereum Signed Message:\\n${message.length}`);\n return keccak_256(concatBytes(new Uint8Array([0x19]), prefix, message));\n}\n\n/**\n * \"Did the device sign exactly what I sent, with the key I expected?\"\n * keccak digest + public-key recovery; the recovered address must equal the\n * request's. Run it before broadcasting.\n */\nexport function verifyEvmSignature(args: VerifyEvmSignatureArgs): VerifyResult {\n if (args.signature.length < 65) {\n return failed('signature is shorter than 65 bytes');\n }\n if (args.reEncodedSignData !== undefined && !equalBytes(args.reEncodedSignData, args.signData)) {\n return failed('the transaction to broadcast is not the one the device signed');\n }\n\n let digest: Uint8Array;\n switch (args.dataType) {\n case EvmDataType.transaction:\n case EvmDataType.typedTransaction:\n digest = keccak_256(args.signData);\n break;\n case EvmDataType.personalMessage:\n digest = personalSignDigest(args.signData);\n break;\n case EvmDataType.typedData:\n return unverifiable(\n 'EIP-712: the digest is the hash of the structure, computed only on the device',\n );\n default:\n return failed(`unknown dataType ${args.dataType satisfies never}`);\n }\n\n let vBig = 0n;\n for (const b of args.signature.slice(64)) vBig = (vBig << 8n) | BigInt(b);\n const recoveryId = foldRecoveryId(vBig);\n if (recoveryId !== 0 && recoveryId !== 1) {\n return failed('implausible recovery value');\n }\n\n let recovered: Uint8Array;\n try {\n const point = secp256k1.Signature.fromCompact(args.signature.slice(0, 64))\n .addRecoveryBit(recoveryId)\n .recoverPublicKey(digest);\n recovered = keccak_256(point.toRawBytes(false).slice(1)).slice(12);\n } catch (e) {\n return failed(`signature could not be checked: ${(e as Error).message}`);\n }\n\n const expected = args.address instanceof Uint8Array ? args.address : hexToBytes(args.address);\n if (expected.length !== 20) {\n return failed(`expected address must be 20 bytes, got ${expected.length}`);\n }\n if (!equalBytes(recovered, expected)) {\n return failed(\n `the signature does not belong to this account (recovered 0x${bytesToHex(recovered)})`,\n );\n }\n return verified;\n}\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { equalBytes } from '../core/bytes';\nimport type { VerifyResult } from './result';\nimport { failed, verified } from './result';\n\nexport interface VerifySolanaSignatureArgs {\n /** The exact bytes the request carried in `signData` (the compiled message). */\n readonly signData: Uint8Array;\n /** 64-byte Ed25519 signature from the reply. */\n readonly signature: Uint8Array;\n /** The 32-byte signer public key the request was built for. */\n readonly publicKey: Uint8Array;\n /**\n * Optional: the message bytes you are ABOUT TO BROADCAST. Matters most on\n * Solana — a blockhash refresh between build and send makes \"what was\n * signed\" and \"what will be sent\" two different objects that must agree.\n */\n readonly broadcastMessageBytes?: Uint8Array;\n}\n\nexport function verifySolanaSignature(args: VerifySolanaSignatureArgs): VerifyResult {\n if (\n args.broadcastMessageBytes !== undefined &&\n !equalBytes(args.broadcastMessageBytes, args.signData)\n ) {\n return failed('the message to broadcast is not the one the device signed');\n }\n let ok: boolean;\n try {\n ok = ed25519.verify(args.signature, args.signData, args.publicKey);\n } catch (e) {\n return failed(`Solana signature could not be checked: ${(e as Error).message}`);\n }\n return ok ? verified : failed('the signature does not belong to this account');\n}\n","import { sha256 } from '@noble/hashes/sha2';\nimport { concatBytes } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\n\n/**\n * Minimal TON Bag-of-Cells reader + cell representation hash — exactly the\n * subset the device's signer implements: generic BoC, ordinary level-0 cells,\n * first root. The transaction digest the device signs IS the root cell's\n * representation hash, so this must agree with the firmware bit for bit.\n *\n * Hardened for scanned/untrusted input: size caps, bounds checks, and\n * forward-only references (the standard BoC topological order; a backward\n * reference would make the single-pass hash read an uncomputed child).\n */\n\nconst BOC_MAGIC = 0xb5ee9c72;\nconst MAX_CELLS = 256;\nconst MAX_CELL_DATA_BYTES = 128;\nconst MAX_REFS = 4;\n\ninterface Cell {\n dataBits: number;\n data: Uint8Array;\n refs: number[];\n depth: number;\n hash: Uint8Array;\n}\n\nfunction err(message: string): EraSdkError {\n return new EraSdkError('malformed-reply', `ton boc: ${message}`);\n}\n\n/** Representation hash of the ROOT cell of a BoC — the bytes TON signs. */\nexport function bocRootHash(boc: Uint8Array): Uint8Array {\n if (boc.length < 10) throw err('too short');\n const magic = ((boc[0]! << 24) | (boc[1]! << 16) | (boc[2]! << 8) | boc[3]!) >>> 0;\n if (magic !== BOC_MAGIC) throw err('not a generic BoC');\n\n const flags = boc[4]!;\n const hasIdx = (flags >> 7) & 1;\n const refSize = flags & 0x07;\n const offSize = boc[5]!;\n if (refSize === 0 || refSize > 4) throw err('bad ref size');\n if (offSize === 0 || offSize > 8) throw err('bad offset size');\n\n let pos = 6;\n const readInt = (byteLen: number): number => {\n let value = 0;\n for (let i = 0; i < byteLen; i++) {\n if (pos >= boc.length) throw err('truncated header');\n value = value * 256 + boc[pos++]!;\n }\n if (!Number.isSafeInteger(value)) throw err('header value out of range');\n return value;\n };\n\n const cellCount = readInt(refSize);\n const rootCount = readInt(refSize);\n readInt(refSize); // absent count\n readInt(offSize); // total cell data size\n if (rootCount === 0) throw err('no roots');\n if (cellCount === 0 || cellCount > MAX_CELLS) throw err('cell count out of range');\n\n const rootIndex = readInt(refSize);\n for (let i = 1; i < rootCount; i++) readInt(refSize); // remaining root indices\n if (rootIndex >= cellCount) throw err('root index out of range');\n if (hasIdx) pos += cellCount * offSize; // skip the offsets index\n\n const cells: Cell[] = [];\n for (let i = 0; i < cellCount; i++) {\n if (pos + 2 > boc.length) throw err('truncated cell');\n const d1 = boc[pos++]!;\n const d2 = boc[pos++]!;\n const refCount = d1 & 0x07;\n if (refCount > MAX_REFS) throw err('too many references');\n\n const dataByteLen = (d2 + 1) >> 1;\n const incomplete = (d2 & 1) === 1;\n if (dataByteLen > MAX_CELL_DATA_BYTES) throw err('cell data too large');\n if (pos + dataByteLen > boc.length) throw err('truncated cell data');\n const data = boc.slice(pos, pos + dataByteLen);\n pos += dataByteLen;\n\n // Bit length from the completion tag (last set bit marks the end).\n let dataBits: number;\n if (incomplete && dataByteLen > 0) {\n const last = data[dataByteLen - 1]!;\n if (last === 0) {\n dataBits = (dataByteLen - 1) * 8;\n } else {\n let trailingZeros = 0;\n for (let b = 0; b < 8; b++) {\n if (last & (1 << b)) break;\n trailingZeros++;\n }\n dataBits = dataByteLen * 8 - 1 - trailingZeros;\n }\n } else {\n dataBits = dataByteLen * 8;\n }\n\n const refs: number[] = [];\n for (let r = 0; r < refCount; r++) {\n const ref = readInt(refSize);\n if (ref >= cellCount) throw err('reference out of range');\n if (ref <= i) throw err('non-topological cell reference');\n refs.push(ref);\n }\n cells.push({ dataBits, data, refs, depth: 0, hash: new Uint8Array(0) });\n }\n\n // Bottom-up (children first — guaranteed by the forward-only reference check).\n for (let i = cellCount - 1; i >= 0; i--) {\n const cell = cells[i]!;\n let maxChildDepth = -1;\n for (const r of cell.refs) {\n maxChildDepth = Math.max(maxChildDepth, cells[r]!.depth);\n }\n cell.depth = cell.refs.length === 0 ? 0 : maxChildDepth + 1;\n\n const dataBytes = (cell.dataBits + 7) >> 3;\n const incomplete = cell.dataBits % 8 !== 0;\n const repr: number[] = [cell.refs.length, dataBytes * 2 - (incomplete ? 1 : 0)];\n for (let b = 0; b < dataBytes; b++) repr.push(cell.data[b] ?? 0);\n if (incomplete && dataBytes > 0) {\n const shift = 7 - (cell.dataBits % 8);\n const last = repr.length - 1;\n repr[last] = (repr[last]! | (1 << shift)) & (0xff << shift) & 0xff;\n }\n for (const r of cell.refs) {\n repr.push((cells[r]!.depth >> 8) & 0xff, cells[r]!.depth & 0xff);\n }\n let bytes: Uint8Array = new Uint8Array(repr);\n for (const r of cell.refs) bytes = concatBytes(bytes, cells[r]!.hash);\n cell.hash = sha256(bytes);\n }\n\n return cells[rootIndex]!.hash;\n}\n","import { ed25519 } from '@noble/curves/ed25519';\nimport { sha256 } from '@noble/hashes/sha2';\nimport { TonDataType } from '../chains/ton';\nimport { concatBytes, utf8Encode } from '../core/bytes';\nimport type { VerifyResult } from './result';\nimport { failed, verified } from './result';\nimport { bocRootHash } from './ton-boc';\n\nexport interface VerifyTonSignatureArgs {\n /** The exact bytes the request carried in `signData`. */\n readonly signData: Uint8Array;\n readonly dataType: TonDataType;\n /** 64-byte Ed25519 signature from the reply. */\n readonly signature: Uint8Array;\n /** The 32-byte signer public key from linking (`accounts.ton()`). */\n readonly publicKey: Uint8Array;\n}\n\n/**\n * Recompute the exact digest the device signs — the BoC ROOT CELL's\n * representation hash for a transaction, or the TON Connect proof digest\n * `sha256(0xFFFF || \"ton-connect\" || sha256(payload))` — and verify the\n * Ed25519 signature against the linked key.\n */\nexport function verifyTonSignature(args: VerifyTonSignatureArgs): VerifyResult {\n if (args.signature.length !== 64) return failed('signature must be 64 bytes');\n if (args.publicKey.length !== 32) return failed('public key must be 32 bytes');\n\n let digest: Uint8Array;\n if (args.dataType === TonDataType.tonProof) {\n digest = sha256(\n concatBytes(new Uint8Array([0xff, 0xff]), utf8Encode('ton-connect'), sha256(args.signData)),\n );\n } else if (args.dataType === TonDataType.transaction) {\n try {\n digest = bocRootHash(args.signData);\n } catch (e) {\n return failed(`signData is not a readable BoC: ${(e as Error).message}`);\n }\n } else {\n return failed(`unknown dataType ${args.dataType satisfies never}`);\n }\n\n let ok: boolean;\n try {\n ok = ed25519.verify(args.signature, digest, args.publicKey);\n } catch (e) {\n return failed(`TON signature could not be checked: ${(e as Error).message}`);\n }\n return ok ? verified : failed('the signature does not belong to this account');\n}\n","import { secp256k1 } from '@noble/curves/secp256k1';\nimport { sha256 } from '@noble/hashes/sha2';\nimport { keccak_256 } from '@noble/hashes/sha3';\nimport { createBase58check } from '@scure/base';\nimport { concatBytes, equalBytes, utf8Decode } from '../core/bytes';\nimport type { SignedTronTx, TronLatestBlock } from '../tron-proto/messages';\nimport { splitSignedTronTx } from '../tron-proto/messages';\nimport { firstBytes, firstVarint, readFields } from '../tron-proto/wire';\nimport type { VerifyResult } from './result';\nimport { failed, verified } from './result';\n\nconst base58check = createBase58check(sha256);\n\nexport interface VerifyTronSignatureArgs {\n /** The `raw_data` bytes the request carried. */\n readonly rawData: Uint8Array;\n /** The base58 owner address the transaction spends from. */\n readonly from: string;\n /** The reference block the request carried (enables the rebuild-path window check). */\n readonly latestBlock?: TronLatestBlock;\n /** The reply: either the split frame from `TronSignatureResult.signedTx`, or the raw hex. */\n readonly signedTx: SignedTronTx | string;\n}\n\n/**\n * The Tron reply is a fully signed transaction broadcast VERBATIM, so it is\n * checked on both counts: every signature must recover to the owner address,\n * and the transaction must move what the user approved.\n *\n * Byte equality with the request's `rawData` is the strong form. When the\n * bytes differ (a firmware that rebuilds `raw_data` from the semantic\n * fields), the fallback compares the fields that decide where the money goes,\n * plus the validity window against the reference block.\n */\nexport function verifyTronSignature(args: VerifyTronSignatureArgs): VerifyResult {\n let signedTx: SignedTronTx;\n try {\n signedTx = typeof args.signedTx === 'string' ? splitSignedTronTx(args.signedTx) : args.signedTx;\n } catch (e) {\n return failed(`the returned Tron transaction is not readable: ${(e as Error).message}`);\n }\n if (signedTx.signatures.length === 0) {\n return failed('the returned Tron transaction carries no signature');\n }\n if (!args.from) {\n return failed('no owner address to check the signature against');\n }\n\n const digest = sha256(signedTx.rawData);\n for (const signature of signedTx.signatures) {\n const recovered = recoverTronAddress(digest, signature);\n if (recovered === null) return failed('signature could not be checked');\n if (recovered !== args.from) {\n return failed('the signature does not belong to this account');\n }\n }\n\n if (equalBytes(signedTx.rawData, args.rawData)) return verified;\n\n // Rebuild path: the firmware built its own raw_data from the semantic\n // fields. Compare the operation, then the validity window.\n const contractResult = compareContracts(args.rawData, signedTx.rawData);\n if (contractResult !== null) return contractResult;\n if (args.latestBlock === undefined) {\n return failed(\n 'the returned raw_data differs from the request and no latestBlock was provided to check the validity window',\n );\n }\n return compareWindow(signedTx.rawData, args.latestBlock);\n}\n\nfunction recoverTronAddress(digest: Uint8Array, signature: Uint8Array): string | null {\n if (signature.length < 65) return null;\n const recovery = signature[64]!;\n const recoveryId = recovery >= 27 ? (recovery - 27) & 1 : recovery & 1;\n try {\n const point = secp256k1.Signature.fromCompact(signature.slice(0, 64))\n .addRecoveryBit(recoveryId)\n .recoverPublicKey(digest);\n const hash = keccak_256(point.toRawBytes(false).slice(1));\n return base58check.encode(concatBytes(new Uint8Array([0x41]), hash.slice(12)));\n } catch {\n return null;\n }\n}\n\n// --- raw_data structural comparison ---------------------------------------\n\ninterface TronContract {\n readonly typeUrl: string;\n readonly type: bigint;\n readonly parameter: Uint8Array;\n}\n\ninterface TronRawData {\n readonly contracts: readonly TronContract[];\n readonly expiration: bigint;\n readonly timestamp: bigint;\n}\n\nfunction parseRawData(bytes: Uint8Array): TronRawData | null {\n try {\n const fields = readFields(bytes);\n const contracts: TronContract[] = [];\n for (const f of fields) {\n if (f.field === 11 && f.wireType === 2) {\n const c = readFields(f.bytes);\n const anyBytes = firstBytes(c, 2);\n const any = anyBytes ? readFields(anyBytes) : [];\n const typeUrlBytes = anyBytes ? firstBytes(any, 1) : null;\n contracts.push({\n typeUrl: typeUrlBytes ? utf8Decode(typeUrlBytes) : '',\n type: firstVarint(c, 1) ?? 0n,\n parameter: (anyBytes && firstBytes(any, 2)) || new Uint8Array(0),\n });\n }\n }\n return {\n contracts,\n expiration: firstVarint(fields, 8) ?? 0n,\n timestamp: firstVarint(fields, 14) ?? 0n,\n };\n } catch {\n return null;\n }\n}\n\n/** null = contracts match (continue to the window check); a result = verdict. */\nfunction compareContracts(askedBytes: Uint8Array, repliedBytes: Uint8Array): VerifyResult | null {\n const asked = parseRawData(askedBytes);\n const replied = parseRawData(repliedBytes);\n if (!asked) return failed('the Tron transaction built by the app could not be read');\n if (!replied) return failed('the Tron transaction built by the device could not be read');\n if (asked.contracts.length !== 1 || replied.contracts.length !== 1) {\n return failed('the Tron transaction does not carry exactly one contract');\n }\n const a = asked.contracts[0]!;\n const b = replied.contracts[0]!;\n const kindA = contractKind(a);\n const kindB = contractKind(b);\n if (kindA !== kindB) {\n return failed('the returned Tron transaction is a different kind of operation');\n }\n\n const pa = readFieldsSafe(a.parameter);\n const pb = readFieldsSafe(b.parameter);\n if (!pa || !pb) return failed('the Tron contract parameters could not be read');\n\n switch (kindA) {\n case 'transfer':\n // TransferContract {1: owner, 2: to, 3: amount}\n if (\n sameBytesField(pa, pb, 1) &&\n sameBytesField(pa, pb, 2) &&\n (firstVarint(pa, 3) ?? 0n) === (firstVarint(pb, 3) ?? 0n)\n ) {\n return null;\n }\n break;\n case 'transferAsset':\n // TransferAssetContract {1: asset, 2: owner, 3: to, 4: amount}\n if (\n sameBytesField(pa, pb, 1) &&\n sameBytesField(pa, pb, 2) &&\n sameBytesField(pa, pb, 3) &&\n (firstVarint(pa, 4) ?? 0n) === (firstVarint(pb, 4) ?? 0n)\n ) {\n return null;\n }\n break;\n case 'triggerSmartContract':\n // TriggerSmartContract {1: owner, 2: contract, 3: call_value, 4: data}.\n // call_value: zero and absent are the same thing — the two sides are\n // serialized by different protobuf writers that disagree on whether a\n // zero scalar is written, and every TRC-20 transfer carries call_value 0.\n if (\n sameBytesField(pa, pb, 1) &&\n sameBytesField(pa, pb, 2) &&\n (firstVarint(pa, 3) ?? 0n) === (firstVarint(pb, 3) ?? 0n) &&\n sameBytesField(pa, pb, 4)\n ) {\n return null;\n }\n break;\n default:\n // An operation this gate cannot compare field by field is not waved\n // through: without byte equality there is nothing left to bind it to\n // what the user approved.\n return failed('the returned Tron transaction carries an operation this check cannot compare');\n }\n return failed('the returned Tron transaction does not match the one approved');\n}\n\ntype ContractKind = 'transfer' | 'transferAsset' | 'triggerSmartContract' | 'other';\n\nfunction contractKind(contract: TronContract): ContractKind {\n if (contract.typeUrl.endsWith('.TransferContract') || contract.type === 1n) return 'transfer';\n if (contract.typeUrl.endsWith('.TransferAssetContract') || contract.type === 2n) {\n return 'transferAsset';\n }\n if (contract.typeUrl.endsWith('.TriggerSmartContract') || contract.type === 31n) {\n return 'triggerSmartContract';\n }\n return 'other';\n}\n\nfunction readFieldsSafe(bytes: Uint8Array): ReturnType<typeof readFields> | null {\n try {\n return readFields(bytes);\n } catch {\n return null;\n }\n}\n\nfunction sameBytesField(\n a: ReturnType<typeof readFields>,\n b: ReturnType<typeof readFields>,\n field: number,\n): boolean {\n const x = firstBytes(a, field) ?? new Uint8Array(0);\n const y = firstBytes(b, field) ?? new Uint8Array(0);\n return equalBytes(x, y);\n}\n\n/**\n * Firmware formulas, not policy: the two device-side transaction builders\n * stamp `timestamp` with the request's reference-block timestamp verbatim and\n * set `expiration` to +10 minutes (one builder) or +10 hours (the other). A\n * range spanning both cannot refuse a reply the live fleet produces.\n */\nconst MIN_EXPIRY_MS = 10n * 60n * 1000n;\nconst MAX_EXPIRY_MS = 10n * 60n * 60n * 1000n;\n\nfunction compareWindow(repliedBytes: Uint8Array, latestBlock: TronLatestBlock): VerifyResult {\n const replied = parseRawData(repliedBytes);\n if (!replied) return failed('the Tron transaction built by the device could not be read');\n const block = BigInt(latestBlock.timestamp);\n if (replied.timestamp !== block) {\n return failed(\n 'the returned Tron transaction is stamped against a different reference block than the one we sent',\n );\n }\n const validFor = replied.expiration - block;\n if (validFor < MIN_EXPIRY_MS || validFor > MAX_EXPIRY_MS) {\n return failed(\n `the returned Tron transaction is valid for ${validFor} ms after the reference block, outside the firmware's ${MIN_EXPIRY_MS}-${MAX_EXPIRY_MS} ms window`,\n );\n }\n return verified;\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,MAAM,QAAQ;CAAC;CAAM;CAAM;CAAM;CAAM;AAAI;AAE3C,MAAa,gBAAgB;CAC3B,YAAY;CACZ,gBAAgB;CAChB,oBAAoB;CACpB,0BAA0B;CAC1B,6BAA6B;AAC/B;AAEA,SAASA,MAAI,SAA8B;CACzC,OAAO,IAAI,YAAY,mBAAmB,SAAS,SAAS;AAC9D;AAEA,IAAM,SAAN,MAAa;CAEX,YAAY,OAA4B;EAAnB,KAAA,QAAA;EADrB,KAAA,SAAS;CACgC;CAEzC,IAAI,YAAoB;EACtB,OAAO,KAAK,MAAM,SAAS,KAAK;CAClC;CAEA,KAAa;EACX,MAAM,IAAI,KAAK,MAAM,KAAK;EAC1B,IAAI,MAAM,KAAA,GAAW,MAAMA,MAAI,WAAW;EAC1C,KAAK,UAAU;EACf,OAAO;CACT;;CAGA,cAAsB;EACpB,MAAM,QAAQ,KAAK,GAAG;EACtB,IAAI,QAAQ,KAAM,OAAO;EACzB,IAAI;EACJ,IAAI;EACJ,IAAI,UAAU,KAAM;GAClB,QAAQ;GACR,UAAU;EACZ,OAAO,IAAI,UAAU,KAAM;GACzB,QAAQ;GACR,UAAU;EACZ,OAAO;GACL,QAAQ;GACR,UAAU;EACZ;EACA,IAAI,QAAQ;EACZ,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK,SAAS,OAAO,KAAK,GAAG,CAAC,KAAK,OAAO,IAAI,CAAC;EAC1E,IAAI,QAAQ,SAAS,MAAMA,MAAI,mCAAmC;EAClE,IAAI,QAAQ,OAAO,OAAO,gBAAgB,GAAG,MAAMA,MAAI,2BAA2B;EAClF,OAAO,OAAO,KAAK;CACrB;CAEA,KAAK,QAA4B;EAC/B,IAAI,SAAS,KAAK,WAAW,MAAMA,MAAI,sBAAsB;EAC7D,MAAM,MAAM,KAAK,MAAM,MAAM,KAAK,QAAQ,KAAK,SAAS,MAAM;EAC9D,KAAK,UAAU;EACf,OAAO;CACT;AACF;;AAGA,SAAS,QAAQ,QAAgC;CAC/C,MAAM,UAA0B,CAAC;CACjC,MAAM,uBAAO,IAAI,IAAY;CAC7B,SAAS;EACP,MAAM,YAAY,OAAO,YAAY;EACrC,IAAI,cAAc,GAAG,OAAO;EAC5B,MAAM,MAAM,OAAO,KAAK,SAAS;EACjC,MAAM,QAAQ,OAAO,KAAK,OAAO,YAAY,CAAC;EAC9C,MAAM,QAAQ,MAAM,KAAK,GAAG,CAAC,CAC1B,KAAK,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAC3C,KAAK,EAAE;EACV,IAAI,KAAK,IAAI,KAAK,GAAG,MAAMA,MAAI,8BAA8B;EAC7D,KAAK,IAAI,KAAK;EACd,QAAQ,KAAK;GAAE,SAAS,IAAI;GAAK,SAAS,IAAI,MAAM,CAAC;GAAG;EAAM,CAAC;CACjE;AACF;;AAGA,SAAS,qBAAqB,IAAqD;CACjF,MAAM,SAAS,IAAI,OAAO,EAAE;CAC5B,OAAO,KAAK,CAAC;CACb,MAAM,SAAS,OAAO,YAAY;CAClC,IAAI,WAAW,GAGb,MAAMA,MAAI,gEAAgE;CAE5E,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK;EAC/B,OAAO,KAAK,EAAM;EAClB,OAAO,KAAK,OAAO,YAAY,CAAC;EAChC,OAAO,KAAK,CAAC;CACf;CACA,MAAM,UAAU,OAAO,YAAY;CACnC,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,KAAK;EAChC,OAAO,KAAK,CAAC;EACb,OAAO,KAAK,OAAO,YAAY,CAAC;CAClC;CACA,OAAO,KAAK,CAAC;CACb,IAAI,OAAO,cAAc,GAAG,MAAMA,MAAI,+CAA+C;CACrF,OAAO;EAAE;EAAQ;CAAQ;AAC3B;AAEA,SAAgB,UAAU,OAA+B;CACvD,MAAM,SAAS,IAAI,OAAO,KAAK;CAC/B,KAAK,MAAM,YAAY,OACrB,IAAI,OAAO,GAAG,MAAM,UAAU,MAAMA,MAAI,WAAW;CAErD,MAAM,YAAY,QAAQ,MAAM;CAEhC,IAAI,aAAgC;CACpC,IAAI,UAAU;CACd,KAAK,MAAM,SAAS,WAAW;EAC7B,IAAI,MAAM,YAAY,KAAQ,MAAM,QAAQ,WAAW,GAAG,aAAa,MAAM;EAC7E,IAAI,MAAM,YAAY,OAAQ,MAAM,QAAQ,WAAW,GAAG;GACxD,IAAI,MAAM,MAAM,WAAW,GAAG,MAAMA,MAAI,mBAAmB;GAC3D,WACG,MAAM,MAAM,KACV,MAAM,MAAM,MAAO,IACnB,MAAM,MAAM,MAAO,KACnB,MAAM,MAAM,MAAO,QACtB;EACJ;CACF;CACA,IAAI,eAAe,MAGjB,MAAMA,MAAI,gDAAgD;CAE5D,IAAI,YAAY,GAAG,MAAMA,MAAI,4BAA4B,SAAS;CAElE,MAAM,SAAS,qBAAqB,UAAU;CAC9C,MAAM,SAA2B,CAAC;CAClC,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,OAAO,KAAK,QAAQ,MAAM,CAAC;CACnE,MAAM,UAA4B,CAAC;CACnC,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,SAAS,KAAK,QAAQ,KAAK,QAAQ,MAAM,CAAC;CACrE,IAAI,OAAO,cAAc,GAAG,MAAMA,MAAI,sCAAsC;CAE5E,OAAO;EAAE;EAAY;EAAS;EAAQ;CAAQ;AAChD;AAEA,SAAgB,aACd,MACA,OACA,SACyB;CACzB,QAAQ,KAAK,OAAO,UAAU,CAAC,EAAA,CAAG,QAAQ,MAAM,EAAE,YAAY,OAAO;AACvE;AAEA,SAAgB,SAAS,MAAkB,OAAe,SAA0B;CAClF,OAAO,aAAa,MAAM,OAAO,OAAO,CAAC,CAAC,SAAS;AACrD;;;ACtKA,MAAa,WAAyB;CAAE,IAAI;CAAM,SAAS;AAAK;AAChE,MAAa,UAAU,YAAkC;CAAE,IAAI;CAAO;AAAO;AAC7E,MAAa,gBAAgB,YAAkC;CAC7D,IAAI;CACJ,SAAS;CACT;AACF;;;;;;;;;;;;ACaA,SAAgB,iBAAiB,MAA0C;CACzE,IAAI;CACJ,IAAI;CACJ,IAAI;EACF,OAAO,UAAU,KAAK,QAAQ;CAChC,SAAS,GAAG;EACV,OAAO,OAAO,qCAAqC,QAAQ,CAAC,GAAG;CACjE;CACA,IAAI;EACF,SAAS,UAAU,KAAK,UAAU;CACpC,SAAS,GAAG;EACV,OAAO,OAAO,iDAAiD,QAAQ,CAAC,GAAG;CAC7E;CAEA,IAAI,CAAC,WAAW,KAAK,YAAY,OAAO,UAAU,GAChD,OAAO,OAAO,oEAAoE;CAQpF,MAAM,iBAAiB,CAAC,cAAc,gBAAgB,cAAc,kBAAkB;CACtF,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,OAAO,QAAQ,KACxC,KAAK,MAAM,QAAQ,gBAAgB;EACjC,IAAI,CAAC,SAAS,QAAQ,GAAG,IAAI,GAAG;EAChC,IAAI,CAAC,SAAS,MAAM,GAAG,IAAI,GACzB,OAAO,OACL,SAAS,EAAE,+BAA+B,KAAK,SAAS,EAAE,EAAE,wEAC9D;EAEF,MAAM,IAAI,aAAa,MAAM,GAAG,IAAI;EACpC,MAAM,IAAI,aAAa,QAAQ,GAAG,IAAI;EACtC,IAAI,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,OAAO,OAAO,MAAM,WAAW,MAAM,OAAO,EAAE,EAAE,CAAE,KAAK,CAAC,GACtF,OAAO,OACL,SAAS,EAAE,kEACb;CAEJ;CAGF,MAAM,YAAY,MAChB,SAAS,QAAQ,GAAG,cAAc,UAAU,KAC5C,SAAS,QAAQ,GAAG,cAAc,wBAAwB,KAC1D,SAAS,QAAQ,GAAG,cAAc,2BAA2B;CAE/D,MAAM,UAAU,OAAO,OAAO,KAAK,GAAG,MAAM,CAAC;CAC7C,IAAI,KAAK,2BAA2B,MAC9B;MAAA,CAAC,QAAQ,MAAM,QAAQ,GACzB,OAAO,OAAO,gDAAgD;CAAA,OAE3D,IAAI,CAAC,QAAQ,KAAK,QAAQ,GAC/B,OAAO,OAAO,wCAAwC;CAExD,OAAO;AACT;;;;;;;AAeA,SAAgB,uBAAuB,MAAgD;CACrF,IAAI,KAAK,UAAU,WAAW,GAC5B,OAAO,OAAO,iBAAiB;CAEjC,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,QAAQ,eAAe,KAAK,OAAO;CACzC,IAAI,UAAU,MACZ,OAAO;EACL,IAAI;EACJ,SAAS;EACT,QAAQ;CACV;CAEF,IAAI,UAAU,MAAM,OAAO,UAAU,MAAM,MAAM,OAAO;CACxD,OAAO,OACL,mBAAmB,OAAO,oBAAoB,MAAM,MAAM,4BACpC,MAAM,IAAI,IAAI,MAAM,KAAK,gFACjD;AACF;AAQA,SAAS,eAAe,SAAqC;CAC3D,MAAM,IAAI,QAAQ,YAAY;CAC9B,IAAI,EAAE,WAAW,MAAM,KAAK,EAAE,WAAW,MAAM,KAAK,EAAE,WAAW,QAAQ,GACvE,OAAO;EAAE,KAAK;EAAI,MAAM;EAAI,OAAO;CAAyB;CAE9D,IAAI,EAAE,WAAW,MAAM,KAAK,EAAE,WAAW,MAAM,KAAK,EAAE,WAAW,QAAQ,GACvE,OAAO;CAKT,IAAI,YAAY,OAAO,GAAG;EACxB,IAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,GAAG,GACnD,OAAO;GAAE,KAAK;GAAI,MAAM;GAAI,OAAO;EAAuB;EAE5D,IAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,GAAG,GAG9E,OAAO;GAAE,KAAK;GAAI,MAAM;GAAI,OAAO;EAAe;CAEtD;CACA,OAAO;AACT;AAEA,SAAS,YAAY,SAA0B;CAC7C,OAAO,QAAQ,UAAU,MAAM,QAAQ,UAAU,MAAM,0BAA0B,KAAK,OAAO;AAC/F;AAEA,SAAS,QAAQ,GAAoB;CACnC,OAAO,aAAa,eAAe,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAC9E;;;;;;;;;;ACvHA,SAAgB,uBAAuB,MAAgD;CACrF,IAAI;CACJ,IAAI;EACF,YAAY,KAAK,cAAc,KAAK,aAAa,gBAAgB,KAAK,UAAU,IAAI,CAAC;CACvF,SAAS,GAAG;EACV,OAAO,OAAO,gCAAiC,EAAY,SAAS;CACtE;CACA,IAAI,UAAU,WAAW,GAAG,OAAO,OAAO,wBAAwB;CAElE,IAAI;CACJ,IAAI;EACF,SAAS,QAAQ,oBAAoB,KAAK,QAAQ,GAAG,EAAE,OAAO,GAAG,CAAC;CACpE,SAAS,GAAG;EACV,OAAO,OAAO,iDAAkD,EAAY,SAAS;CACvF;CAEA,KAAK,MAAM,WAAW,WAAW;EAC/B,IAAI;EACJ,IAAI;GACF,KAAK,QAAQ,OAAO,QAAQ,WAAW,QAAQ,QAAQ,IAAI;EAC7D,SAAS,GAAG;GACV,OAAO,OAAO,2CAA4C,EAAY,SAAS;EACjF;EACA,IAAI,CAAC,IAAI,OAAO,OAAO,0DAA0D;CACnF;CAEA,IAAI,KAAK,WAAW,KAAK,eAAe,KAAK,YAAY,SAAS,GAAG;EACnE,MAAM,gBAAgB,UAAU,KAAK,QAAQ,WAAW;EACxD,MAAM,2BAAW,IAAI,IAAoB;EACzC,KAAK,MAAM,QAAQ,IAAI,IAAI,KAAK,WAAW,GAAG;GAC5C,MAAM,SAAS,UAAU,IAAI;GAC7B,IACE,OAAO,WAAW,cAAc,SAAS,KACzC,CAAC,cAAc,OACZ,GAAG,MAAM,OAAO,EAAE,CAAE,UAAU,EAAE,SAAS,OAAO,EAAE,CAAE,aAAa,EAAE,QACtE,KACA,OAAO,MAAM,cAAc,MAAM,CAAC,CAAC,MAAM,MAAM,EAAE,QAAQ,GAEzD,OAAO,OACL,eAAe,KAAK,2DACtB;GAEF,MAAM,OAAO,OAAO,MAAM,cAAc,MAAM,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK;GAClE,MAAM,OAAO,sBAAsB,KAAK,QAAQ,WAAW,KAAK,QAAQ,WAAW,IAAI;GACvF,SAAS,IAAI,WAAW,IAAI,GAAG,IAAI;EACrC;EAEA,KAAK,MAAM,CAAC,SAAS,SAAS,UAC5B,IAAI,CAAC,UAAU,MAAM,MAAM,WAAW,EAAE,IAAI,MAAM,OAAO,GACvD,OAAO,OAAO,4CAA4C,MAAM;EAIpE,KAAK,MAAM,WAAW,WACpB,IAAI,CAAC,SAAS,IAAI,WAAW,QAAQ,IAAI,CAAC,GACxC,OAAO,OAAO,4DAA4D;CAGhF;CACA,OAAO;AACT;AAQA,SAAgB,oBAAoB,OAA+B;CACjE,MAAM,MAAM,MAAM;CAClB,IAAI,QAAQ,KAAA,GAAW,MAAM,IAAI,MAAM,aAAa;CAEpD,IADc,OAAO,MACP,GAAG,MAAM,IAAI,MAAM,kBAAkB;CACnD,IAAI;CACJ,KAAK,MAAM,QAAU,IACnB,QAAQ;MACH;EACL,MAAM,OAAO,SAAS,OAAO,CAAC;EAC9B,IAAI,KAAK,UAAU,IAAI,MAAM,IAAI,MAAM,4BAA4B;EACnE,QAAQ,KAAK;CACf;CACA,MAAM,MAAM,SAAS,OAAO,OAAO,CAAC;CACpC,OAAO,MAAM,MAAM,OAAO,GAAG;AAC/B;AAEA,SAAS,SAAS,OAAmB,QAAiD;CACpF,MAAM,UAAU,MAAM;CACtB,IAAI,YAAY,KAAA,GAAW,MAAM,IAAI,MAAM,WAAW;CACtD,MAAM,OAAO,UAAU;CACvB,IAAI,OAAO,IAAI,OAAO;EAAE,OAAO,OAAO,IAAI;EAAG,MAAM,SAAS;CAAE;CAC9D,IAAI,SAAS,IAAI,OAAO;EAAE,OAAO,CAAC;EAAI,MAAM,SAAS;CAAE;CACvD,IAAI;CACJ,IAAI,SAAS,IAAI,QAAQ;MACpB,IAAI,SAAS,IAAI,QAAQ;MACzB,IAAI,SAAS,IAAI,QAAQ;MACzB,IAAI,SAAS,IAAI,QAAQ;MACzB,MAAM,IAAI,MAAM,0BAA0B;CAC/C,IAAI,QAAQ;CACZ,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;EAC9B,MAAM,IAAI,MAAM,SAAS,IAAI;EAC7B,IAAI,MAAM,KAAA,GAAW,MAAM,IAAI,MAAM,WAAW;EAChD,QAAS,SAAS,KAAM,OAAO,CAAC;CAClC;CACA,OAAO;EAAE;EAAO,MAAM,SAAS,IAAI;CAAM;AAC3C;;AAGA,SAAS,SAAS,OAAmB,QAAgB,OAAuB;CAC1E,IAAI,QAAQ,IAAI,MAAM,IAAI,MAAM,kBAAkB;CAClD,MAAM,UAAU,MAAM;CACtB,IAAI,YAAY,KAAA,GAAW,MAAM,IAAI,MAAM,WAAW;CACtD,MAAM,QAAQ,WAAW;CACzB,MAAM,OAAO,SAAS,OAAO,MAAM;CAEnC,QAAQ,OAAR;EACE,KAAK;EACL,KAAK;GACH,IAAI,KAAK,UAAU,CAAC,IAAI,MAAM,IAAI,MAAM,mBAAmB;GAC3D,OAAO,KAAK;EACd,KAAK;EACL,KAAK,GAAG;GACN,IAAI,KAAK,UAAU,CAAC,IAAI;IAEtB,IAAI,MAAM,KAAK;IACf,OAAO,MAAM,SAAS,KAAM;KAC1B,MAAM,QAAQ,SAAS,OAAO,GAAG;KACjC,IAAI,MAAM,QAAQ,IAAI,MAAM,IAAI,MAAM,iBAAiB;KACvD,MAAM,MAAM,OAAO,OAAO,MAAM,KAAK;KACrC,IAAI,MAAM,MAAM,QAAQ,MAAM,IAAI,MAAM,kBAAkB;IAC5D;IACA,OAAO,MAAM;GACf;GACA,MAAM,MAAM,KAAK,OAAO,OAAO,KAAK,KAAK;GACzC,IAAI,MAAM,MAAM,QAAQ,MAAM,IAAI,MAAM,kBAAkB;GAC1D,OAAO;EACT;EACA,KAAK;EACL,KAAK,GAAG;GACN,MAAM,WAAW,UAAU,IAAI,IAAI;GACnC,IAAI,KAAK,UAAU,CAAC,IAAI;IACtB,IAAI,MAAM,KAAK;IACf,OAAO,MAAM,SAAS,KACpB,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAAK,MAAM,SAAS,OAAO,KAAK,QAAQ,CAAC;IAEzE,OAAO,MAAM;GACf;GACA,IAAI,MAAM,KAAK;GACf,MAAM,QAAQ,OAAO,KAAK,KAAK,IAAI;GACnC,IAAI,QAAQ,KAAW,MAAM,IAAI,MAAM,qBAAqB;GAC5D,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK,MAAM,SAAS,OAAO,KAAK,QAAQ,CAAC;GACpE,OAAO;EACT;EACA,KAAK;GACH,IAAI,KAAK,UAAU,CAAC,IAAI,MAAM,IAAI,MAAM,eAAe;GACvD,OAAO,SAAS,OAAO,KAAK,MAAM,QAAQ,CAAC;EAC7C,KAAK;GACH,KAAK,UAAU,QAAU,IAAI,MAAM,IAAI,MAAM,kBAAkB;GAC/D,KAAK,UAAU,QAAU,IAAI,OAAO,SAAS;GAC7C,KAAK,UAAU,QAAU,IAAI,OAAO,SAAS;GAC7C,KAAK,UAAU,QAAU,IAAI,OAAO,SAAS;GAC7C,OAAO,KAAK;EACd,SACE,MAAM,IAAI,MAAM,aAAa;CACjC;AACF;;;;ACjLA,SAAS,mBAAmB,SAAiC;CAC3D,MAAM,SAAS,WAAW,6BAA6B,QAAQ,QAAQ;CACvE,OAAO,WAAW,YAAY,IAAI,WAAW,CAAC,EAAI,CAAC,GAAG,QAAQ,OAAO,CAAC;AACxE;;;;;;AAOA,SAAgB,mBAAmB,MAA4C;CAC7E,IAAI,KAAK,UAAU,SAAS,IAC1B,OAAO,OAAO,oCAAoC;CAEpD,IAAI,KAAK,sBAAsB,KAAA,KAAa,CAAC,WAAW,KAAK,mBAAmB,KAAK,QAAQ,GAC3F,OAAO,OAAO,+DAA+D;CAG/E,IAAI;CACJ,QAAQ,KAAK,UAAb;EACE,KAAK,YAAY;EACjB,KAAK,YAAY;GACf,SAAS,WAAW,KAAK,QAAQ;GACjC;EACF,KAAK,YAAY;GACf,SAAS,mBAAmB,KAAK,QAAQ;GACzC;EACF,KAAK,YAAY,WACf,OAAO,aACL,+EACF;EACF,SACE,OAAO,OAAO,oBAAoB,KAAK,UAA0B;CACrE;CAEA,IAAI,OAAO;CACX,KAAK,MAAM,KAAK,KAAK,UAAU,MAAM,EAAE,GAAG,OAAQ,QAAQ,KAAM,OAAO,CAAC;CACxE,MAAM,aAAa,eAAe,IAAI;CACtC,IAAI,eAAe,KAAK,eAAe,GACrC,OAAO,OAAO,4BAA4B;CAG5C,IAAI;CACJ,IAAI;EACF,MAAM,QAAQ,UAAU,UAAU,YAAY,KAAK,UAAU,MAAM,GAAG,EAAE,CAAC,CAAC,CACvE,eAAe,UAAU,CAAC,CAC1B,iBAAiB,MAAM;EAC1B,YAAY,WAAW,MAAM,WAAW,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;CACnE,SAAS,GAAG;EACV,OAAO,OAAO,mCAAoC,EAAY,SAAS;CACzE;CAEA,MAAM,WAAW,KAAK,mBAAmB,aAAa,KAAK,UAAU,WAAW,KAAK,OAAO;CAC5F,IAAI,SAAS,WAAW,IACtB,OAAO,OAAO,0CAA0C,SAAS,QAAQ;CAE3E,IAAI,CAAC,WAAW,WAAW,QAAQ,GACjC,OAAO,OACL,8DAA8D,WAAW,SAAS,EAAE,EACtF;CAEF,OAAO;AACT;;;ACpEA,SAAgB,sBAAsB,MAA+C;CACnF,IACE,KAAK,0BAA0B,KAAA,KAC/B,CAAC,WAAW,KAAK,uBAAuB,KAAK,QAAQ,GAErD,OAAO,OAAO,2DAA2D;CAE3E,IAAI;CACJ,IAAI;EACF,KAAK,QAAQ,OAAO,KAAK,WAAW,KAAK,UAAU,KAAK,SAAS;CACnE,SAAS,GAAG;EACV,OAAO,OAAO,0CAA2C,EAAY,SAAS;CAChF;CACA,OAAO,KAAK,WAAW,OAAO,+CAA+C;AAC/E;;;;;;;;;;;;;ACnBA,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,sBAAsB;AAC5B,MAAM,WAAW;AAUjB,SAAS,IAAI,SAA8B;CACzC,OAAO,IAAI,YAAY,mBAAmB,YAAY,SAAS;AACjE;;AAGA,SAAgB,YAAY,KAA6B;CACvD,IAAI,IAAI,SAAS,IAAI,MAAM,IAAI,WAAW;CAE1C,KADgB,IAAI,MAAO,KAAO,IAAI,MAAO,KAAO,IAAI,MAAO,IAAK,IAAI,QAAS,MACnE,WAAW,MAAM,IAAI,mBAAmB;CAEtD,MAAM,QAAQ,IAAI;CAClB,MAAM,SAAU,SAAS,IAAK;CAC9B,MAAM,UAAU,QAAQ;CACxB,MAAM,UAAU,IAAI;CACpB,IAAI,YAAY,KAAK,UAAU,GAAG,MAAM,IAAI,cAAc;CAC1D,IAAI,YAAY,KAAK,UAAU,GAAG,MAAM,IAAI,iBAAiB;CAE7D,IAAI,MAAM;CACV,MAAM,WAAW,YAA4B;EAC3C,IAAI,QAAQ;EACZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,KAAK;GAChC,IAAI,OAAO,IAAI,QAAQ,MAAM,IAAI,kBAAkB;GACnD,QAAQ,QAAQ,MAAM,IAAI;EAC5B;EACA,IAAI,CAAC,OAAO,cAAc,KAAK,GAAG,MAAM,IAAI,2BAA2B;EACvE,OAAO;CACT;CAEA,MAAM,YAAY,QAAQ,OAAO;CACjC,MAAM,YAAY,QAAQ,OAAO;CACjC,QAAQ,OAAO;CACf,QAAQ,OAAO;CACf,IAAI,cAAc,GAAG,MAAM,IAAI,UAAU;CACzC,IAAI,cAAc,KAAK,YAAY,WAAW,MAAM,IAAI,yBAAyB;CAEjF,MAAM,YAAY,QAAQ,OAAO;CACjC,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK,QAAQ,OAAO;CACnD,IAAI,aAAa,WAAW,MAAM,IAAI,yBAAyB;CAC/D,IAAI,QAAQ,OAAO,YAAY;CAE/B,MAAM,QAAgB,CAAC;CACvB,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK;EAClC,IAAI,MAAM,IAAI,IAAI,QAAQ,MAAM,IAAI,gBAAgB;EACpD,MAAM,KAAK,IAAI;EACf,MAAM,KAAK,IAAI;EACf,MAAM,WAAW,KAAK;EACtB,IAAI,WAAW,UAAU,MAAM,IAAI,qBAAqB;EAExD,MAAM,cAAe,KAAK,KAAM;EAChC,MAAM,cAAc,KAAK,OAAO;EAChC,IAAI,cAAc,qBAAqB,MAAM,IAAI,qBAAqB;EACtE,IAAI,MAAM,cAAc,IAAI,QAAQ,MAAM,IAAI,qBAAqB;EACnE,MAAM,OAAO,IAAI,MAAM,KAAK,MAAM,WAAW;EAC7C,OAAO;EAGP,IAAI;EACJ,IAAI,cAAc,cAAc,GAAG;GACjC,MAAM,OAAO,KAAK,cAAc;GAChC,IAAI,SAAS,GACX,YAAY,cAAc,KAAK;QAC1B;IACL,IAAI,gBAAgB;IACpB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;KAC1B,IAAI,OAAQ,KAAK,GAAI;KACrB;IACF;IACA,WAAW,cAAc,IAAI,IAAI;GACnC;EACF,OACE,WAAW,cAAc;EAG3B,MAAM,OAAiB,CAAC;EACxB,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAAK;GACjC,MAAM,MAAM,QAAQ,OAAO;GAC3B,IAAI,OAAO,WAAW,MAAM,IAAI,wBAAwB;GACxD,IAAI,OAAO,GAAG,MAAM,IAAI,gCAAgC;GACxD,KAAK,KAAK,GAAG;EACf;EACA,MAAM,KAAK;GAAE;GAAU;GAAM;GAAM,OAAO;GAAG,sBAAM,IAAI,WAAW,CAAC;EAAE,CAAC;CACxE;CAGA,KAAK,IAAI,IAAI,YAAY,GAAG,KAAK,GAAG,KAAK;EACvC,MAAM,OAAO,MAAM;EACnB,IAAI,gBAAgB;EACpB,KAAK,MAAM,KAAK,KAAK,MACnB,gBAAgB,KAAK,IAAI,eAAe,MAAM,EAAE,CAAE,KAAK;EAEzD,KAAK,QAAQ,KAAK,KAAK,WAAW,IAAI,IAAI,gBAAgB;EAE1D,MAAM,YAAa,KAAK,WAAW,KAAM;EACzC,MAAM,aAAa,KAAK,WAAW,MAAM;EACzC,MAAM,OAAiB,CAAC,KAAK,KAAK,QAAQ,YAAY,KAAK,aAAa,IAAI,EAAE;EAC9E,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK,KAAK,KAAK,KAAK,KAAK,MAAM,CAAC;EAC/D,IAAI,cAAc,YAAY,GAAG;GAC/B,MAAM,QAAQ,IAAK,KAAK,WAAW;GACnC,MAAM,OAAO,KAAK,SAAS;GAC3B,KAAK,SAAS,KAAK,QAAU,KAAK,SAAW,OAAQ,QAAS;EAChE;EACA,KAAK,MAAM,KAAK,KAAK,MACnB,KAAK,KAAM,MAAM,EAAE,CAAE,SAAS,IAAK,KAAM,MAAM,EAAE,CAAE,QAAQ,GAAI;EAEjE,IAAI,QAAoB,IAAI,WAAW,IAAI;EAC3C,KAAK,MAAM,KAAK,KAAK,MAAM,QAAQ,YAAY,OAAO,MAAM,EAAE,CAAE,IAAI;EACpE,KAAK,OAAO,OAAO,KAAK;CAC1B;CAEA,OAAO,MAAM,UAAU,CAAE;AAC3B;;;;;;;;;AClHA,SAAgB,mBAAmB,MAA4C;CAC7E,IAAI,KAAK,UAAU,WAAW,IAAI,OAAO,OAAO,4BAA4B;CAC5E,IAAI,KAAK,UAAU,WAAW,IAAI,OAAO,OAAO,6BAA6B;CAE7E,IAAI;CACJ,IAAI,KAAK,aAAa,YAAY,UAChC,SAAS,OACP,YAAY,IAAI,WAAW,CAAC,KAAM,GAAI,CAAC,GAAG,WAAW,aAAa,GAAG,OAAO,KAAK,QAAQ,CAAC,CAC5F;MACK,IAAI,KAAK,aAAa,YAAY,aACvC,IAAI;EACF,SAAS,YAAY,KAAK,QAAQ;CACpC,SAAS,GAAG;EACV,OAAO,OAAO,mCAAoC,EAAY,SAAS;CACzE;MAEA,OAAO,OAAO,oBAAoB,KAAK,UAA0B;CAGnE,IAAI;CACJ,IAAI;EACF,KAAK,QAAQ,OAAO,KAAK,WAAW,QAAQ,KAAK,SAAS;CAC5D,SAAS,GAAG;EACV,OAAO,OAAO,uCAAwC,EAAY,SAAS;CAC7E;CACA,OAAO,KAAK,WAAW,OAAO,+CAA+C;AAC/E;;;ACvCA,MAAM,cAAc,kBAAkB,MAAM;;;;;;;;;;;AAuB5C,SAAgB,oBAAoB,MAA6C;CAC/E,IAAI;CACJ,IAAI;EACF,WAAW,OAAO,KAAK,aAAa,WAAW,kBAAkB,KAAK,QAAQ,IAAI,KAAK;CACzF,SAAS,GAAG;EACV,OAAO,OAAO,kDAAmD,EAAY,SAAS;CACxF;CACA,IAAI,SAAS,WAAW,WAAW,GACjC,OAAO,OAAO,oDAAoD;CAEpE,IAAI,CAAC,KAAK,MACR,OAAO,OAAO,iDAAiD;CAGjE,MAAM,SAAS,OAAO,SAAS,OAAO;CACtC,KAAK,MAAM,aAAa,SAAS,YAAY;EAC3C,MAAM,YAAY,mBAAmB,QAAQ,SAAS;EACtD,IAAI,cAAc,MAAM,OAAO,OAAO,gCAAgC;EACtE,IAAI,cAAc,KAAK,MACrB,OAAO,OAAO,+CAA+C;CAEjE;CAEA,IAAI,WAAW,SAAS,SAAS,KAAK,OAAO,GAAG,OAAO;CAIvD,MAAM,iBAAiB,iBAAiB,KAAK,SAAS,SAAS,OAAO;CACtE,IAAI,mBAAmB,MAAM,OAAO;CACpC,IAAI,KAAK,gBAAgB,KAAA,GACvB,OAAO,OACL,6GACF;CAEF,OAAO,cAAc,SAAS,SAAS,KAAK,WAAW;AACzD;AAEA,SAAS,mBAAmB,QAAoB,WAAsC;CACpF,IAAI,UAAU,SAAS,IAAI,OAAO;CAClC,MAAM,WAAW,UAAU;CAC3B,MAAM,aAAa,YAAY,KAAM,WAAW,KAAM,IAAI,WAAW;CACrE,IAAI;EACF,MAAM,QAAQ,UAAU,UAAU,YAAY,UAAU,MAAM,GAAG,EAAE,CAAC,CAAC,CAClE,eAAe,UAAU,CAAC,CAC1B,iBAAiB,MAAM;EAC1B,MAAM,OAAO,WAAW,MAAM,WAAW,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;EACxD,OAAO,YAAY,OAAO,YAAY,IAAI,WAAW,CAAC,EAAI,CAAC,GAAG,KAAK,MAAM,EAAE,CAAC,CAAC;CAC/E,QAAQ;EACN,OAAO;CACT;AACF;AAgBA,SAAS,aAAa,OAAuC;CAC3D,IAAI;EACF,MAAM,SAAS,WAAW,KAAK;EAC/B,MAAM,YAA4B,CAAC;EACnC,KAAK,MAAM,KAAK,QACd,IAAI,EAAE,UAAU,MAAM,EAAE,aAAa,GAAG;GACtC,MAAM,IAAI,WAAW,EAAE,KAAK;GAC5B,MAAM,WAAW,WAAW,GAAG,CAAC;GAChC,MAAM,MAAM,WAAW,WAAW,QAAQ,IAAI,CAAC;GAC/C,MAAM,eAAe,WAAW,WAAW,KAAK,CAAC,IAAI;GACrD,UAAU,KAAK;IACb,SAAS,eAAe,WAAW,YAAY,IAAI;IACnD,MAAM,YAAY,GAAG,CAAC,KAAK;IAC3B,WAAY,YAAY,WAAW,KAAK,CAAC,qBAAM,IAAI,WAAW,CAAC;GACjE,CAAC;EACH;EAEF,OAAO;GACL;GACA,YAAY,YAAY,QAAQ,CAAC,KAAK;GACtC,WAAW,YAAY,QAAQ,EAAE,KAAK;EACxC;CACF,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAS,iBAAiB,YAAwB,cAA+C;CAC/F,MAAM,QAAQ,aAAa,UAAU;CACrC,MAAM,UAAU,aAAa,YAAY;CACzC,IAAI,CAAC,OAAO,OAAO,OAAO,yDAAyD;CACnF,IAAI,CAAC,SAAS,OAAO,OAAO,4DAA4D;CACxF,IAAI,MAAM,UAAU,WAAW,KAAK,QAAQ,UAAU,WAAW,GAC/D,OAAO,OAAO,0DAA0D;CAE1E,MAAM,IAAI,MAAM,UAAU;CAC1B,MAAM,IAAI,QAAQ,UAAU;CAC5B,MAAM,QAAQ,aAAa,CAAC;CAE5B,IAAI,UADU,aAAa,CACT,GAChB,OAAO,OAAO,gEAAgE;CAGhF,MAAM,KAAK,eAAe,EAAE,SAAS;CACrC,MAAM,KAAK,eAAe,EAAE,SAAS;CACrC,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,OAAO,gDAAgD;CAE9E,QAAQ,OAAR;EACE,KAAK;GAEH,IACE,eAAe,IAAI,IAAI,CAAC,KACxB,eAAe,IAAI,IAAI,CAAC,MACvB,YAAY,IAAI,CAAC,KAAK,SAAS,YAAY,IAAI,CAAC,KAAK,KAEtD,OAAO;GAET;EACF,KAAK;GAEH,IACE,eAAe,IAAI,IAAI,CAAC,KACxB,eAAe,IAAI,IAAI,CAAC,KACxB,eAAe,IAAI,IAAI,CAAC,MACvB,YAAY,IAAI,CAAC,KAAK,SAAS,YAAY,IAAI,CAAC,KAAK,KAEtD,OAAO;GAET;EACF,KAAK;GAKH,IACE,eAAe,IAAI,IAAI,CAAC,KACxB,eAAe,IAAI,IAAI,CAAC,MACvB,YAAY,IAAI,CAAC,KAAK,SAAS,YAAY,IAAI,CAAC,KAAK,OACtD,eAAe,IAAI,IAAI,CAAC,GAExB,OAAO;GAET;EACF,SAIE,OAAO,OAAO,8EAA8E;CAChG;CACA,OAAO,OAAO,+DAA+D;AAC/E;AAIA,SAAS,aAAa,UAAsC;CAC1D,IAAI,SAAS,QAAQ,SAAS,mBAAmB,KAAK,SAAS,SAAS,IAAI,OAAO;CACnF,IAAI,SAAS,QAAQ,SAAS,wBAAwB,KAAK,SAAS,SAAS,IAC3E,OAAO;CAET,IAAI,SAAS,QAAQ,SAAS,uBAAuB,KAAK,SAAS,SAAS,KAC1E,OAAO;CAET,OAAO;AACT;AAEA,SAAS,eAAe,OAAyD;CAC/E,IAAI;EACF,OAAO,WAAW,KAAK;CACzB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,eACP,GACA,GACA,OACS;CACT,MAAM,IAAI,WAAW,GAAG,KAAK,qBAAK,IAAI,WAAW,CAAC;CAClD,MAAM,IAAI,WAAW,GAAG,KAAK,qBAAK,IAAI,WAAW,CAAC;CAClD,OAAO,WAAW,GAAG,CAAC;AACxB;;;;;;;AAQA,MAAM,gBAAgB,MAAM,MAAM;AAClC,MAAM,gBAAgB,MAAM,MAAM,MAAM;AAExC,SAAS,cAAc,cAA0B,aAA4C;CAC3F,MAAM,UAAU,aAAa,YAAY;CACzC,IAAI,CAAC,SAAS,OAAO,OAAO,4DAA4D;CACxF,MAAM,QAAQ,OAAO,YAAY,SAAS;CAC1C,IAAI,QAAQ,cAAc,OACxB,OAAO,OACL,mGACF;CAEF,MAAM,WAAW,QAAQ,aAAa;CACtC,IAAI,WAAW,iBAAiB,WAAW,eACzC,OAAO,OACL,8CAA8C,SAAS,wDAAwD,cAAc,GAAG,cAAc,WAChJ;CAEF,OAAO;AACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hwlt/era-connect",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "ERA hardware wallet SDK: air-gapped UR/QR account linking and transaction signing for EVM, Bitcoin, Solana and Tron",
5
5
  "keywords": [
6
6
  "era-wallet",
@@ -36,6 +36,8 @@
36
36
  "evm",
37
37
  "btc",
38
38
  "solana",
39
+ "cardano",
40
+ "ton",
39
41
  "tron",
40
42
  "verify",
41
43
  "NOTICE"
@@ -102,6 +104,28 @@
102
104
  "default": "./dist/tron.cjs"
103
105
  }
104
106
  },
107
+ "./ton": {
108
+ "react-native": "./dist/ton.js",
109
+ "import": {
110
+ "types": "./dist/ton.d.ts",
111
+ "default": "./dist/ton.js"
112
+ },
113
+ "require": {
114
+ "types": "./dist/ton.d.cts",
115
+ "default": "./dist/ton.cjs"
116
+ }
117
+ },
118
+ "./cardano": {
119
+ "react-native": "./dist/cardano.js",
120
+ "import": {
121
+ "types": "./dist/cardano.d.ts",
122
+ "default": "./dist/cardano.js"
123
+ },
124
+ "require": {
125
+ "types": "./dist/cardano.d.cts",
126
+ "default": "./dist/cardano.cjs"
127
+ }
128
+ },
105
129
  "./verify": {
106
130
  "react-native": "./dist/verify.js",
107
131
  "import": {
@@ -115,12 +139,6 @@
115
139
  },
116
140
  "./package.json": "./package.json"
117
141
  },
118
- "scripts": {
119
- "build": "tsdown",
120
- "test": "vitest run",
121
- "test:watch": "vitest",
122
- "typecheck": "tsc --noEmit"
123
- },
124
142
  "dependencies": {
125
143
  "@noble/curves": "^1.8.1",
126
144
  "@noble/hashes": "^1.7.1",
@@ -136,5 +154,11 @@
136
154
  "tsdown": "^0.22.14",
137
155
  "typescript": "^5.7.3",
138
156
  "vitest": "^3.0.5"
157
+ },
158
+ "scripts": {
159
+ "build": "tsdown",
160
+ "test": "vitest run",
161
+ "test:watch": "vitest",
162
+ "typecheck": "tsc --noEmit"
139
163
  }
140
- }
164
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "main": "../dist/ton.cjs",
3
+ "module": "../dist/ton.js",
4
+ "types": "../dist/ton.d.ts",
5
+ "react-native": "../dist/ton.js"
6
+ }