@qrkit/core 0.1.0 → 0.2.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.
- package/README.md +35 -29
- package/dist/index.cjs +6 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -12
- package/dist/index.js.map +1 -1
- package/package.json +2 -10
package/README.md
CHANGED
|
@@ -12,50 +12,56 @@ pnpm add @qrkit/core
|
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
import { parseXpub, deriveEvmAccount, buildEthSignRequestURParts, parseEthSignature } from '@qrkit/core'
|
|
17
|
-
|
|
18
|
-
// 1. Parse the connection QR exported from the wallet (crypto-hdkey or crypto-account)
|
|
19
|
-
const parsed = parseXpub(scannedUR)
|
|
20
|
-
const account = deriveEvmAccount(parsed)
|
|
21
|
-
// account.address → EIP-55 checksummed address
|
|
22
|
-
// account.publicKey → compressed pubkey hex
|
|
23
|
-
// account.sourceFingerprint → required by Shell for signing
|
|
15
|
+
### 1. Parse the connection QR from the wallet
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
const parts = buildEthSignRequestURParts(message, account.address, account.sourceFingerprint)
|
|
27
|
-
// parts.length === 1 for short messages, >1 for animated QR
|
|
17
|
+
Scan the `crypto-hdkey` or `crypto-account` QR exported by the hardware wallet, then derive the EVM account:
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// → '0x...' hex string
|
|
32
|
-
```
|
|
19
|
+
```ts
|
|
20
|
+
import { parseConnection } from '@qrkit/core'
|
|
33
21
|
|
|
34
|
-
|
|
22
|
+
// scannedUR comes from a QR scanner — { type: string, cbor: Uint8Array }
|
|
23
|
+
const [account] = parseConnection(scannedUR, { chains: ['evm'] })
|
|
35
24
|
|
|
36
|
-
|
|
25
|
+
account.address // EIP-55 checksummed address
|
|
26
|
+
account.publicKey // compressed pubkey hex
|
|
27
|
+
account.sourceFingerprint // master key fingerprint — required for signing
|
|
28
|
+
```
|
|
37
29
|
|
|
38
|
-
|
|
30
|
+
### 2. Build a sign request
|
|
39
31
|
|
|
40
|
-
|
|
32
|
+
Encode a message as animated UR parts to display as a QR code for the wallet to scan:
|
|
41
33
|
|
|
42
34
|
```ts
|
|
43
|
-
|
|
44
|
-
import { defineConfig } from 'vite'
|
|
45
|
-
import { nodePolyfills } from 'vite-plugin-node-polyfills'
|
|
35
|
+
import { buildEthSignRequestURParts, buildEthSignRequestUR } from '@qrkit/core'
|
|
46
36
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
// Animated QR (multiple parts for long messages)
|
|
38
|
+
const parts = buildEthSignRequestURParts(message, account.address, account.sourceFingerprint)
|
|
39
|
+
// parts is string[] — cycle through them to animate the QR
|
|
40
|
+
|
|
41
|
+
// Single-frame QR (short messages)
|
|
42
|
+
const ur = buildEthSignRequestUR(message, account.address, account.sourceFingerprint)
|
|
50
43
|
```
|
|
51
44
|
|
|
52
|
-
|
|
45
|
+
### 3. Parse the wallet's signature response
|
|
46
|
+
|
|
47
|
+
After the user scans the wallet's response QR, decode the signature:
|
|
53
48
|
|
|
54
49
|
```ts
|
|
55
|
-
import {
|
|
56
|
-
|
|
50
|
+
import { parseEthSignature } from '@qrkit/core'
|
|
51
|
+
|
|
52
|
+
const signature = parseEthSignature(scannedResponseUR)
|
|
53
|
+
// → '0x...' hex string, ready for ethers / viem
|
|
57
54
|
```
|
|
58
55
|
|
|
56
|
+
## API
|
|
57
|
+
|
|
58
|
+
| Export | Description |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `parseConnection(ur, options)` | Parse a `crypto-hdkey` or `crypto-account` UR into `Account[]` |
|
|
61
|
+
| `buildEthSignRequestURParts(message, address, fingerprint)` | Build animated UR parts for an EIP-191 sign request |
|
|
62
|
+
| `buildEthSignRequestUR(message, address, fingerprint)` | Build a single-frame UR for a sign request |
|
|
63
|
+
| `parseEthSignature(ur)` | Decode an `eth-signature` UR into a `0x...` hex string |
|
|
64
|
+
|
|
59
65
|
## License
|
|
60
66
|
|
|
61
67
|
[Apache 2.0](../../LICENSE)
|
package/dist/index.cjs
CHANGED
|
@@ -219,17 +219,11 @@ function encode(value) {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
// src/urEncoding.ts
|
|
222
|
-
var import_bc_ur = require("@
|
|
222
|
+
var import_bc_ur = require("@qrkit/bc-ur");
|
|
223
223
|
function encodeURParts(cbor, type, maxFragmentLength = 200) {
|
|
224
|
-
const ur =
|
|
225
|
-
const encoder = new import_bc_ur.
|
|
226
|
-
return
|
|
227
|
-
{ length: encoder.fragmentsLength },
|
|
228
|
-
() => encoder.nextPart().toUpperCase()
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
function encodeURFirstPart(cbor, type, maxFragmentLength = 200) {
|
|
232
|
-
return encodeURParts(cbor, type, maxFragmentLength)[0];
|
|
224
|
+
const ur = import_bc_ur.UR.fromCbor({ type, payload: cbor });
|
|
225
|
+
const encoder = new import_bc_ur.UrFountainEncoder(ur, maxFragmentLength);
|
|
226
|
+
return encoder.getAllPartsUr().map((part) => part.toString().toUpperCase());
|
|
233
227
|
}
|
|
234
228
|
|
|
235
229
|
// src/eth/signRequest.ts
|
|
@@ -277,10 +271,10 @@ function buildEthSignRequestURParts(message, address, sourceFingerprint, origin)
|
|
|
277
271
|
);
|
|
278
272
|
}
|
|
279
273
|
function buildEthSignRequestUR(message, address, sourceFingerprint, origin) {
|
|
280
|
-
return
|
|
274
|
+
return encodeURParts(
|
|
281
275
|
buildEthSignRequestCbor(message, address, sourceFingerprint, origin),
|
|
282
276
|
"eth-sign-request"
|
|
283
|
-
);
|
|
277
|
+
)[0];
|
|
284
278
|
}
|
|
285
279
|
|
|
286
280
|
// src/eth/signature.ts
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/parseXpub.ts","../src/eth/address.ts","../src/eth/deriveAccount.ts","../src/parseConnection.ts","../src/cbor.ts","../src/urEncoding.ts","../src/eth/signRequest.ts","../src/eth/signature.ts"],"sourcesContent":["// Types\nexport type { ScannedUR, Chain, QRKitConfig, Account, EvmAccount } from \"./types.js\";\nexport type { ParsedXpub, XpubType } from \"./parseXpub.js\";\n\n// Connection\nexport { parseConnection } from \"./parseConnection.js\";\n\n// EVM signing\nexport { buildEthSignRequestUR, buildEthSignRequestURParts } from \"./eth/signRequest.js\";\nexport { parseEthSignature } from \"./eth/signature.js\";\n","import { HDKey } from \"@scure/bip32\";\nimport { decode as cborDecode, type TagDecoder } from \"cborg\";\n\nimport type { ScannedUR } from \"./types.js\";\n\nexport type XpubType = \"xpub\";\n\nexport interface ParsedXpub {\n hdKey: HDKey;\n type: XpubType;\n /** BIP-44 purpose index: 44 for EVM */\n purpose: number | undefined;\n /** BIP-44 coin type: 60 = ETH */\n coinType: number | undefined;\n /** source-fingerprint from the origin keypath — required by Shell for signing */\n sourceFingerprint: number | undefined;\n raw: string;\n}\n\nfunction get(m: unknown, k: number): unknown {\n if (m instanceof Map) return (m as Map<number, unknown>).get(k);\n return undefined;\n}\n\nconst passthrough = (v: unknown) => v;\n\nfunction decodeCbor(cbor: Uint8Array): Map<number, unknown> {\n return cborDecode(cbor, {\n useMaps: true,\n tags: Object.assign([] as TagDecoder[], {\n 303: passthrough, // crypto-hdkey\n 304: passthrough, // crypto-keypath\n 305: passthrough, // crypto-coin-info\n }),\n }) as Map<number, unknown>;\n}\n\nfunction parseCryptoHdKey(map: unknown, raw: string): ParsedXpub {\n const keyData = get(map, 3) as Uint8Array | undefined;\n const chainCode = get(map, 4) as Uint8Array | undefined;\n\n if (!keyData || !chainCode) {\n throw new Error(\"crypto-hdkey missing key-data or chain-code\");\n }\n\n let purpose: number | undefined;\n let coinType: number | undefined;\n let sourceFingerprint: number | undefined;\n const origin = get(map, 6);\n if (origin) {\n const components = get(origin, 1);\n if (Array.isArray(components)) {\n if (components.length >= 1) purpose = components[0] as number;\n if (components.length >= 3) coinType = components[2] as number;\n }\n sourceFingerprint = get(origin, 2) as number | undefined;\n }\n\n const hdKey = new HDKey({ publicKey: keyData, chainCode });\n return { hdKey, type: \"xpub\", purpose, coinType, sourceFingerprint, raw };\n}\n\nfunction parseScannedUR(scanned: ScannedUR): ParsedXpub | ParsedXpub[] {\n const { type, cbor } = scanned;\n const raw = `ur:${type}`;\n\n if (type !== \"crypto-hdkey\" && type !== \"crypto-account\") {\n throw new Error(`Unsupported UR type: ${type}`);\n }\n\n const map = decodeCbor(cbor);\n\n if (type === \"crypto-hdkey\") {\n return parseCryptoHdKey(map, raw);\n }\n\n const accounts = map.get(2) as unknown[] | undefined;\n if (!Array.isArray(accounts) || accounts.length === 0) {\n throw new Error(\"crypto-account contains no keys\");\n }\n return accounts.map((entry) => parseCryptoHdKey(entry, raw));\n}\n\nexport function parseXpub(input: ScannedUR | string): ParsedXpub[] {\n if (typeof input !== \"string\") {\n const result = parseScannedUR(input);\n return Array.isArray(result) ? result : [result];\n }\n // Raw base58 xpub string — @scure/bip32 handles decoding directly\n const hdKey = HDKey.fromExtendedKey(input.trim());\n return [\n {\n hdKey,\n type: \"xpub\",\n purpose: undefined,\n coinType: undefined,\n sourceFingerprint: undefined,\n raw: input.trim(),\n },\n ];\n}\n","import { keccak_256 } from \"@noble/hashes/sha3.js\";\nimport * as secp from \"@noble/secp256k1\";\n\nexport function pubKeyToEthAddress(compressedPubKey: Uint8Array): string {\n const uncompressed = secp.Point.fromBytes(compressedPubKey).toBytes(false);\n const hash = keccak_256(uncompressed.slice(1));\n const hex = [...hash.slice(12)]\n .map((b: number) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n return toChecksumAddress(hex);\n}\n\nfunction toChecksumAddress(hex: string): string {\n const checksumHash = keccak_256(new TextEncoder().encode(hex));\n return (\n \"0x\" +\n [...hex]\n .map((c, i) => {\n if (c >= \"0\" && c <= \"9\") return c;\n const nibble =\n i % 2 === 0\n ? (checksumHash[Math.floor(i / 2)] >> 4) & 0xf\n : checksumHash[Math.floor(i / 2)] & 0xf;\n return nibble >= 8 ? c.toUpperCase() : c.toLowerCase();\n })\n .join(\"\")\n );\n}\n","import type { HDKey } from \"@scure/bip32\";\n\nimport type { ParsedXpub } from \"../parseXpub.js\";\nimport { pubKeyToEthAddress } from \"./address.js\";\n\nexport interface DerivedAccount {\n address: string;\n publicKey: string;\n /** source-fingerprint from the scanned xpub — required by Shell for signing */\n sourceFingerprint: number | undefined;\n}\n\n// Derive the first external address (index 0) from an account-level xpub.\n// Account-level xpub is at depth 3 (m/purpose'/coin'/account').\n// External chain is child 0, then address index 0.\nfunction firstChild(accountKey: HDKey): HDKey {\n return accountKey.deriveChild(0).deriveChild(0);\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function deriveEvmAccount(parsed: ParsedXpub[]): DerivedAccount | undefined {\n for (const entry of parsed) {\n const { hdKey, purpose, coinType, type, sourceFingerprint } = entry;\n const isEvm =\n (purpose === 44 && coinType === 60) || (purpose === undefined && type === \"xpub\");\n\n if (!isEvm) continue;\n\n const child = firstChild(hdKey);\n if (!child.publicKey) continue;\n\n return {\n address: pubKeyToEthAddress(child.publicKey),\n publicKey: toHex(child.publicKey),\n sourceFingerprint,\n };\n }\n return undefined;\n}\n","import { parseXpub } from \"./parseXpub.js\";\nimport { deriveEvmAccount } from \"./eth/deriveAccount.js\";\nimport type { Account, Chain, EvmAccount, QRKitConfig, ScannedUR } from \"./types.js\";\n\nconst ALL_CHAINS: Chain[] = [\"evm\", \"btc\"];\n\n/**\n * Parse a connection QR (crypto-hdkey or crypto-account) and return\n * only the accounts for the chains configured in QRKitConfig.\n *\n * A dApp configured with `chains: [\"evm\"]` will never see BTC accounts,\n * and vice versa. Both chains can be enabled with `chains: [\"evm\", \"btc\"]`.\n * If `chains` is omitted, all supported chains are tried.\n */\nexport function parseConnection(\n scannedUR: ScannedUR,\n config: QRKitConfig = {},\n): Account[] {\n const chains = config.chains ?? ALL_CHAINS;\n const parsed = parseXpub(scannedUR);\n const accounts: Account[] = [];\n\n if (chains.includes(\"evm\")) {\n const account = deriveEvmAccount(parsed);\n if (account) {\n accounts.push({ chain: \"evm\", ...account } satisfies EvmAccount);\n }\n }\n\n // \"btc\" — will be implemented in src/btc/ when Bitcoin support is added.\n\n return accounts;\n}\n","// Minimal CBOR encoder.\n// Supports only the types needed for eth-sign-request: uint, bytes, text, array, map (integer keys), bool, tag.\n\nfunction majorType(major: number, n: number): number[] {\n const base = major << 5;\n if (n <= 23) return [base | n];\n if (n <= 0xff) return [base | 0x18, n];\n if (n <= 0xffff) return [base | 0x19, (n >> 8) & 0xff, n & 0xff];\n return [base | 0x1a, (n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];\n}\n\nexport function encodeItem(value: unknown): number[] {\n if (typeof value === \"boolean\") {\n return [value ? 0xf5 : 0xf4];\n }\n if (typeof value === \"number\") {\n return majorType(0, value);\n }\n if (value instanceof Uint8Array) {\n return [...majorType(2, value.length), ...value];\n }\n if (typeof value === \"string\") {\n const bytes = new TextEncoder().encode(value);\n return [...majorType(3, bytes.length), ...bytes];\n }\n if (Array.isArray(value)) {\n return [...majorType(4, value.length), ...value.flatMap(encodeItem)];\n }\n if (value instanceof CborTag) {\n return [...majorType(6, value.tag), ...encodeItem(value.value)];\n }\n if (value instanceof Map) {\n const entries = [...value.entries()];\n return [\n ...majorType(5, entries.length),\n ...entries.flatMap(([k, v]) => [...encodeItem(k), ...encodeItem(v)]),\n ];\n }\n throw new Error(`Unsupported CBOR value: ${typeof value}`);\n}\n\nexport class CborTag {\n tag: number;\n value: unknown;\n constructor(tag: number, value: unknown) {\n this.tag = tag;\n this.value = value;\n }\n}\n\nexport function encode(value: unknown): Uint8Array {\n return new Uint8Array(encodeItem(value));\n}\n","import { UR, UREncoder } from \"@ngraveio/bc-ur\";\n\nexport function encodeURParts(\n cbor: Uint8Array,\n type: string,\n maxFragmentLength = 200,\n): string[] {\n // @ngraveio/bc-ur requires a Buffer. In Node.js it is a global; in browsers the\n // bundler must provide the `buffer` polyfill (same requirement as the prototype).\n const ur = new UR(Buffer.from(cbor), type);\n const encoder = new UREncoder(ur, maxFragmentLength);\n return Array.from({ length: encoder.fragmentsLength }, () =>\n encoder.nextPart().toUpperCase(),\n );\n}\n\nexport function encodeURFirstPart(\n cbor: Uint8Array,\n type: string,\n maxFragmentLength = 200,\n): string {\n return encodeURParts(cbor, type, maxFragmentLength)[0];\n}\n","import { encode, CborTag } from \"../cbor.js\";\nimport { encodeURFirstPart, encodeURParts } from \"../urEncoding.js\";\n\n// EIP-191 personal_sign = eth-raw-bytes (ERC-4527 data type 3)\nconst DATA_TYPE_ETH_RAW_BYTES = 3;\n\n// CBOR tag for crypto-keypath\nconst TAG_KEYPATH = 304;\n\nfunction randomBytes(n: number): Uint8Array {\n const buf = new Uint8Array(n);\n crypto.getRandomValues(buf);\n return buf;\n}\n\nfunction buildKeypath(\n purpose: number,\n coinType: number,\n sourceFingerprint: number | undefined,\n): CborTag {\n // m/purpose'/coinType'/0'/0/0\n const components = [purpose, true, coinType, true, 0, true, 0, false, 0, false];\n const keypathMap = new Map<number, unknown>([[1, components]]);\n if (sourceFingerprint !== undefined) {\n keypathMap.set(2, sourceFingerprint);\n }\n return new CborTag(TAG_KEYPATH, keypathMap);\n}\n\nfunction buildEthSignRequestCbor(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin = \"qrkit\",\n): Uint8Array {\n const requestId = randomBytes(16);\n const messageBytes = new TextEncoder().encode(message);\n const keypath = buildKeypath(44, 60, sourceFingerprint);\n\n const addrHex = address.replace(/^0x/i, \"\");\n const addrBytes = new Uint8Array(addrHex.match(/.{2}/g)!.map((b) => parseInt(b, 16)));\n\n const map = new Map<number, unknown>([\n [1, new CborTag(37, requestId)], // request-id: uuid = #6.37(bstr)\n [2, messageBytes], // sign-data\n [3, DATA_TYPE_ETH_RAW_BYTES], // data-type: eth-raw-bytes for EIP-191\n [5, keypath], // derivation-path\n [6, addrBytes], // address\n [7, origin], // origin\n ]);\n\n return encode(map);\n}\n\nexport function buildEthSignRequestURParts(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin?: string,\n): string[] {\n return encodeURParts(\n buildEthSignRequestCbor(message, address, sourceFingerprint, origin),\n \"eth-sign-request\",\n );\n}\n\nexport function buildEthSignRequestUR(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin?: string,\n): string {\n return encodeURFirstPart(\n buildEthSignRequestCbor(message, address, sourceFingerprint, origin),\n \"eth-sign-request\",\n );\n}\n","import { decode as cborDecode, type TagDecoder } from \"cborg\";\n\nimport type { ScannedUR } from \"../types.js\";\n\nexport function parseEthSignature(scanned: ScannedUR): string {\n if (scanned.type !== \"eth-signature\") {\n throw new Error(`Expected eth-signature, got: ${scanned.type}`);\n }\n\n const map = cborDecode(scanned.cbor, {\n useMaps: true,\n tags: Object.assign([] as TagDecoder[], { 37: (v: unknown) => v }),\n }) as Map<number, unknown>;\n\n const sigBytes = map.get(2) as Uint8Array | undefined;\n if (!sigBytes || sigBytes.length < 64) {\n throw new Error(\"Invalid or missing signature bytes\");\n }\n\n return \"0x\" + [...sigBytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"],"mappings":"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","names":["cborDecode","import_cborg","cborDecode"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/parseXpub.ts","../src/eth/address.ts","../src/eth/deriveAccount.ts","../src/parseConnection.ts","../src/cbor.ts","../src/urEncoding.ts","../src/eth/signRequest.ts","../src/eth/signature.ts"],"sourcesContent":["// Types\nexport type { ScannedUR, Chain, QRKitConfig, Account, EvmAccount } from \"./types.js\";\nexport type { ParsedXpub, XpubType } from \"./parseXpub.js\";\n\n// Connection\nexport { parseConnection } from \"./parseConnection.js\";\n\n// EVM signing\nexport { buildEthSignRequestUR, buildEthSignRequestURParts } from \"./eth/signRequest.js\";\nexport { parseEthSignature } from \"./eth/signature.js\";\n","import { HDKey } from \"@scure/bip32\";\nimport { decode as cborDecode, type TagDecoder } from \"cborg\";\n\nimport type { ScannedUR } from \"./types.js\";\n\nexport type XpubType = \"xpub\";\n\nexport interface ParsedXpub {\n hdKey: HDKey;\n type: XpubType;\n /** BIP-44 purpose index: 44 for EVM */\n purpose: number | undefined;\n /** BIP-44 coin type: 60 = ETH */\n coinType: number | undefined;\n /** source-fingerprint from the origin keypath — required by Shell for signing */\n sourceFingerprint: number | undefined;\n raw: string;\n}\n\nfunction get(m: unknown, k: number): unknown {\n if (m instanceof Map) return (m as Map<number, unknown>).get(k);\n return undefined;\n}\n\nconst passthrough = (v: unknown) => v;\n\nfunction decodeCbor(cbor: Uint8Array): Map<number, unknown> {\n return cborDecode(cbor, {\n useMaps: true,\n tags: Object.assign([] as TagDecoder[], {\n 303: passthrough, // crypto-hdkey\n 304: passthrough, // crypto-keypath\n 305: passthrough, // crypto-coin-info\n }),\n }) as Map<number, unknown>;\n}\n\nfunction parseCryptoHdKey(map: unknown, raw: string): ParsedXpub {\n const keyData = get(map, 3) as Uint8Array | undefined;\n const chainCode = get(map, 4) as Uint8Array | undefined;\n\n if (!keyData || !chainCode) {\n throw new Error(\"crypto-hdkey missing key-data or chain-code\");\n }\n\n let purpose: number | undefined;\n let coinType: number | undefined;\n let sourceFingerprint: number | undefined;\n const origin = get(map, 6);\n if (origin) {\n const components = get(origin, 1);\n if (Array.isArray(components)) {\n if (components.length >= 1) purpose = components[0] as number;\n if (components.length >= 3) coinType = components[2] as number;\n }\n sourceFingerprint = get(origin, 2) as number | undefined;\n }\n\n const hdKey = new HDKey({ publicKey: keyData, chainCode });\n return { hdKey, type: \"xpub\", purpose, coinType, sourceFingerprint, raw };\n}\n\nfunction parseScannedUR(scanned: ScannedUR): ParsedXpub | ParsedXpub[] {\n const { type, cbor } = scanned;\n const raw = `ur:${type}`;\n\n if (type !== \"crypto-hdkey\" && type !== \"crypto-account\") {\n throw new Error(`Unsupported UR type: ${type}`);\n }\n\n const map = decodeCbor(cbor);\n\n if (type === \"crypto-hdkey\") {\n return parseCryptoHdKey(map, raw);\n }\n\n const accounts = map.get(2) as unknown[] | undefined;\n if (!Array.isArray(accounts) || accounts.length === 0) {\n throw new Error(\"crypto-account contains no keys\");\n }\n return accounts.map((entry) => parseCryptoHdKey(entry, raw));\n}\n\nexport function parseXpub(input: ScannedUR | string): ParsedXpub[] {\n if (typeof input !== \"string\") {\n const result = parseScannedUR(input);\n return Array.isArray(result) ? result : [result];\n }\n // Raw base58 xpub string — @scure/bip32 handles decoding directly\n const hdKey = HDKey.fromExtendedKey(input.trim());\n return [\n {\n hdKey,\n type: \"xpub\",\n purpose: undefined,\n coinType: undefined,\n sourceFingerprint: undefined,\n raw: input.trim(),\n },\n ];\n}\n","import { keccak_256 } from \"@noble/hashes/sha3.js\";\nimport * as secp from \"@noble/secp256k1\";\n\nexport function pubKeyToEthAddress(compressedPubKey: Uint8Array): string {\n const uncompressed = secp.Point.fromBytes(compressedPubKey).toBytes(false);\n const hash = keccak_256(uncompressed.slice(1));\n const hex = [...hash.slice(12)]\n .map((b: number) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n return toChecksumAddress(hex);\n}\n\nfunction toChecksumAddress(hex: string): string {\n const checksumHash = keccak_256(new TextEncoder().encode(hex));\n return (\n \"0x\" +\n [...hex]\n .map((c, i) => {\n if (c >= \"0\" && c <= \"9\") return c;\n const nibble =\n i % 2 === 0\n ? (checksumHash[Math.floor(i / 2)] >> 4) & 0xf\n : checksumHash[Math.floor(i / 2)] & 0xf;\n return nibble >= 8 ? c.toUpperCase() : c.toLowerCase();\n })\n .join(\"\")\n );\n}\n","import type { HDKey } from \"@scure/bip32\";\n\nimport type { ParsedXpub } from \"../parseXpub.js\";\nimport { pubKeyToEthAddress } from \"./address.js\";\n\nexport interface DerivedAccount {\n address: string;\n publicKey: string;\n /** source-fingerprint from the scanned xpub — required by Shell for signing */\n sourceFingerprint: number | undefined;\n}\n\n// Derive the first external address (index 0) from an account-level xpub.\n// Account-level xpub is at depth 3 (m/purpose'/coin'/account').\n// External chain is child 0, then address index 0.\nfunction firstChild(accountKey: HDKey): HDKey {\n return accountKey.deriveChild(0).deriveChild(0);\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function deriveEvmAccount(parsed: ParsedXpub[]): DerivedAccount | undefined {\n for (const entry of parsed) {\n const { hdKey, purpose, coinType, type, sourceFingerprint } = entry;\n const isEvm =\n (purpose === 44 && coinType === 60) || (purpose === undefined && type === \"xpub\");\n\n if (!isEvm) continue;\n\n const child = firstChild(hdKey);\n if (!child.publicKey) continue;\n\n return {\n address: pubKeyToEthAddress(child.publicKey),\n publicKey: toHex(child.publicKey),\n sourceFingerprint,\n };\n }\n return undefined;\n}\n","import { parseXpub } from \"./parseXpub.js\";\nimport { deriveEvmAccount } from \"./eth/deriveAccount.js\";\nimport type { Account, Chain, EvmAccount, QRKitConfig, ScannedUR } from \"./types.js\";\n\nconst ALL_CHAINS: Chain[] = [\"evm\", \"btc\"];\n\n/**\n * Parse a connection QR (crypto-hdkey or crypto-account) and return\n * only the accounts for the chains configured in QRKitConfig.\n *\n * A dApp configured with `chains: [\"evm\"]` will never see BTC accounts,\n * and vice versa. Both chains can be enabled with `chains: [\"evm\", \"btc\"]`.\n * If `chains` is omitted, all supported chains are tried.\n */\nexport function parseConnection(\n scannedUR: ScannedUR,\n config: QRKitConfig = {},\n): Account[] {\n const chains = config.chains ?? ALL_CHAINS;\n const parsed = parseXpub(scannedUR);\n const accounts: Account[] = [];\n\n if (chains.includes(\"evm\")) {\n const account = deriveEvmAccount(parsed);\n if (account) {\n accounts.push({ chain: \"evm\", ...account } satisfies EvmAccount);\n }\n }\n\n // \"btc\" — will be implemented in src/btc/ when Bitcoin support is added.\n\n return accounts;\n}\n","// Minimal CBOR encoder.\n// Supports only the types needed for eth-sign-request: uint, bytes, text, array, map (integer keys), bool, tag.\n\nfunction majorType(major: number, n: number): number[] {\n const base = major << 5;\n if (n <= 23) return [base | n];\n if (n <= 0xff) return [base | 0x18, n];\n if (n <= 0xffff) return [base | 0x19, (n >> 8) & 0xff, n & 0xff];\n return [base | 0x1a, (n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];\n}\n\nexport function encodeItem(value: unknown): number[] {\n if (typeof value === \"boolean\") {\n return [value ? 0xf5 : 0xf4];\n }\n if (typeof value === \"number\") {\n return majorType(0, value);\n }\n if (value instanceof Uint8Array) {\n return [...majorType(2, value.length), ...value];\n }\n if (typeof value === \"string\") {\n const bytes = new TextEncoder().encode(value);\n return [...majorType(3, bytes.length), ...bytes];\n }\n if (Array.isArray(value)) {\n return [...majorType(4, value.length), ...value.flatMap(encodeItem)];\n }\n if (value instanceof CborTag) {\n return [...majorType(6, value.tag), ...encodeItem(value.value)];\n }\n if (value instanceof Map) {\n const entries = [...value.entries()];\n return [\n ...majorType(5, entries.length),\n ...entries.flatMap(([k, v]) => [...encodeItem(k), ...encodeItem(v)]),\n ];\n }\n throw new Error(`Unsupported CBOR value: ${typeof value}`);\n}\n\nexport class CborTag {\n tag: number;\n value: unknown;\n constructor(tag: number, value: unknown) {\n this.tag = tag;\n this.value = value;\n }\n}\n\nexport function encode(value: unknown): Uint8Array {\n return new Uint8Array(encodeItem(value));\n}\n","import { UR, UrFountainEncoder } from \"@qrkit/bc-ur\";\n\nexport function encodeURParts(\n cbor: Uint8Array,\n type: string,\n maxFragmentLength = 200,\n): string[] {\n const ur = UR.fromCbor({ type, payload: cbor });\n const encoder = new UrFountainEncoder(ur, maxFragmentLength);\n return encoder.getAllPartsUr().map((part) => part.toString().toUpperCase());\n}\n","import { encode, CborTag } from \"../cbor.js\";\nimport { encodeURParts } from \"../urEncoding.js\";\n\n// EIP-191 personal_sign = eth-raw-bytes (ERC-4527 data type 3)\nconst DATA_TYPE_ETH_RAW_BYTES = 3;\n\n// CBOR tag for crypto-keypath\nconst TAG_KEYPATH = 304;\n\nfunction randomBytes(n: number): Uint8Array {\n const buf = new Uint8Array(n);\n crypto.getRandomValues(buf);\n return buf;\n}\n\nfunction buildKeypath(\n purpose: number,\n coinType: number,\n sourceFingerprint: number | undefined,\n): CborTag {\n // m/purpose'/coinType'/0'/0/0\n const components = [purpose, true, coinType, true, 0, true, 0, false, 0, false];\n const keypathMap = new Map<number, unknown>([[1, components]]);\n if (sourceFingerprint !== undefined) {\n keypathMap.set(2, sourceFingerprint);\n }\n return new CborTag(TAG_KEYPATH, keypathMap);\n}\n\nfunction buildEthSignRequestCbor(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin = \"qrkit\",\n): Uint8Array {\n const requestId = randomBytes(16);\n const messageBytes = new TextEncoder().encode(message);\n const keypath = buildKeypath(44, 60, sourceFingerprint);\n\n const addrHex = address.replace(/^0x/i, \"\");\n const addrBytes = new Uint8Array(addrHex.match(/.{2}/g)!.map((b) => parseInt(b, 16)));\n\n const map = new Map<number, unknown>([\n [1, new CborTag(37, requestId)], // request-id: uuid = #6.37(bstr)\n [2, messageBytes], // sign-data\n [3, DATA_TYPE_ETH_RAW_BYTES], // data-type: eth-raw-bytes for EIP-191\n [5, keypath], // derivation-path\n [6, addrBytes], // address\n [7, origin], // origin\n ]);\n\n return encode(map);\n}\n\nexport function buildEthSignRequestURParts(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin?: string,\n): string[] {\n return encodeURParts(\n buildEthSignRequestCbor(message, address, sourceFingerprint, origin),\n \"eth-sign-request\",\n );\n}\n\nexport function buildEthSignRequestUR(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin?: string,\n): string {\n return encodeURParts(\n buildEthSignRequestCbor(message, address, sourceFingerprint, origin),\n \"eth-sign-request\",\n )[0];\n}\n","import { decode as cborDecode, type TagDecoder } from \"cborg\";\n\nimport type { ScannedUR } from \"../types.js\";\n\nexport function parseEthSignature(scanned: ScannedUR): string {\n if (scanned.type !== \"eth-signature\") {\n throw new Error(`Expected eth-signature, got: ${scanned.type}`);\n }\n\n const map = cborDecode(scanned.cbor, {\n useMaps: true,\n tags: Object.assign([] as TagDecoder[], { 37: (v: unknown) => v }),\n }) as Map<number, unknown>;\n\n const sigBytes = map.get(2) as Uint8Array | undefined;\n if (!sigBytes || sigBytes.length < 64) {\n throw new Error(\"Invalid or missing signature bytes\");\n }\n\n return \"0x\" + [...sigBytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"],"mappings":"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","names":["cborDecode","import_cborg","cborDecode"]}
|
package/dist/index.js
CHANGED
|
@@ -180,17 +180,11 @@ function encode(value) {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
// src/urEncoding.ts
|
|
183
|
-
import { UR,
|
|
183
|
+
import { UR, UrFountainEncoder } from "@qrkit/bc-ur";
|
|
184
184
|
function encodeURParts(cbor, type, maxFragmentLength = 200) {
|
|
185
|
-
const ur =
|
|
186
|
-
const encoder = new
|
|
187
|
-
return
|
|
188
|
-
{ length: encoder.fragmentsLength },
|
|
189
|
-
() => encoder.nextPart().toUpperCase()
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
function encodeURFirstPart(cbor, type, maxFragmentLength = 200) {
|
|
193
|
-
return encodeURParts(cbor, type, maxFragmentLength)[0];
|
|
185
|
+
const ur = UR.fromCbor({ type, payload: cbor });
|
|
186
|
+
const encoder = new UrFountainEncoder(ur, maxFragmentLength);
|
|
187
|
+
return encoder.getAllPartsUr().map((part) => part.toString().toUpperCase());
|
|
194
188
|
}
|
|
195
189
|
|
|
196
190
|
// src/eth/signRequest.ts
|
|
@@ -238,10 +232,10 @@ function buildEthSignRequestURParts(message, address, sourceFingerprint, origin)
|
|
|
238
232
|
);
|
|
239
233
|
}
|
|
240
234
|
function buildEthSignRequestUR(message, address, sourceFingerprint, origin) {
|
|
241
|
-
return
|
|
235
|
+
return encodeURParts(
|
|
242
236
|
buildEthSignRequestCbor(message, address, sourceFingerprint, origin),
|
|
243
237
|
"eth-sign-request"
|
|
244
|
-
);
|
|
238
|
+
)[0];
|
|
245
239
|
}
|
|
246
240
|
|
|
247
241
|
// src/eth/signature.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/parseXpub.ts","../src/eth/address.ts","../src/eth/deriveAccount.ts","../src/parseConnection.ts","../src/cbor.ts","../src/urEncoding.ts","../src/eth/signRequest.ts","../src/eth/signature.ts"],"sourcesContent":["import { HDKey } from \"@scure/bip32\";\nimport { decode as cborDecode, type TagDecoder } from \"cborg\";\n\nimport type { ScannedUR } from \"./types.js\";\n\nexport type XpubType = \"xpub\";\n\nexport interface ParsedXpub {\n hdKey: HDKey;\n type: XpubType;\n /** BIP-44 purpose index: 44 for EVM */\n purpose: number | undefined;\n /** BIP-44 coin type: 60 = ETH */\n coinType: number | undefined;\n /** source-fingerprint from the origin keypath — required by Shell for signing */\n sourceFingerprint: number | undefined;\n raw: string;\n}\n\nfunction get(m: unknown, k: number): unknown {\n if (m instanceof Map) return (m as Map<number, unknown>).get(k);\n return undefined;\n}\n\nconst passthrough = (v: unknown) => v;\n\nfunction decodeCbor(cbor: Uint8Array): Map<number, unknown> {\n return cborDecode(cbor, {\n useMaps: true,\n tags: Object.assign([] as TagDecoder[], {\n 303: passthrough, // crypto-hdkey\n 304: passthrough, // crypto-keypath\n 305: passthrough, // crypto-coin-info\n }),\n }) as Map<number, unknown>;\n}\n\nfunction parseCryptoHdKey(map: unknown, raw: string): ParsedXpub {\n const keyData = get(map, 3) as Uint8Array | undefined;\n const chainCode = get(map, 4) as Uint8Array | undefined;\n\n if (!keyData || !chainCode) {\n throw new Error(\"crypto-hdkey missing key-data or chain-code\");\n }\n\n let purpose: number | undefined;\n let coinType: number | undefined;\n let sourceFingerprint: number | undefined;\n const origin = get(map, 6);\n if (origin) {\n const components = get(origin, 1);\n if (Array.isArray(components)) {\n if (components.length >= 1) purpose = components[0] as number;\n if (components.length >= 3) coinType = components[2] as number;\n }\n sourceFingerprint = get(origin, 2) as number | undefined;\n }\n\n const hdKey = new HDKey({ publicKey: keyData, chainCode });\n return { hdKey, type: \"xpub\", purpose, coinType, sourceFingerprint, raw };\n}\n\nfunction parseScannedUR(scanned: ScannedUR): ParsedXpub | ParsedXpub[] {\n const { type, cbor } = scanned;\n const raw = `ur:${type}`;\n\n if (type !== \"crypto-hdkey\" && type !== \"crypto-account\") {\n throw new Error(`Unsupported UR type: ${type}`);\n }\n\n const map = decodeCbor(cbor);\n\n if (type === \"crypto-hdkey\") {\n return parseCryptoHdKey(map, raw);\n }\n\n const accounts = map.get(2) as unknown[] | undefined;\n if (!Array.isArray(accounts) || accounts.length === 0) {\n throw new Error(\"crypto-account contains no keys\");\n }\n return accounts.map((entry) => parseCryptoHdKey(entry, raw));\n}\n\nexport function parseXpub(input: ScannedUR | string): ParsedXpub[] {\n if (typeof input !== \"string\") {\n const result = parseScannedUR(input);\n return Array.isArray(result) ? result : [result];\n }\n // Raw base58 xpub string — @scure/bip32 handles decoding directly\n const hdKey = HDKey.fromExtendedKey(input.trim());\n return [\n {\n hdKey,\n type: \"xpub\",\n purpose: undefined,\n coinType: undefined,\n sourceFingerprint: undefined,\n raw: input.trim(),\n },\n ];\n}\n","import { keccak_256 } from \"@noble/hashes/sha3.js\";\nimport * as secp from \"@noble/secp256k1\";\n\nexport function pubKeyToEthAddress(compressedPubKey: Uint8Array): string {\n const uncompressed = secp.Point.fromBytes(compressedPubKey).toBytes(false);\n const hash = keccak_256(uncompressed.slice(1));\n const hex = [...hash.slice(12)]\n .map((b: number) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n return toChecksumAddress(hex);\n}\n\nfunction toChecksumAddress(hex: string): string {\n const checksumHash = keccak_256(new TextEncoder().encode(hex));\n return (\n \"0x\" +\n [...hex]\n .map((c, i) => {\n if (c >= \"0\" && c <= \"9\") return c;\n const nibble =\n i % 2 === 0\n ? (checksumHash[Math.floor(i / 2)] >> 4) & 0xf\n : checksumHash[Math.floor(i / 2)] & 0xf;\n return nibble >= 8 ? c.toUpperCase() : c.toLowerCase();\n })\n .join(\"\")\n );\n}\n","import type { HDKey } from \"@scure/bip32\";\n\nimport type { ParsedXpub } from \"../parseXpub.js\";\nimport { pubKeyToEthAddress } from \"./address.js\";\n\nexport interface DerivedAccount {\n address: string;\n publicKey: string;\n /** source-fingerprint from the scanned xpub — required by Shell for signing */\n sourceFingerprint: number | undefined;\n}\n\n// Derive the first external address (index 0) from an account-level xpub.\n// Account-level xpub is at depth 3 (m/purpose'/coin'/account').\n// External chain is child 0, then address index 0.\nfunction firstChild(accountKey: HDKey): HDKey {\n return accountKey.deriveChild(0).deriveChild(0);\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function deriveEvmAccount(parsed: ParsedXpub[]): DerivedAccount | undefined {\n for (const entry of parsed) {\n const { hdKey, purpose, coinType, type, sourceFingerprint } = entry;\n const isEvm =\n (purpose === 44 && coinType === 60) || (purpose === undefined && type === \"xpub\");\n\n if (!isEvm) continue;\n\n const child = firstChild(hdKey);\n if (!child.publicKey) continue;\n\n return {\n address: pubKeyToEthAddress(child.publicKey),\n publicKey: toHex(child.publicKey),\n sourceFingerprint,\n };\n }\n return undefined;\n}\n","import { parseXpub } from \"./parseXpub.js\";\nimport { deriveEvmAccount } from \"./eth/deriveAccount.js\";\nimport type { Account, Chain, EvmAccount, QRKitConfig, ScannedUR } from \"./types.js\";\n\nconst ALL_CHAINS: Chain[] = [\"evm\", \"btc\"];\n\n/**\n * Parse a connection QR (crypto-hdkey or crypto-account) and return\n * only the accounts for the chains configured in QRKitConfig.\n *\n * A dApp configured with `chains: [\"evm\"]` will never see BTC accounts,\n * and vice versa. Both chains can be enabled with `chains: [\"evm\", \"btc\"]`.\n * If `chains` is omitted, all supported chains are tried.\n */\nexport function parseConnection(\n scannedUR: ScannedUR,\n config: QRKitConfig = {},\n): Account[] {\n const chains = config.chains ?? ALL_CHAINS;\n const parsed = parseXpub(scannedUR);\n const accounts: Account[] = [];\n\n if (chains.includes(\"evm\")) {\n const account = deriveEvmAccount(parsed);\n if (account) {\n accounts.push({ chain: \"evm\", ...account } satisfies EvmAccount);\n }\n }\n\n // \"btc\" — will be implemented in src/btc/ when Bitcoin support is added.\n\n return accounts;\n}\n","// Minimal CBOR encoder.\n// Supports only the types needed for eth-sign-request: uint, bytes, text, array, map (integer keys), bool, tag.\n\nfunction majorType(major: number, n: number): number[] {\n const base = major << 5;\n if (n <= 23) return [base | n];\n if (n <= 0xff) return [base | 0x18, n];\n if (n <= 0xffff) return [base | 0x19, (n >> 8) & 0xff, n & 0xff];\n return [base | 0x1a, (n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];\n}\n\nexport function encodeItem(value: unknown): number[] {\n if (typeof value === \"boolean\") {\n return [value ? 0xf5 : 0xf4];\n }\n if (typeof value === \"number\") {\n return majorType(0, value);\n }\n if (value instanceof Uint8Array) {\n return [...majorType(2, value.length), ...value];\n }\n if (typeof value === \"string\") {\n const bytes = new TextEncoder().encode(value);\n return [...majorType(3, bytes.length), ...bytes];\n }\n if (Array.isArray(value)) {\n return [...majorType(4, value.length), ...value.flatMap(encodeItem)];\n }\n if (value instanceof CborTag) {\n return [...majorType(6, value.tag), ...encodeItem(value.value)];\n }\n if (value instanceof Map) {\n const entries = [...value.entries()];\n return [\n ...majorType(5, entries.length),\n ...entries.flatMap(([k, v]) => [...encodeItem(k), ...encodeItem(v)]),\n ];\n }\n throw new Error(`Unsupported CBOR value: ${typeof value}`);\n}\n\nexport class CborTag {\n tag: number;\n value: unknown;\n constructor(tag: number, value: unknown) {\n this.tag = tag;\n this.value = value;\n }\n}\n\nexport function encode(value: unknown): Uint8Array {\n return new Uint8Array(encodeItem(value));\n}\n","import { UR, UREncoder } from \"@ngraveio/bc-ur\";\n\nexport function encodeURParts(\n cbor: Uint8Array,\n type: string,\n maxFragmentLength = 200,\n): string[] {\n // @ngraveio/bc-ur requires a Buffer. In Node.js it is a global; in browsers the\n // bundler must provide the `buffer` polyfill (same requirement as the prototype).\n const ur = new UR(Buffer.from(cbor), type);\n const encoder = new UREncoder(ur, maxFragmentLength);\n return Array.from({ length: encoder.fragmentsLength }, () =>\n encoder.nextPart().toUpperCase(),\n );\n}\n\nexport function encodeURFirstPart(\n cbor: Uint8Array,\n type: string,\n maxFragmentLength = 200,\n): string {\n return encodeURParts(cbor, type, maxFragmentLength)[0];\n}\n","import { encode, CborTag } from \"../cbor.js\";\nimport { encodeURFirstPart, encodeURParts } from \"../urEncoding.js\";\n\n// EIP-191 personal_sign = eth-raw-bytes (ERC-4527 data type 3)\nconst DATA_TYPE_ETH_RAW_BYTES = 3;\n\n// CBOR tag for crypto-keypath\nconst TAG_KEYPATH = 304;\n\nfunction randomBytes(n: number): Uint8Array {\n const buf = new Uint8Array(n);\n crypto.getRandomValues(buf);\n return buf;\n}\n\nfunction buildKeypath(\n purpose: number,\n coinType: number,\n sourceFingerprint: number | undefined,\n): CborTag {\n // m/purpose'/coinType'/0'/0/0\n const components = [purpose, true, coinType, true, 0, true, 0, false, 0, false];\n const keypathMap = new Map<number, unknown>([[1, components]]);\n if (sourceFingerprint !== undefined) {\n keypathMap.set(2, sourceFingerprint);\n }\n return new CborTag(TAG_KEYPATH, keypathMap);\n}\n\nfunction buildEthSignRequestCbor(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin = \"qrkit\",\n): Uint8Array {\n const requestId = randomBytes(16);\n const messageBytes = new TextEncoder().encode(message);\n const keypath = buildKeypath(44, 60, sourceFingerprint);\n\n const addrHex = address.replace(/^0x/i, \"\");\n const addrBytes = new Uint8Array(addrHex.match(/.{2}/g)!.map((b) => parseInt(b, 16)));\n\n const map = new Map<number, unknown>([\n [1, new CborTag(37, requestId)], // request-id: uuid = #6.37(bstr)\n [2, messageBytes], // sign-data\n [3, DATA_TYPE_ETH_RAW_BYTES], // data-type: eth-raw-bytes for EIP-191\n [5, keypath], // derivation-path\n [6, addrBytes], // address\n [7, origin], // origin\n ]);\n\n return encode(map);\n}\n\nexport function buildEthSignRequestURParts(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin?: string,\n): string[] {\n return encodeURParts(\n buildEthSignRequestCbor(message, address, sourceFingerprint, origin),\n \"eth-sign-request\",\n );\n}\n\nexport function buildEthSignRequestUR(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin?: string,\n): string {\n return encodeURFirstPart(\n buildEthSignRequestCbor(message, address, sourceFingerprint, origin),\n \"eth-sign-request\",\n );\n}\n","import { decode as cborDecode, type TagDecoder } from \"cborg\";\n\nimport type { ScannedUR } from \"../types.js\";\n\nexport function parseEthSignature(scanned: ScannedUR): string {\n if (scanned.type !== \"eth-signature\") {\n throw new Error(`Expected eth-signature, got: ${scanned.type}`);\n }\n\n const map = cborDecode(scanned.cbor, {\n useMaps: true,\n tags: Object.assign([] as TagDecoder[], { 37: (v: unknown) => v }),\n }) as Map<number, unknown>;\n\n const sigBytes = map.get(2) as Uint8Array | undefined;\n if (!sigBytes || sigBytes.length < 64) {\n throw new Error(\"Invalid or missing signature bytes\");\n }\n\n return \"0x\" + [...sigBytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"],"mappings":"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","names":["cborDecode"]}
|
|
1
|
+
{"version":3,"sources":["../src/parseXpub.ts","../src/eth/address.ts","../src/eth/deriveAccount.ts","../src/parseConnection.ts","../src/cbor.ts","../src/urEncoding.ts","../src/eth/signRequest.ts","../src/eth/signature.ts"],"sourcesContent":["import { HDKey } from \"@scure/bip32\";\nimport { decode as cborDecode, type TagDecoder } from \"cborg\";\n\nimport type { ScannedUR } from \"./types.js\";\n\nexport type XpubType = \"xpub\";\n\nexport interface ParsedXpub {\n hdKey: HDKey;\n type: XpubType;\n /** BIP-44 purpose index: 44 for EVM */\n purpose: number | undefined;\n /** BIP-44 coin type: 60 = ETH */\n coinType: number | undefined;\n /** source-fingerprint from the origin keypath — required by Shell for signing */\n sourceFingerprint: number | undefined;\n raw: string;\n}\n\nfunction get(m: unknown, k: number): unknown {\n if (m instanceof Map) return (m as Map<number, unknown>).get(k);\n return undefined;\n}\n\nconst passthrough = (v: unknown) => v;\n\nfunction decodeCbor(cbor: Uint8Array): Map<number, unknown> {\n return cborDecode(cbor, {\n useMaps: true,\n tags: Object.assign([] as TagDecoder[], {\n 303: passthrough, // crypto-hdkey\n 304: passthrough, // crypto-keypath\n 305: passthrough, // crypto-coin-info\n }),\n }) as Map<number, unknown>;\n}\n\nfunction parseCryptoHdKey(map: unknown, raw: string): ParsedXpub {\n const keyData = get(map, 3) as Uint8Array | undefined;\n const chainCode = get(map, 4) as Uint8Array | undefined;\n\n if (!keyData || !chainCode) {\n throw new Error(\"crypto-hdkey missing key-data or chain-code\");\n }\n\n let purpose: number | undefined;\n let coinType: number | undefined;\n let sourceFingerprint: number | undefined;\n const origin = get(map, 6);\n if (origin) {\n const components = get(origin, 1);\n if (Array.isArray(components)) {\n if (components.length >= 1) purpose = components[0] as number;\n if (components.length >= 3) coinType = components[2] as number;\n }\n sourceFingerprint = get(origin, 2) as number | undefined;\n }\n\n const hdKey = new HDKey({ publicKey: keyData, chainCode });\n return { hdKey, type: \"xpub\", purpose, coinType, sourceFingerprint, raw };\n}\n\nfunction parseScannedUR(scanned: ScannedUR): ParsedXpub | ParsedXpub[] {\n const { type, cbor } = scanned;\n const raw = `ur:${type}`;\n\n if (type !== \"crypto-hdkey\" && type !== \"crypto-account\") {\n throw new Error(`Unsupported UR type: ${type}`);\n }\n\n const map = decodeCbor(cbor);\n\n if (type === \"crypto-hdkey\") {\n return parseCryptoHdKey(map, raw);\n }\n\n const accounts = map.get(2) as unknown[] | undefined;\n if (!Array.isArray(accounts) || accounts.length === 0) {\n throw new Error(\"crypto-account contains no keys\");\n }\n return accounts.map((entry) => parseCryptoHdKey(entry, raw));\n}\n\nexport function parseXpub(input: ScannedUR | string): ParsedXpub[] {\n if (typeof input !== \"string\") {\n const result = parseScannedUR(input);\n return Array.isArray(result) ? result : [result];\n }\n // Raw base58 xpub string — @scure/bip32 handles decoding directly\n const hdKey = HDKey.fromExtendedKey(input.trim());\n return [\n {\n hdKey,\n type: \"xpub\",\n purpose: undefined,\n coinType: undefined,\n sourceFingerprint: undefined,\n raw: input.trim(),\n },\n ];\n}\n","import { keccak_256 } from \"@noble/hashes/sha3.js\";\nimport * as secp from \"@noble/secp256k1\";\n\nexport function pubKeyToEthAddress(compressedPubKey: Uint8Array): string {\n const uncompressed = secp.Point.fromBytes(compressedPubKey).toBytes(false);\n const hash = keccak_256(uncompressed.slice(1));\n const hex = [...hash.slice(12)]\n .map((b: number) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n return toChecksumAddress(hex);\n}\n\nfunction toChecksumAddress(hex: string): string {\n const checksumHash = keccak_256(new TextEncoder().encode(hex));\n return (\n \"0x\" +\n [...hex]\n .map((c, i) => {\n if (c >= \"0\" && c <= \"9\") return c;\n const nibble =\n i % 2 === 0\n ? (checksumHash[Math.floor(i / 2)] >> 4) & 0xf\n : checksumHash[Math.floor(i / 2)] & 0xf;\n return nibble >= 8 ? c.toUpperCase() : c.toLowerCase();\n })\n .join(\"\")\n );\n}\n","import type { HDKey } from \"@scure/bip32\";\n\nimport type { ParsedXpub } from \"../parseXpub.js\";\nimport { pubKeyToEthAddress } from \"./address.js\";\n\nexport interface DerivedAccount {\n address: string;\n publicKey: string;\n /** source-fingerprint from the scanned xpub — required by Shell for signing */\n sourceFingerprint: number | undefined;\n}\n\n// Derive the first external address (index 0) from an account-level xpub.\n// Account-level xpub is at depth 3 (m/purpose'/coin'/account').\n// External chain is child 0, then address index 0.\nfunction firstChild(accountKey: HDKey): HDKey {\n return accountKey.deriveChild(0).deriveChild(0);\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function deriveEvmAccount(parsed: ParsedXpub[]): DerivedAccount | undefined {\n for (const entry of parsed) {\n const { hdKey, purpose, coinType, type, sourceFingerprint } = entry;\n const isEvm =\n (purpose === 44 && coinType === 60) || (purpose === undefined && type === \"xpub\");\n\n if (!isEvm) continue;\n\n const child = firstChild(hdKey);\n if (!child.publicKey) continue;\n\n return {\n address: pubKeyToEthAddress(child.publicKey),\n publicKey: toHex(child.publicKey),\n sourceFingerprint,\n };\n }\n return undefined;\n}\n","import { parseXpub } from \"./parseXpub.js\";\nimport { deriveEvmAccount } from \"./eth/deriveAccount.js\";\nimport type { Account, Chain, EvmAccount, QRKitConfig, ScannedUR } from \"./types.js\";\n\nconst ALL_CHAINS: Chain[] = [\"evm\", \"btc\"];\n\n/**\n * Parse a connection QR (crypto-hdkey or crypto-account) and return\n * only the accounts for the chains configured in QRKitConfig.\n *\n * A dApp configured with `chains: [\"evm\"]` will never see BTC accounts,\n * and vice versa. Both chains can be enabled with `chains: [\"evm\", \"btc\"]`.\n * If `chains` is omitted, all supported chains are tried.\n */\nexport function parseConnection(\n scannedUR: ScannedUR,\n config: QRKitConfig = {},\n): Account[] {\n const chains = config.chains ?? ALL_CHAINS;\n const parsed = parseXpub(scannedUR);\n const accounts: Account[] = [];\n\n if (chains.includes(\"evm\")) {\n const account = deriveEvmAccount(parsed);\n if (account) {\n accounts.push({ chain: \"evm\", ...account } satisfies EvmAccount);\n }\n }\n\n // \"btc\" — will be implemented in src/btc/ when Bitcoin support is added.\n\n return accounts;\n}\n","// Minimal CBOR encoder.\n// Supports only the types needed for eth-sign-request: uint, bytes, text, array, map (integer keys), bool, tag.\n\nfunction majorType(major: number, n: number): number[] {\n const base = major << 5;\n if (n <= 23) return [base | n];\n if (n <= 0xff) return [base | 0x18, n];\n if (n <= 0xffff) return [base | 0x19, (n >> 8) & 0xff, n & 0xff];\n return [base | 0x1a, (n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];\n}\n\nexport function encodeItem(value: unknown): number[] {\n if (typeof value === \"boolean\") {\n return [value ? 0xf5 : 0xf4];\n }\n if (typeof value === \"number\") {\n return majorType(0, value);\n }\n if (value instanceof Uint8Array) {\n return [...majorType(2, value.length), ...value];\n }\n if (typeof value === \"string\") {\n const bytes = new TextEncoder().encode(value);\n return [...majorType(3, bytes.length), ...bytes];\n }\n if (Array.isArray(value)) {\n return [...majorType(4, value.length), ...value.flatMap(encodeItem)];\n }\n if (value instanceof CborTag) {\n return [...majorType(6, value.tag), ...encodeItem(value.value)];\n }\n if (value instanceof Map) {\n const entries = [...value.entries()];\n return [\n ...majorType(5, entries.length),\n ...entries.flatMap(([k, v]) => [...encodeItem(k), ...encodeItem(v)]),\n ];\n }\n throw new Error(`Unsupported CBOR value: ${typeof value}`);\n}\n\nexport class CborTag {\n tag: number;\n value: unknown;\n constructor(tag: number, value: unknown) {\n this.tag = tag;\n this.value = value;\n }\n}\n\nexport function encode(value: unknown): Uint8Array {\n return new Uint8Array(encodeItem(value));\n}\n","import { UR, UrFountainEncoder } from \"@qrkit/bc-ur\";\n\nexport function encodeURParts(\n cbor: Uint8Array,\n type: string,\n maxFragmentLength = 200,\n): string[] {\n const ur = UR.fromCbor({ type, payload: cbor });\n const encoder = new UrFountainEncoder(ur, maxFragmentLength);\n return encoder.getAllPartsUr().map((part) => part.toString().toUpperCase());\n}\n","import { encode, CborTag } from \"../cbor.js\";\nimport { encodeURParts } from \"../urEncoding.js\";\n\n// EIP-191 personal_sign = eth-raw-bytes (ERC-4527 data type 3)\nconst DATA_TYPE_ETH_RAW_BYTES = 3;\n\n// CBOR tag for crypto-keypath\nconst TAG_KEYPATH = 304;\n\nfunction randomBytes(n: number): Uint8Array {\n const buf = new Uint8Array(n);\n crypto.getRandomValues(buf);\n return buf;\n}\n\nfunction buildKeypath(\n purpose: number,\n coinType: number,\n sourceFingerprint: number | undefined,\n): CborTag {\n // m/purpose'/coinType'/0'/0/0\n const components = [purpose, true, coinType, true, 0, true, 0, false, 0, false];\n const keypathMap = new Map<number, unknown>([[1, components]]);\n if (sourceFingerprint !== undefined) {\n keypathMap.set(2, sourceFingerprint);\n }\n return new CborTag(TAG_KEYPATH, keypathMap);\n}\n\nfunction buildEthSignRequestCbor(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin = \"qrkit\",\n): Uint8Array {\n const requestId = randomBytes(16);\n const messageBytes = new TextEncoder().encode(message);\n const keypath = buildKeypath(44, 60, sourceFingerprint);\n\n const addrHex = address.replace(/^0x/i, \"\");\n const addrBytes = new Uint8Array(addrHex.match(/.{2}/g)!.map((b) => parseInt(b, 16)));\n\n const map = new Map<number, unknown>([\n [1, new CborTag(37, requestId)], // request-id: uuid = #6.37(bstr)\n [2, messageBytes], // sign-data\n [3, DATA_TYPE_ETH_RAW_BYTES], // data-type: eth-raw-bytes for EIP-191\n [5, keypath], // derivation-path\n [6, addrBytes], // address\n [7, origin], // origin\n ]);\n\n return encode(map);\n}\n\nexport function buildEthSignRequestURParts(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin?: string,\n): string[] {\n return encodeURParts(\n buildEthSignRequestCbor(message, address, sourceFingerprint, origin),\n \"eth-sign-request\",\n );\n}\n\nexport function buildEthSignRequestUR(\n message: string,\n address: string,\n sourceFingerprint: number | undefined,\n origin?: string,\n): string {\n return encodeURParts(\n buildEthSignRequestCbor(message, address, sourceFingerprint, origin),\n \"eth-sign-request\",\n )[0];\n}\n","import { decode as cborDecode, type TagDecoder } from \"cborg\";\n\nimport type { ScannedUR } from \"../types.js\";\n\nexport function parseEthSignature(scanned: ScannedUR): string {\n if (scanned.type !== \"eth-signature\") {\n throw new Error(`Expected eth-signature, got: ${scanned.type}`);\n }\n\n const map = cborDecode(scanned.cbor, {\n useMaps: true,\n tags: Object.assign([] as TagDecoder[], { 37: (v: unknown) => v }),\n }) as Map<number, unknown>;\n\n const sigBytes = map.get(2) as Uint8Array | undefined;\n if (!sigBytes || sigBytes.length < 64) {\n throw new Error(\"Invalid or missing signature bytes\");\n }\n\n return \"0x\" + [...sigBytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"],"mappings":"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","names":["cborDecode"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qrkit/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Protocol core for QR-based airgapped wallet flows — UR decoding, xpub parsing, address derivation, sign request and signature handling.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -18,20 +18,12 @@
|
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@
|
|
21
|
+
"@qrkit/bc-ur": "2.0.0-beta.9-qrkit.2",
|
|
22
22
|
"@noble/hashes": "^2.0.1",
|
|
23
23
|
"@noble/secp256k1": "^3.0.0",
|
|
24
24
|
"@scure/bip32": "^2.0.1",
|
|
25
25
|
"cborg": "^4.5.8"
|
|
26
26
|
},
|
|
27
|
-
"peerDependencies": {
|
|
28
|
-
"buffer": ">=6"
|
|
29
|
-
},
|
|
30
|
-
"peerDependenciesMeta": {
|
|
31
|
-
"buffer": {
|
|
32
|
-
"optional": true
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
27
|
"devDependencies": {
|
|
36
28
|
"eslint": "^10.2.0",
|
|
37
29
|
"tsup": "^8.4.0",
|