@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.
- package/README.md +25 -5
- package/cardano/package.json +6 -0
- package/dist/cardano-DP6U_K1J.cjs +105 -0
- package/dist/cardano-DP6U_K1J.cjs.map +1 -0
- package/dist/cardano-Do1ahUlK.d.cts +62 -0
- package/dist/cardano-OzjWZHh6.d.ts +62 -0
- package/dist/cardano-yuOygqwp.js +94 -0
- package/dist/cardano-yuOygqwp.js.map +1 -0
- package/dist/cardano.cjs +10 -0
- package/dist/cardano.d.cts +3 -0
- package/dist/cardano.d.ts +3 -0
- package/dist/cardano.js +3 -0
- package/dist/derive-C4JdycUh.js +117 -0
- package/dist/derive-C4JdycUh.js.map +1 -0
- package/dist/derive-CuILJRPT.cjs +176 -0
- package/dist/derive-CuILJRPT.cjs.map +1 -0
- package/dist/index.cjs +133 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -7
- package/dist/index.d.ts +64 -7
- package/dist/index.js +120 -85
- package/dist/index.js.map +1 -1
- package/dist/ton-5Uvi5ifg.js +86 -0
- package/dist/ton-5Uvi5ifg.js.map +1 -0
- package/dist/ton-C0UP-cf_.d.ts +51 -0
- package/dist/ton-DUJGXdGa.cjs +97 -0
- package/dist/ton-DUJGXdGa.cjs.map +1 -0
- package/dist/ton-DmeIvknN.d.cts +51 -0
- package/dist/ton.cjs +10 -0
- package/dist/ton.d.cts +3 -0
- package/dist/ton.d.ts +3 -0
- package/dist/ton.js +3 -0
- package/dist/verify.cjs +302 -13
- package/dist/verify.cjs.map +1 -1
- package/dist/verify.d.cts +56 -1
- package/dist/verify.d.ts +56 -1
- package/dist/verify.js +301 -15
- package/dist/verify.js.map +1 -1
- package/package.json +32 -8
- package/ton/package.json +6 -0
package/README.md
CHANGED
|
@@ -66,16 +66,36 @@ verifyEvmSignature({ signData: rlpBytes, dataType: 1,
|
|
|
66
66
|
|
|
67
67
|
## Chain support
|
|
68
68
|
|
|
69
|
+
The ERA device is multichain; the SDK ships dedicated modules for four chains
|
|
70
|
+
today and speaks the rest through the same primitives.
|
|
71
|
+
|
|
72
|
+
**Dedicated modules:**
|
|
73
|
+
|
|
69
74
|
| Chain | Sign transaction | Sign message | Subpath |
|
|
70
75
|
|---|---|---|---|
|
|
71
76
|
| EVM (all chains) | `eth-sign-request` | personal_sign / EIP-712 | `@hwlt/era-connect/evm` |
|
|
72
77
|
| Bitcoin | `crypto-psbt` (PSBT v0) | legacy P2PKH only | `@hwlt/era-connect/btc` |
|
|
73
78
|
| Solana | `sol-sign-request` | off-chain messages | `@hwlt/era-connect/solana` |
|
|
74
|
-
| Tron |
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
| Tron | structured envelope (any contract via `rawData`) | UTF-8 in `rawData` | `@hwlt/era-connect/tron` |
|
|
80
|
+
| TON | `ton-sign-request` (BoC root-hash signing) | TON Connect proof | `@hwlt/era-connect/ton` |
|
|
81
|
+
| Cardano | `cardano-sign-request` (witness-set replies, soft-derived vkey binding) | — | `@hwlt/era-connect/cardano` |
|
|
82
|
+
|
|
83
|
+
**Also supported by the device** — usable today via `era.raw` +
|
|
84
|
+
`era.scanner({expectedTypes})`, dedicated modules on the roadmap (order driven
|
|
85
|
+
by integrator demand):
|
|
86
|
+
|
|
87
|
+
| Chain | Wire type | Module status |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| Cosmos (~35 networks, incl. Ethermint: INJ/EVMOS/DYM) | `cosmos-sign-request` / `evm-sign-request` | planned |
|
|
90
|
+
| Sui | `sui-sign-request` / `sui-sign-hash-request` | planned |
|
|
91
|
+
| Dogecoin | `crypto-psbt-extend` | planned |
|
|
92
|
+
| Litecoin, Dash, Bitcoin Cash | structured envelope (as Tron) | planned |
|
|
93
|
+
| XRP | `ur:bytes` (XRP Toolkit convention) | planned |
|
|
94
|
+
|
|
95
|
+
Two things make the gap workable now: **linking already surfaces every account
|
|
96
|
+
the device exports** (unknown chains still carry their path, fingerprint and
|
|
97
|
+
public key), and the scanner/fountain layer is chain-agnostic. The device's
|
|
98
|
+
chain list keeps growing — confirm the current set with the ERA team.
|
|
79
99
|
|
|
80
100
|
## Documentation
|
|
81
101
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const require_shared = require("./shared-nISrEktU.cjs");
|
|
2
|
+
//#region src/chains/cardano.ts
|
|
3
|
+
const REPLY_TYPES = ["cardano-signature"];
|
|
4
|
+
const UTXO_TAG = 2201;
|
|
5
|
+
const CERT_KEY_TAG = 2204;
|
|
6
|
+
var CardanoChain = class {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.context = require_shared.resolveContext(config);
|
|
9
|
+
}
|
|
10
|
+
/** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */
|
|
11
|
+
generateSignRequest(props) {
|
|
12
|
+
const requestId = require_shared.resolveRequestId(this.context, props.requestId);
|
|
13
|
+
if (props.signData.length === 0) throw new require_shared.EraSdkError("invalid-props", "signData must not be empty");
|
|
14
|
+
if (props.utxos.length === 0) throw new require_shared.EraSdkError("invalid-props", "at least one utxo is required");
|
|
15
|
+
const utxos = props.utxos.map((utxo) => {
|
|
16
|
+
if (utxo.transactionHash.length !== 32) throw new require_shared.EraSdkError("invalid-props", "utxo transactionHash must be 32 bytes");
|
|
17
|
+
if (!Number.isSafeInteger(utxo.index) || utxo.index < 0) throw new require_shared.EraSdkError("invalid-props", "utxo index must be a non-negative integer");
|
|
18
|
+
const entries = [[1, require_shared.cbBytes(utxo.transactionHash)], [2, require_shared.cbUint(utxo.index)]];
|
|
19
|
+
if (utxo.amount !== void 0) entries.push([3, require_shared.cbText(utxo.amount)]);
|
|
20
|
+
entries.push([4, require_shared.keypath304(require_shared.parsePath(utxo.path), require_shared.normalizeXfp(utxo.xfp))]);
|
|
21
|
+
if (utxo.address !== void 0) entries.push([5, require_shared.cbText(utxo.address)]);
|
|
22
|
+
return require_shared.cbTag(UTXO_TAG, require_shared.cbMap(entries));
|
|
23
|
+
});
|
|
24
|
+
const certKeys = (props.certKeys ?? []).map((certKey) => {
|
|
25
|
+
const entries = [];
|
|
26
|
+
if (certKey.keyHash !== void 0) entries.push([1, require_shared.cbBytes(certKey.keyHash)]);
|
|
27
|
+
entries.push([2, require_shared.keypath304(require_shared.parsePath(certKey.path), require_shared.normalizeXfp(certKey.xfp))]);
|
|
28
|
+
return require_shared.cbTag(CERT_KEY_TAG, require_shared.cbMap(entries));
|
|
29
|
+
});
|
|
30
|
+
const entries = [
|
|
31
|
+
[1, require_shared.cbTag(37, require_shared.cbBytes(requestId))],
|
|
32
|
+
[2, require_shared.cbBytes(props.signData)],
|
|
33
|
+
[3, require_shared.cbArray(utxos)]
|
|
34
|
+
];
|
|
35
|
+
if (certKeys.length > 0) entries.push([4, require_shared.cbArray(certKeys)]);
|
|
36
|
+
entries.push([5, require_shared.cbText(props.origin ?? this.context.origin)]);
|
|
37
|
+
const ur = new require_shared.Ur("cardano-sign-request", require_shared.cborEncode(require_shared.cbMap(entries)));
|
|
38
|
+
return require_shared.makeSignRequest({
|
|
39
|
+
ur,
|
|
40
|
+
requestId,
|
|
41
|
+
replyTypes: REPLY_TYPES,
|
|
42
|
+
context: this.context,
|
|
43
|
+
parse: (reply) => parseCardanoSignature(reply, requestId)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */
|
|
47
|
+
parseSignature(input, expect) {
|
|
48
|
+
return parseCardanoSignature(require_shared.toUr(input), expect?.requestId === void 0 ? void 0 : require_shared.normalizeRequestId(expect.requestId));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function parseCardanoSignature(ur, expectedRequestId) {
|
|
52
|
+
require_shared.requireUrType(ur, [...REPLY_TYPES], "cardano-signature");
|
|
53
|
+
const map = require_shared.requireReplyMap(ur, "cardano-signature");
|
|
54
|
+
const requestId = require_shared.requireRequestIdEcho(map, 1, expectedRequestId, "cardano-signature");
|
|
55
|
+
const witnessSet = require_shared.asBytes(require_shared.mapGet(map, 2));
|
|
56
|
+
if (!witnessSet || witnessSet.length === 0) throw new require_shared.EraSdkError("malformed-reply", "cardano-signature is missing the witness set (key 2)");
|
|
57
|
+
return {
|
|
58
|
+
requestId,
|
|
59
|
+
witnessSet,
|
|
60
|
+
witnesses: parseWitnessSet(witnessSet)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */
|
|
64
|
+
function parseWitnessSet(witnessSet) {
|
|
65
|
+
let decoded;
|
|
66
|
+
try {
|
|
67
|
+
decoded = require_shared.cborDecode(witnessSet);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
throw new require_shared.EraSdkError("malformed-reply", `witness set is not readable CBOR: ${e.message}`);
|
|
70
|
+
}
|
|
71
|
+
const root = require_shared.stripTags(decoded);
|
|
72
|
+
if (root.kind !== "map") throw new require_shared.EraSdkError("malformed-reply", "witness set is not a CBOR map");
|
|
73
|
+
const vkeyWitnesses = require_shared.mapGet(root, 0);
|
|
74
|
+
const list = vkeyWitnesses === void 0 ? void 0 : require_shared.stripTags(vkeyWitnesses);
|
|
75
|
+
if (list?.kind !== "array") throw new require_shared.EraSdkError("malformed-reply", "witness set carries no vkey witnesses (key 0)");
|
|
76
|
+
const witnesses = [];
|
|
77
|
+
for (const item of list.items) {
|
|
78
|
+
const pair = require_shared.stripTags(item);
|
|
79
|
+
if (pair.kind !== "array" || pair.items.length < 2) throw new require_shared.EraSdkError("malformed-reply", "malformed vkey witness");
|
|
80
|
+
const vkey = require_shared.asBytes(pair.items[0]);
|
|
81
|
+
const signature = require_shared.asBytes(pair.items[1]);
|
|
82
|
+
if (!vkey || vkey.length !== 32 || !signature || signature.length !== 64) throw new require_shared.EraSdkError("malformed-reply", "vkey witness is not [32-byte key, 64-byte signature]");
|
|
83
|
+
witnesses.push({
|
|
84
|
+
vkey,
|
|
85
|
+
signature
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (witnesses.length === 0) throw new require_shared.EraSdkError("malformed-reply", "witness set is empty");
|
|
89
|
+
return witnesses;
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
Object.defineProperty(exports, "CardanoChain", {
|
|
93
|
+
enumerable: true,
|
|
94
|
+
get: function() {
|
|
95
|
+
return CardanoChain;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
Object.defineProperty(exports, "parseWitnessSet", {
|
|
99
|
+
enumerable: true,
|
|
100
|
+
get: function() {
|
|
101
|
+
return parseWitnessSet;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=cardano-DP6U_K1J.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cardano-DP6U_K1J.cjs","names":["resolveContext","resolveRequestId","EraSdkError","cbBytes","cbUint","cbText","keypath304","parsePath","normalizeXfp","cbTag","cbMap","cbArray","UrValue","cborEncode","makeSignRequest","toUr","normalizeRequestId","requireReplyMap","requireRequestIdEcho","asBytes","mapGet","cborDecode","stripTags"],"sources":["../src/chains/cardano.ts"],"sourcesContent":["import { cborDecode } from '../cbor/decode';\nimport { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport {\n asBytes,\n cbArray,\n cbBytes,\n cbMap,\n cbTag,\n cbText,\n cbUint,\n mapGet,\n stripTags,\n} from '../cbor/model';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId } from '../core/rand';\nimport { keypath304, normalizeXfp, parsePath } from '../registry/keypath';\nimport type { Ur } from '../ur/ur';\nimport { Ur as UrValue } from '../ur/ur';\nimport type { ChainContext, EraConnectConfig, ExpectedReply, SignRequest } from './shared';\nimport {\n makeSignRequest,\n requireReplyMap,\n requireRequestIdEcho,\n requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\n/** One transaction input the device must sign for. */\nexport interface CardanoUtxoRef {\n /** 32-byte hash of the transaction that created the UTXO. */\n readonly transactionHash: Uint8Array;\n readonly index: number;\n /** Full signing path for this input, e.g. `m/1852'/1815'/0'/0/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /** Lovelace amount as a decimal string — device display. */\n readonly amount?: string;\n /** Bech32 address of the UTXO — device display. */\n readonly address?: string;\n}\n\n/** A certificate/withdrawal key the transaction additionally needs a witness from. */\nexport interface CardanoCertKeyRef {\n /** Full signing path, e.g. the stake key `m/1852'/1815'/0'/2/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /** 28-byte key hash — device display/matching. */\n readonly keyHash?: Uint8Array;\n}\n\nexport interface CardanoSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /**\n * The FULL transaction CBOR (`[body, witness_set, is_valid, aux_data]`) as\n * your Cardano tooling serializes it. The device extracts the body (first\n * array element) and signs its BLAKE2b-256 hash.\n */\n readonly signData: Uint8Array;\n /** At least one; the device signs once per UNIQUE path across utxos + certKeys. */\n readonly utxos: readonly CardanoUtxoRef[];\n readonly certKeys?: readonly CardanoCertKeyRef[];\n readonly origin?: string;\n}\n\nexport interface CardanoWitness {\n /** 32-byte verification key. */\n readonly vkey: Uint8Array;\n /** 64-byte Ed25519 signature over the tx-body BLAKE2b-256 hash. */\n readonly signature: Uint8Array;\n}\n\nexport interface CardanoSignatureResult {\n readonly requestId: Uint8Array;\n /** The witness-set CBOR verbatim (`{0: #6.258([[vkey, sig]…])}`) — merge it into your tx. */\n readonly witnessSet: Uint8Array;\n /** The `[vkey, signature]` pairs parsed out of the witness set. */\n readonly witnesses: readonly CardanoWitness[];\n}\n\nconst REPLY_TYPES = ['cardano-signature'] as const;\nconst UTXO_TAG = 2201;\nconst CERT_KEY_TAG = 2204;\n\nexport class CardanoChain {\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */\n generateSignRequest(props: CardanoSignRequestProps): SignRequest<CardanoSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n if (props.signData.length === 0) {\n throw new EraSdkError('invalid-props', 'signData must not be empty');\n }\n if (props.utxos.length === 0) {\n throw new EraSdkError('invalid-props', 'at least one utxo is required');\n }\n\n const utxos = props.utxos.map((utxo) => {\n if (utxo.transactionHash.length !== 32) {\n throw new EraSdkError('invalid-props', 'utxo transactionHash must be 32 bytes');\n }\n if (!Number.isSafeInteger(utxo.index) || utxo.index < 0) {\n throw new EraSdkError('invalid-props', 'utxo index must be a non-negative integer');\n }\n const entries: [number, CborValue][] = [\n [1, cbBytes(utxo.transactionHash)],\n [2, cbUint(utxo.index)],\n ];\n if (utxo.amount !== undefined) entries.push([3, cbText(utxo.amount)]);\n entries.push([4, keypath304(parsePath(utxo.path), normalizeXfp(utxo.xfp))]);\n if (utxo.address !== undefined) entries.push([5, cbText(utxo.address)]);\n return cbTag(UTXO_TAG, cbMap(entries));\n });\n\n const certKeys = (props.certKeys ?? []).map((certKey) => {\n const entries: [number, CborValue][] = [];\n if (certKey.keyHash !== undefined) entries.push([1, cbBytes(certKey.keyHash)]);\n entries.push([2, keypath304(parsePath(certKey.path), normalizeXfp(certKey.xfp))]);\n return cbTag(CERT_KEY_TAG, cbMap(entries));\n });\n\n const entries: [number, CborValue][] = [\n [1, cbTag(37, cbBytes(requestId))],\n [2, cbBytes(props.signData)],\n [3, cbArray(utxos)],\n ];\n if (certKeys.length > 0) entries.push([4, cbArray(certKeys)]);\n entries.push([5, cbText(props.origin ?? this.context.origin)]);\n\n const ur = new UrValue('cardano-sign-request', cborEncode(cbMap(entries)));\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseCardanoSignature(reply, requestId),\n });\n }\n\n /** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseSignature(input: Ur | string, expect?: ExpectedReply): CardanoSignatureResult {\n return parseCardanoSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseCardanoSignature(\n ur: Ur,\n expectedRequestId: Uint8Array | undefined,\n): CardanoSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'cardano-signature');\n const map = requireReplyMap(ur, 'cardano-signature');\n const requestId = requireRequestIdEcho(map, 1, expectedRequestId, 'cardano-signature');\n\n const witnessSet = asBytes(mapGet(map, 2));\n if (!witnessSet || witnessSet.length === 0) {\n throw new EraSdkError(\n 'malformed-reply',\n 'cardano-signature is missing the witness set (key 2)',\n );\n }\n return { requestId, witnessSet, witnesses: parseWitnessSet(witnessSet) };\n}\n\n/** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */\nexport function parseWitnessSet(witnessSet: Uint8Array): CardanoWitness[] {\n let decoded: CborValue;\n try {\n decoded = cborDecode(witnessSet);\n } catch (e) {\n throw new EraSdkError(\n 'malformed-reply',\n `witness set is not readable CBOR: ${(e as Error).message}`,\n );\n }\n const root = stripTags(decoded);\n if (root.kind !== 'map') {\n throw new EraSdkError('malformed-reply', 'witness set is not a CBOR map');\n }\n const vkeyWitnesses = mapGet(root, 0);\n const list = vkeyWitnesses === undefined ? undefined : stripTags(vkeyWitnesses);\n if (list?.kind !== 'array') {\n throw new EraSdkError('malformed-reply', 'witness set carries no vkey witnesses (key 0)');\n }\n const witnesses: CardanoWitness[] = [];\n for (const item of list.items) {\n const pair = stripTags(item);\n if (pair.kind !== 'array' || pair.items.length < 2) {\n throw new EraSdkError('malformed-reply', 'malformed vkey witness');\n }\n const vkey = asBytes(pair.items[0]);\n const signature = asBytes(pair.items[1]);\n if (!vkey || vkey.length !== 32 || !signature || signature.length !== 64) {\n throw new EraSdkError(\n 'malformed-reply',\n 'vkey witness is not [32-byte key, 64-byte signature]',\n );\n }\n witnesses.push({ vkey, signature });\n }\n if (witnesses.length === 0) {\n throw new EraSdkError('malformed-reply', 'witness set is empty');\n }\n return witnesses;\n}\n"],"mappings":";;AAkFA,MAAM,cAAc,CAAC,mBAAmB;AACxC,MAAM,WAAW;AACjB,MAAM,eAAe;AAErB,IAAa,eAAb,MAA0B;CAGxB,YAAY,QAA2B;EACrC,KAAK,UAAUA,eAAAA,eAAe,MAAM;CACtC;;CAGA,oBAAoB,OAAqE;EACvF,MAAM,YAAYC,eAAAA,iBAAiB,KAAK,SAAS,MAAM,SAAS;EAChE,IAAI,MAAM,SAAS,WAAW,GAC5B,MAAM,IAAIC,eAAAA,YAAY,iBAAiB,4BAA4B;EAErE,IAAI,MAAM,MAAM,WAAW,GACzB,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,+BAA+B;EAGxE,MAAM,QAAQ,MAAM,MAAM,KAAK,SAAS;GACtC,IAAI,KAAK,gBAAgB,WAAW,IAClC,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,uCAAuC;GAEhF,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,KAAK,KAAK,QAAQ,GACpD,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,2CAA2C;GAEpF,MAAM,UAAiC,CACrC,CAAC,GAAGC,eAAAA,QAAQ,KAAK,eAAe,CAAC,GACjC,CAAC,GAAGC,eAAAA,OAAO,KAAK,KAAK,CAAC,CACxB;GACA,IAAI,KAAK,WAAW,KAAA,GAAW,QAAQ,KAAK,CAAC,GAAGC,eAAAA,OAAO,KAAK,MAAM,CAAC,CAAC;GACpE,QAAQ,KAAK,CAAC,GAAGC,eAAAA,WAAWC,eAAAA,UAAU,KAAK,IAAI,GAAGC,eAAAA,aAAa,KAAK,GAAG,CAAC,CAAC,CAAC;GAC1E,IAAI,KAAK,YAAY,KAAA,GAAW,QAAQ,KAAK,CAAC,GAAGH,eAAAA,OAAO,KAAK,OAAO,CAAC,CAAC;GACtE,OAAOI,eAAAA,MAAM,UAAUC,eAAAA,MAAM,OAAO,CAAC;EACvC,CAAC;EAED,MAAM,YAAY,MAAM,YAAY,CAAC,EAAA,CAAG,KAAK,YAAY;GACvD,MAAM,UAAiC,CAAC;GACxC,IAAI,QAAQ,YAAY,KAAA,GAAW,QAAQ,KAAK,CAAC,GAAGP,eAAAA,QAAQ,QAAQ,OAAO,CAAC,CAAC;GAC7E,QAAQ,KAAK,CAAC,GAAGG,eAAAA,WAAWC,eAAAA,UAAU,QAAQ,IAAI,GAAGC,eAAAA,aAAa,QAAQ,GAAG,CAAC,CAAC,CAAC;GAChF,OAAOC,eAAAA,MAAM,cAAcC,eAAAA,MAAM,OAAO,CAAC;EAC3C,CAAC;EAED,MAAM,UAAiC;GACrC,CAAC,GAAGD,eAAAA,MAAM,IAAIN,eAAAA,QAAQ,SAAS,CAAC,CAAC;GACjC,CAAC,GAAGA,eAAAA,QAAQ,MAAM,QAAQ,CAAC;GAC3B,CAAC,GAAGQ,eAAAA,QAAQ,KAAK,CAAC;EACpB;EACA,IAAI,SAAS,SAAS,GAAG,QAAQ,KAAK,CAAC,GAAGA,eAAAA,QAAQ,QAAQ,CAAC,CAAC;EAC5D,QAAQ,KAAK,CAAC,GAAGN,eAAAA,OAAO,MAAM,UAAU,KAAK,QAAQ,MAAM,CAAC,CAAC;EAE7D,MAAM,KAAK,IAAIO,eAAAA,GAAQ,wBAAwBC,eAAAA,WAAWH,eAAAA,MAAM,OAAO,CAAC,CAAC;EACzE,OAAOI,eAAAA,gBAAgB;GACrB;GACA;GACA,YAAY;GACZ,SAAS,KAAK;GACd,QAAQ,UAAU,sBAAsB,OAAO,SAAS;EAC1D,CAAC;CACH;;CAGA,eAAe,OAAoB,QAAgD;EACjF,OAAO,sBACLC,eAAAA,KAAK,KAAK,GACV,QAAQ,cAAc,KAAA,IAAY,KAAA,IAAYC,eAAAA,mBAAmB,OAAO,SAAS,CACnF;CACF;AACF;AAEA,SAAS,sBACP,IACA,mBACwB;CACxB,eAAA,cAAc,IAAI,CAAC,GAAG,WAAW,GAAG,mBAAmB;CACvD,MAAM,MAAMC,eAAAA,gBAAgB,IAAI,mBAAmB;CACnD,MAAM,YAAYC,eAAAA,qBAAqB,KAAK,GAAG,mBAAmB,mBAAmB;CAErF,MAAM,aAAaC,eAAAA,QAAQC,eAAAA,OAAO,KAAK,CAAC,CAAC;CACzC,IAAI,CAAC,cAAc,WAAW,WAAW,GACvC,MAAM,IAAIlB,eAAAA,YACR,mBACA,sDACF;CAEF,OAAO;EAAE;EAAW;EAAY,WAAW,gBAAgB,UAAU;CAAE;AACzE;;AAGA,SAAgB,gBAAgB,YAA0C;CACxE,IAAI;CACJ,IAAI;EACF,UAAUmB,eAAAA,WAAW,UAAU;CACjC,SAAS,GAAG;EACV,MAAM,IAAInB,eAAAA,YACR,mBACA,qCAAsC,EAAY,SACpD;CACF;CACA,MAAM,OAAOoB,eAAAA,UAAU,OAAO;CAC9B,IAAI,KAAK,SAAS,OAChB,MAAM,IAAIpB,eAAAA,YAAY,mBAAmB,+BAA+B;CAE1E,MAAM,gBAAgBkB,eAAAA,OAAO,MAAM,CAAC;CACpC,MAAM,OAAO,kBAAkB,KAAA,IAAY,KAAA,IAAYE,eAAAA,UAAU,aAAa;CAC9E,IAAI,MAAM,SAAS,SACjB,MAAM,IAAIpB,eAAAA,YAAY,mBAAmB,+CAA+C;CAE1F,MAAM,YAA8B,CAAC;CACrC,KAAK,MAAM,QAAQ,KAAK,OAAO;EAC7B,MAAM,OAAOoB,eAAAA,UAAU,IAAI;EAC3B,IAAI,KAAK,SAAS,WAAW,KAAK,MAAM,SAAS,GAC/C,MAAM,IAAIpB,eAAAA,YAAY,mBAAmB,wBAAwB;EAEnE,MAAM,OAAOiB,eAAAA,QAAQ,KAAK,MAAM,EAAE;EAClC,MAAM,YAAYA,eAAAA,QAAQ,KAAK,MAAM,EAAE;EACvC,IAAI,CAAC,QAAQ,KAAK,WAAW,MAAM,CAAC,aAAa,UAAU,WAAW,IACpE,MAAM,IAAIjB,eAAAA,YACR,mBACA,sDACF;EAEF,UAAU,KAAK;GAAE;GAAM;EAAU,CAAC;CACpC;CACA,IAAI,UAAU,WAAW,GACvB,MAAM,IAAIA,eAAAA,YAAY,mBAAmB,sBAAsB;CAEjE,OAAO;AACT"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { _ as Ur, a as SignRequest, i as ExpectedReply, r as EraConnectConfig } from "./shared-CI8zhQiE.cjs";
|
|
2
|
+
//#region src/chains/cardano.d.ts
|
|
3
|
+
/** One transaction input the device must sign for. */
|
|
4
|
+
interface CardanoUtxoRef {
|
|
5
|
+
/** 32-byte hash of the transaction that created the UTXO. */
|
|
6
|
+
readonly transactionHash: Uint8Array;
|
|
7
|
+
readonly index: number;
|
|
8
|
+
/** Full signing path for this input, e.g. `m/1852'/1815'/0'/0/0`. */
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly xfp: string | number;
|
|
11
|
+
/** Lovelace amount as a decimal string — device display. */
|
|
12
|
+
readonly amount?: string;
|
|
13
|
+
/** Bech32 address of the UTXO — device display. */
|
|
14
|
+
readonly address?: string;
|
|
15
|
+
}
|
|
16
|
+
/** A certificate/withdrawal key the transaction additionally needs a witness from. */
|
|
17
|
+
interface CardanoCertKeyRef {
|
|
18
|
+
/** Full signing path, e.g. the stake key `m/1852'/1815'/0'/2/0`. */
|
|
19
|
+
readonly path: string;
|
|
20
|
+
readonly xfp: string | number;
|
|
21
|
+
/** 28-byte key hash — device display/matching. */
|
|
22
|
+
readonly keyHash?: Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
interface CardanoSignRequestProps {
|
|
25
|
+
readonly requestId?: Uint8Array | string;
|
|
26
|
+
/**
|
|
27
|
+
* The FULL transaction CBOR (`[body, witness_set, is_valid, aux_data]`) as
|
|
28
|
+
* your Cardano tooling serializes it. The device extracts the body (first
|
|
29
|
+
* array element) and signs its BLAKE2b-256 hash.
|
|
30
|
+
*/
|
|
31
|
+
readonly signData: Uint8Array;
|
|
32
|
+
/** At least one; the device signs once per UNIQUE path across utxos + certKeys. */
|
|
33
|
+
readonly utxos: readonly CardanoUtxoRef[];
|
|
34
|
+
readonly certKeys?: readonly CardanoCertKeyRef[];
|
|
35
|
+
readonly origin?: string;
|
|
36
|
+
}
|
|
37
|
+
interface CardanoWitness {
|
|
38
|
+
/** 32-byte verification key. */
|
|
39
|
+
readonly vkey: Uint8Array;
|
|
40
|
+
/** 64-byte Ed25519 signature over the tx-body BLAKE2b-256 hash. */
|
|
41
|
+
readonly signature: Uint8Array;
|
|
42
|
+
}
|
|
43
|
+
interface CardanoSignatureResult {
|
|
44
|
+
readonly requestId: Uint8Array;
|
|
45
|
+
/** The witness-set CBOR verbatim (`{0: #6.258([[vkey, sig]…])}`) — merge it into your tx. */
|
|
46
|
+
readonly witnessSet: Uint8Array;
|
|
47
|
+
/** The `[vkey, signature]` pairs parsed out of the witness set. */
|
|
48
|
+
readonly witnesses: readonly CardanoWitness[];
|
|
49
|
+
}
|
|
50
|
+
declare class CardanoChain {
|
|
51
|
+
private readonly context;
|
|
52
|
+
constructor(config?: EraConnectConfig);
|
|
53
|
+
/** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */
|
|
54
|
+
generateSignRequest(props: CardanoSignRequestProps): SignRequest<CardanoSignatureResult>;
|
|
55
|
+
/** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */
|
|
56
|
+
parseSignature(input: Ur | string, expect?: ExpectedReply): CardanoSignatureResult;
|
|
57
|
+
}
|
|
58
|
+
/** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */
|
|
59
|
+
declare function parseWitnessSet(witnessSet: Uint8Array): CardanoWitness[];
|
|
60
|
+
//#endregion
|
|
61
|
+
export { CardanoUtxoRef as a, CardanoSignatureResult as i, CardanoChain as n, CardanoWitness as o, CardanoSignRequestProps as r, parseWitnessSet as s, CardanoCertKeyRef as t };
|
|
62
|
+
//# sourceMappingURL=cardano-Do1ahUlK.d.cts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { _ as Ur, a as SignRequest, i as ExpectedReply, r as EraConnectConfig } from "./shared-CI8zhQiE.js";
|
|
2
|
+
//#region src/chains/cardano.d.ts
|
|
3
|
+
/** One transaction input the device must sign for. */
|
|
4
|
+
interface CardanoUtxoRef {
|
|
5
|
+
/** 32-byte hash of the transaction that created the UTXO. */
|
|
6
|
+
readonly transactionHash: Uint8Array;
|
|
7
|
+
readonly index: number;
|
|
8
|
+
/** Full signing path for this input, e.g. `m/1852'/1815'/0'/0/0`. */
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly xfp: string | number;
|
|
11
|
+
/** Lovelace amount as a decimal string — device display. */
|
|
12
|
+
readonly amount?: string;
|
|
13
|
+
/** Bech32 address of the UTXO — device display. */
|
|
14
|
+
readonly address?: string;
|
|
15
|
+
}
|
|
16
|
+
/** A certificate/withdrawal key the transaction additionally needs a witness from. */
|
|
17
|
+
interface CardanoCertKeyRef {
|
|
18
|
+
/** Full signing path, e.g. the stake key `m/1852'/1815'/0'/2/0`. */
|
|
19
|
+
readonly path: string;
|
|
20
|
+
readonly xfp: string | number;
|
|
21
|
+
/** 28-byte key hash — device display/matching. */
|
|
22
|
+
readonly keyHash?: Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
interface CardanoSignRequestProps {
|
|
25
|
+
readonly requestId?: Uint8Array | string;
|
|
26
|
+
/**
|
|
27
|
+
* The FULL transaction CBOR (`[body, witness_set, is_valid, aux_data]`) as
|
|
28
|
+
* your Cardano tooling serializes it. The device extracts the body (first
|
|
29
|
+
* array element) and signs its BLAKE2b-256 hash.
|
|
30
|
+
*/
|
|
31
|
+
readonly signData: Uint8Array;
|
|
32
|
+
/** At least one; the device signs once per UNIQUE path across utxos + certKeys. */
|
|
33
|
+
readonly utxos: readonly CardanoUtxoRef[];
|
|
34
|
+
readonly certKeys?: readonly CardanoCertKeyRef[];
|
|
35
|
+
readonly origin?: string;
|
|
36
|
+
}
|
|
37
|
+
interface CardanoWitness {
|
|
38
|
+
/** 32-byte verification key. */
|
|
39
|
+
readonly vkey: Uint8Array;
|
|
40
|
+
/** 64-byte Ed25519 signature over the tx-body BLAKE2b-256 hash. */
|
|
41
|
+
readonly signature: Uint8Array;
|
|
42
|
+
}
|
|
43
|
+
interface CardanoSignatureResult {
|
|
44
|
+
readonly requestId: Uint8Array;
|
|
45
|
+
/** The witness-set CBOR verbatim (`{0: #6.258([[vkey, sig]…])}`) — merge it into your tx. */
|
|
46
|
+
readonly witnessSet: Uint8Array;
|
|
47
|
+
/** The `[vkey, signature]` pairs parsed out of the witness set. */
|
|
48
|
+
readonly witnesses: readonly CardanoWitness[];
|
|
49
|
+
}
|
|
50
|
+
declare class CardanoChain {
|
|
51
|
+
private readonly context;
|
|
52
|
+
constructor(config?: EraConnectConfig);
|
|
53
|
+
/** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */
|
|
54
|
+
generateSignRequest(props: CardanoSignRequestProps): SignRequest<CardanoSignatureResult>;
|
|
55
|
+
/** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */
|
|
56
|
+
parseSignature(input: Ur | string, expect?: ExpectedReply): CardanoSignatureResult;
|
|
57
|
+
}
|
|
58
|
+
/** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */
|
|
59
|
+
declare function parseWitnessSet(witnessSet: Uint8Array): CardanoWitness[];
|
|
60
|
+
//#endregion
|
|
61
|
+
export { CardanoUtxoRef as a, CardanoSignatureResult as i, CardanoChain as n, CardanoWitness as o, CardanoSignRequestProps as r, parseWitnessSet as s, CardanoCertKeyRef as t };
|
|
62
|
+
//# sourceMappingURL=cardano-OzjWZHh6.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { G as cbMap, J as cbUint, K as cbTag, M as keypath304, N as normalizeXfp, P as parsePath, U as cbArray, W as cbBytes, X as stripTags, Y as mapGet, Z as EraSdkError, _ as Ur, b as cborDecode, c as resolveRequestId, h as normalizeRequestId, i as requireRequestIdEcho, l as toUr, n as makeSignRequest, o as requireUrType, q as cbText, r as requireReplyMap, s as resolveContext, x as cborEncode, z as asBytes } from "./shared-B8Wc7ViU.js";
|
|
2
|
+
//#region src/chains/cardano.ts
|
|
3
|
+
const REPLY_TYPES = ["cardano-signature"];
|
|
4
|
+
const UTXO_TAG = 2201;
|
|
5
|
+
const CERT_KEY_TAG = 2204;
|
|
6
|
+
var CardanoChain = class {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.context = resolveContext(config);
|
|
9
|
+
}
|
|
10
|
+
/** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */
|
|
11
|
+
generateSignRequest(props) {
|
|
12
|
+
const requestId = resolveRequestId(this.context, props.requestId);
|
|
13
|
+
if (props.signData.length === 0) throw new EraSdkError("invalid-props", "signData must not be empty");
|
|
14
|
+
if (props.utxos.length === 0) throw new EraSdkError("invalid-props", "at least one utxo is required");
|
|
15
|
+
const utxos = props.utxos.map((utxo) => {
|
|
16
|
+
if (utxo.transactionHash.length !== 32) throw new EraSdkError("invalid-props", "utxo transactionHash must be 32 bytes");
|
|
17
|
+
if (!Number.isSafeInteger(utxo.index) || utxo.index < 0) throw new EraSdkError("invalid-props", "utxo index must be a non-negative integer");
|
|
18
|
+
const entries = [[1, cbBytes(utxo.transactionHash)], [2, cbUint(utxo.index)]];
|
|
19
|
+
if (utxo.amount !== void 0) entries.push([3, cbText(utxo.amount)]);
|
|
20
|
+
entries.push([4, keypath304(parsePath(utxo.path), normalizeXfp(utxo.xfp))]);
|
|
21
|
+
if (utxo.address !== void 0) entries.push([5, cbText(utxo.address)]);
|
|
22
|
+
return cbTag(UTXO_TAG, cbMap(entries));
|
|
23
|
+
});
|
|
24
|
+
const certKeys = (props.certKeys ?? []).map((certKey) => {
|
|
25
|
+
const entries = [];
|
|
26
|
+
if (certKey.keyHash !== void 0) entries.push([1, cbBytes(certKey.keyHash)]);
|
|
27
|
+
entries.push([2, keypath304(parsePath(certKey.path), normalizeXfp(certKey.xfp))]);
|
|
28
|
+
return cbTag(CERT_KEY_TAG, cbMap(entries));
|
|
29
|
+
});
|
|
30
|
+
const entries = [
|
|
31
|
+
[1, cbTag(37, cbBytes(requestId))],
|
|
32
|
+
[2, cbBytes(props.signData)],
|
|
33
|
+
[3, cbArray(utxos)]
|
|
34
|
+
];
|
|
35
|
+
if (certKeys.length > 0) entries.push([4, cbArray(certKeys)]);
|
|
36
|
+
entries.push([5, cbText(props.origin ?? this.context.origin)]);
|
|
37
|
+
const ur = new Ur("cardano-sign-request", cborEncode(cbMap(entries)));
|
|
38
|
+
return makeSignRequest({
|
|
39
|
+
ur,
|
|
40
|
+
requestId,
|
|
41
|
+
replyTypes: REPLY_TYPES,
|
|
42
|
+
context: this.context,
|
|
43
|
+
parse: (reply) => parseCardanoSignature(reply, requestId)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */
|
|
47
|
+
parseSignature(input, expect) {
|
|
48
|
+
return parseCardanoSignature(toUr(input), expect?.requestId === void 0 ? void 0 : normalizeRequestId(expect.requestId));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function parseCardanoSignature(ur, expectedRequestId) {
|
|
52
|
+
requireUrType(ur, [...REPLY_TYPES], "cardano-signature");
|
|
53
|
+
const map = requireReplyMap(ur, "cardano-signature");
|
|
54
|
+
const requestId = requireRequestIdEcho(map, 1, expectedRequestId, "cardano-signature");
|
|
55
|
+
const witnessSet = asBytes(mapGet(map, 2));
|
|
56
|
+
if (!witnessSet || witnessSet.length === 0) throw new EraSdkError("malformed-reply", "cardano-signature is missing the witness set (key 2)");
|
|
57
|
+
return {
|
|
58
|
+
requestId,
|
|
59
|
+
witnessSet,
|
|
60
|
+
witnesses: parseWitnessSet(witnessSet)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */
|
|
64
|
+
function parseWitnessSet(witnessSet) {
|
|
65
|
+
let decoded;
|
|
66
|
+
try {
|
|
67
|
+
decoded = cborDecode(witnessSet);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
throw new EraSdkError("malformed-reply", `witness set is not readable CBOR: ${e.message}`);
|
|
70
|
+
}
|
|
71
|
+
const root = stripTags(decoded);
|
|
72
|
+
if (root.kind !== "map") throw new EraSdkError("malformed-reply", "witness set is not a CBOR map");
|
|
73
|
+
const vkeyWitnesses = mapGet(root, 0);
|
|
74
|
+
const list = vkeyWitnesses === void 0 ? void 0 : stripTags(vkeyWitnesses);
|
|
75
|
+
if (list?.kind !== "array") throw new EraSdkError("malformed-reply", "witness set carries no vkey witnesses (key 0)");
|
|
76
|
+
const witnesses = [];
|
|
77
|
+
for (const item of list.items) {
|
|
78
|
+
const pair = stripTags(item);
|
|
79
|
+
if (pair.kind !== "array" || pair.items.length < 2) throw new EraSdkError("malformed-reply", "malformed vkey witness");
|
|
80
|
+
const vkey = asBytes(pair.items[0]);
|
|
81
|
+
const signature = asBytes(pair.items[1]);
|
|
82
|
+
if (!vkey || vkey.length !== 32 || !signature || signature.length !== 64) throw new EraSdkError("malformed-reply", "vkey witness is not [32-byte key, 64-byte signature]");
|
|
83
|
+
witnesses.push({
|
|
84
|
+
vkey,
|
|
85
|
+
signature
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (witnesses.length === 0) throw new EraSdkError("malformed-reply", "witness set is empty");
|
|
89
|
+
return witnesses;
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { parseWitnessSet as n, CardanoChain as t };
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=cardano-yuOygqwp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cardano-yuOygqwp.js","names":["UrValue"],"sources":["../src/chains/cardano.ts"],"sourcesContent":["import { cborDecode } from '../cbor/decode';\nimport { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport {\n asBytes,\n cbArray,\n cbBytes,\n cbMap,\n cbTag,\n cbText,\n cbUint,\n mapGet,\n stripTags,\n} from '../cbor/model';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId } from '../core/rand';\nimport { keypath304, normalizeXfp, parsePath } from '../registry/keypath';\nimport type { Ur } from '../ur/ur';\nimport { Ur as UrValue } from '../ur/ur';\nimport type { ChainContext, EraConnectConfig, ExpectedReply, SignRequest } from './shared';\nimport {\n makeSignRequest,\n requireReplyMap,\n requireRequestIdEcho,\n requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\n/** One transaction input the device must sign for. */\nexport interface CardanoUtxoRef {\n /** 32-byte hash of the transaction that created the UTXO. */\n readonly transactionHash: Uint8Array;\n readonly index: number;\n /** Full signing path for this input, e.g. `m/1852'/1815'/0'/0/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /** Lovelace amount as a decimal string — device display. */\n readonly amount?: string;\n /** Bech32 address of the UTXO — device display. */\n readonly address?: string;\n}\n\n/** A certificate/withdrawal key the transaction additionally needs a witness from. */\nexport interface CardanoCertKeyRef {\n /** Full signing path, e.g. the stake key `m/1852'/1815'/0'/2/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /** 28-byte key hash — device display/matching. */\n readonly keyHash?: Uint8Array;\n}\n\nexport interface CardanoSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /**\n * The FULL transaction CBOR (`[body, witness_set, is_valid, aux_data]`) as\n * your Cardano tooling serializes it. The device extracts the body (first\n * array element) and signs its BLAKE2b-256 hash.\n */\n readonly signData: Uint8Array;\n /** At least one; the device signs once per UNIQUE path across utxos + certKeys. */\n readonly utxos: readonly CardanoUtxoRef[];\n readonly certKeys?: readonly CardanoCertKeyRef[];\n readonly origin?: string;\n}\n\nexport interface CardanoWitness {\n /** 32-byte verification key. */\n readonly vkey: Uint8Array;\n /** 64-byte Ed25519 signature over the tx-body BLAKE2b-256 hash. */\n readonly signature: Uint8Array;\n}\n\nexport interface CardanoSignatureResult {\n readonly requestId: Uint8Array;\n /** The witness-set CBOR verbatim (`{0: #6.258([[vkey, sig]…])}`) — merge it into your tx. */\n readonly witnessSet: Uint8Array;\n /** The `[vkey, signature]` pairs parsed out of the witness set. */\n readonly witnesses: readonly CardanoWitness[];\n}\n\nconst REPLY_TYPES = ['cardano-signature'] as const;\nconst UTXO_TAG = 2201;\nconst CERT_KEY_TAG = 2204;\n\nexport class CardanoChain {\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */\n generateSignRequest(props: CardanoSignRequestProps): SignRequest<CardanoSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n if (props.signData.length === 0) {\n throw new EraSdkError('invalid-props', 'signData must not be empty');\n }\n if (props.utxos.length === 0) {\n throw new EraSdkError('invalid-props', 'at least one utxo is required');\n }\n\n const utxos = props.utxos.map((utxo) => {\n if (utxo.transactionHash.length !== 32) {\n throw new EraSdkError('invalid-props', 'utxo transactionHash must be 32 bytes');\n }\n if (!Number.isSafeInteger(utxo.index) || utxo.index < 0) {\n throw new EraSdkError('invalid-props', 'utxo index must be a non-negative integer');\n }\n const entries: [number, CborValue][] = [\n [1, cbBytes(utxo.transactionHash)],\n [2, cbUint(utxo.index)],\n ];\n if (utxo.amount !== undefined) entries.push([3, cbText(utxo.amount)]);\n entries.push([4, keypath304(parsePath(utxo.path), normalizeXfp(utxo.xfp))]);\n if (utxo.address !== undefined) entries.push([5, cbText(utxo.address)]);\n return cbTag(UTXO_TAG, cbMap(entries));\n });\n\n const certKeys = (props.certKeys ?? []).map((certKey) => {\n const entries: [number, CborValue][] = [];\n if (certKey.keyHash !== undefined) entries.push([1, cbBytes(certKey.keyHash)]);\n entries.push([2, keypath304(parsePath(certKey.path), normalizeXfp(certKey.xfp))]);\n return cbTag(CERT_KEY_TAG, cbMap(entries));\n });\n\n const entries: [number, CborValue][] = [\n [1, cbTag(37, cbBytes(requestId))],\n [2, cbBytes(props.signData)],\n [3, cbArray(utxos)],\n ];\n if (certKeys.length > 0) entries.push([4, cbArray(certKeys)]);\n entries.push([5, cbText(props.origin ?? this.context.origin)]);\n\n const ur = new UrValue('cardano-sign-request', cborEncode(cbMap(entries)));\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseCardanoSignature(reply, requestId),\n });\n }\n\n /** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseSignature(input: Ur | string, expect?: ExpectedReply): CardanoSignatureResult {\n return parseCardanoSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseCardanoSignature(\n ur: Ur,\n expectedRequestId: Uint8Array | undefined,\n): CardanoSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'cardano-signature');\n const map = requireReplyMap(ur, 'cardano-signature');\n const requestId = requireRequestIdEcho(map, 1, expectedRequestId, 'cardano-signature');\n\n const witnessSet = asBytes(mapGet(map, 2));\n if (!witnessSet || witnessSet.length === 0) {\n throw new EraSdkError(\n 'malformed-reply',\n 'cardano-signature is missing the witness set (key 2)',\n );\n }\n return { requestId, witnessSet, witnesses: parseWitnessSet(witnessSet) };\n}\n\n/** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */\nexport function parseWitnessSet(witnessSet: Uint8Array): CardanoWitness[] {\n let decoded: CborValue;\n try {\n decoded = cborDecode(witnessSet);\n } catch (e) {\n throw new EraSdkError(\n 'malformed-reply',\n `witness set is not readable CBOR: ${(e as Error).message}`,\n );\n }\n const root = stripTags(decoded);\n if (root.kind !== 'map') {\n throw new EraSdkError('malformed-reply', 'witness set is not a CBOR map');\n }\n const vkeyWitnesses = mapGet(root, 0);\n const list = vkeyWitnesses === undefined ? undefined : stripTags(vkeyWitnesses);\n if (list?.kind !== 'array') {\n throw new EraSdkError('malformed-reply', 'witness set carries no vkey witnesses (key 0)');\n }\n const witnesses: CardanoWitness[] = [];\n for (const item of list.items) {\n const pair = stripTags(item);\n if (pair.kind !== 'array' || pair.items.length < 2) {\n throw new EraSdkError('malformed-reply', 'malformed vkey witness');\n }\n const vkey = asBytes(pair.items[0]);\n const signature = asBytes(pair.items[1]);\n if (!vkey || vkey.length !== 32 || !signature || signature.length !== 64) {\n throw new EraSdkError(\n 'malformed-reply',\n 'vkey witness is not [32-byte key, 64-byte signature]',\n );\n }\n witnesses.push({ vkey, signature });\n }\n if (witnesses.length === 0) {\n throw new EraSdkError('malformed-reply', 'witness set is empty');\n }\n return witnesses;\n}\n"],"mappings":"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"}
|
package/dist/cardano.cjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_shared = require("./shared-nISrEktU.cjs");
|
|
3
|
+
const require_cardano = require("./cardano-DP6U_K1J.cjs");
|
|
4
|
+
exports.AnimatedUr = require_shared.AnimatedUr;
|
|
5
|
+
exports.CardanoChain = require_cardano.CardanoChain;
|
|
6
|
+
exports.EraSdkError = require_shared.EraSdkError;
|
|
7
|
+
exports.TypedUrScanner = require_shared.TypedUrScanner;
|
|
8
|
+
exports.Ur = require_shared.Ur;
|
|
9
|
+
exports.UrScanner = require_shared.UrScanner;
|
|
10
|
+
exports.parseWitnessSet = require_cardano.parseWitnessSet;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { _ as Ur, a as SignRequest, c as TypedUrScanner, d as EraErrorCode, f as EraSdkError, i as ExpectedReply, l as UrScanner, p as AnimatedUr, r as EraConnectConfig } from "./shared-CI8zhQiE.cjs";
|
|
2
|
+
import { a as CardanoUtxoRef, i as CardanoSignatureResult, n as CardanoChain, o as CardanoWitness, r as CardanoSignRequestProps, s as parseWitnessSet, t as CardanoCertKeyRef } from "./cardano-Do1ahUlK.cjs";
|
|
3
|
+
export { AnimatedUr, type CardanoCertKeyRef, CardanoChain, type CardanoSignRequestProps, type CardanoSignatureResult, type CardanoUtxoRef, type CardanoWitness, type EraConnectConfig, type EraErrorCode, EraSdkError, type ExpectedReply, type SignRequest, TypedUrScanner, Ur, UrScanner, parseWitnessSet };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { _ as Ur, a as SignRequest, c as TypedUrScanner, d as EraErrorCode, f as EraSdkError, i as ExpectedReply, l as UrScanner, p as AnimatedUr, r as EraConnectConfig } from "./shared-CI8zhQiE.js";
|
|
2
|
+
import { a as CardanoUtxoRef, i as CardanoSignatureResult, n as CardanoChain, o as CardanoWitness, r as CardanoSignRequestProps, s as parseWitnessSet, t as CardanoCertKeyRef } from "./cardano-OzjWZHh6.js";
|
|
3
|
+
export { AnimatedUr, type CardanoCertKeyRef, CardanoChain, type CardanoSignRequestProps, type CardanoSignatureResult, type CardanoUtxoRef, type CardanoWitness, type EraConnectConfig, type EraErrorCode, EraSdkError, type ExpectedReply, type SignRequest, TypedUrScanner, Ur, UrScanner, parseWitnessSet };
|
package/dist/cardano.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { Z as EraSdkError, _ as Ur, d as UrScanner, f as AnimatedUr, u as TypedUrScanner } from "./shared-B8Wc7ViU.js";
|
|
2
|
+
import { n as parseWitnessSet, t as CardanoChain } from "./cardano-yuOygqwp.js";
|
|
3
|
+
export { AnimatedUr, CardanoChain, EraSdkError, TypedUrScanner, Ur, UrScanner, parseWitnessSet };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { O as u32be, T as concatBytes, Z as EraSdkError, w as bytesToHex } from "./shared-B8Wc7ViU.js";
|
|
2
|
+
import { secp256k1 } from "@noble/curves/secp256k1";
|
|
3
|
+
import { ripemd160 } from "@noble/hashes/ripemd160";
|
|
4
|
+
import { sha256, sha512 } from "@noble/hashes/sha2";
|
|
5
|
+
import { keccak_256 } from "@noble/hashes/sha3";
|
|
6
|
+
import { base58, bech32, createBase58check } from "@scure/base";
|
|
7
|
+
import { HDKey } from "@scure/bip32";
|
|
8
|
+
import { ed25519 } from "@noble/curves/ed25519";
|
|
9
|
+
import { hmac } from "@noble/hashes/hmac";
|
|
10
|
+
//#region src/accounts/derive.ts
|
|
11
|
+
const base58check = createBase58check(sha256);
|
|
12
|
+
/** Non-hardened BIP-32 child public key from an account-level (publicKey, chainCode). */
|
|
13
|
+
function derivePublicKey(publicKey, chainCode, change, index) {
|
|
14
|
+
const child = new HDKey({
|
|
15
|
+
publicKey,
|
|
16
|
+
chainCode
|
|
17
|
+
}).deriveChild(change).deriveChild(index);
|
|
18
|
+
if (!child.publicKey) throw new EraSdkError("invalid-props", "child derivation produced no public key");
|
|
19
|
+
return child.publicKey;
|
|
20
|
+
}
|
|
21
|
+
function uncompressed(publicKey33) {
|
|
22
|
+
return secp256k1.ProjectivePoint.fromHex(publicKey33).toRawBytes(false);
|
|
23
|
+
}
|
|
24
|
+
/** EIP-55 checksummed address from a compressed secp256k1 public key. */
|
|
25
|
+
function evmAddressFromPublicKey(publicKey33) {
|
|
26
|
+
const hash = keccak_256(uncompressed(publicKey33).slice(1));
|
|
27
|
+
const addr = bytesToHex(hash.slice(12));
|
|
28
|
+
const check = keccak_256(new Uint8Array([...addr].map((c) => c.charCodeAt(0))));
|
|
29
|
+
let out = "";
|
|
30
|
+
for (let i = 0; i < addr.length; i++) {
|
|
31
|
+
const nibble = i % 2 === 0 ? check[i >> 1] >> 4 : check[i >> 1] & 15;
|
|
32
|
+
out += nibble >= 8 ? addr[i].toUpperCase() : addr[i];
|
|
33
|
+
}
|
|
34
|
+
return `0x${out}`;
|
|
35
|
+
}
|
|
36
|
+
function hash160(data) {
|
|
37
|
+
return ripemd160(sha256(data));
|
|
38
|
+
}
|
|
39
|
+
/** P2WPKH (witness v0) bech32 address. */
|
|
40
|
+
function btcP2wpkhAddressFromPublicKey(publicKey33, hrp = "bc") {
|
|
41
|
+
return bech32.encode(hrp, [0, ...bech32.toWords(hash160(publicKey33))]);
|
|
42
|
+
}
|
|
43
|
+
/** Legacy P2PKH base58check address (`1...`) — the kind the device signs messages for. */
|
|
44
|
+
function btcP2pkhAddressFromPublicKey(publicKey33, testnet = false) {
|
|
45
|
+
return base58check.encode(concatBytes(new Uint8Array([testnet ? 111 : 0]), hash160(publicKey33)));
|
|
46
|
+
}
|
|
47
|
+
/** Nested segwit (P2SH-P2WPKH) base58check address (`3...`). */
|
|
48
|
+
function btcNestedSegwitAddressFromPublicKey(publicKey33, testnet = false) {
|
|
49
|
+
const redeemScript = concatBytes(new Uint8Array([0, 20]), hash160(publicKey33));
|
|
50
|
+
return base58check.encode(concatBytes(new Uint8Array([testnet ? 196 : 5]), hash160(redeemScript)));
|
|
51
|
+
}
|
|
52
|
+
/** Tron base58check address (0x41-prefixed keccak hash). */
|
|
53
|
+
function tronAddressFromPublicKey(publicKey33) {
|
|
54
|
+
const hash = keccak_256(uncompressed(publicKey33).slice(1));
|
|
55
|
+
return base58check.encode(concatBytes(new Uint8Array([65]), hash.slice(12)));
|
|
56
|
+
}
|
|
57
|
+
/** A Solana address IS the Ed25519 public key, base58. */
|
|
58
|
+
function solanaAddressFromPublicKey(publicKey32) {
|
|
59
|
+
return base58.encode(publicKey32);
|
|
60
|
+
}
|
|
61
|
+
const XPUB_VERSION = 76067358;
|
|
62
|
+
const ZPUB_VERSION = 78792518;
|
|
63
|
+
/** BIP-32 extended public key serialization. */
|
|
64
|
+
function serializeExtendedPublicKey(args) {
|
|
65
|
+
const { version = XPUB_VERSION, depth, parentFingerprint, childNumber, chainCode, publicKey } = args;
|
|
66
|
+
if (chainCode.length !== 32 || publicKey.length !== 33) throw new EraSdkError("invalid-props", "extended key needs a 32-byte chain code and 33-byte key");
|
|
67
|
+
return base58check.encode(concatBytes(u32be(version), new Uint8Array([depth & 255]), u32be(parentFingerprint), u32be(childNumber), chainCode, publicKey));
|
|
68
|
+
}
|
|
69
|
+
function u32le(value) {
|
|
70
|
+
return new Uint8Array([
|
|
71
|
+
value & 255,
|
|
72
|
+
value >> 8 & 255,
|
|
73
|
+
value >> 16 & 255,
|
|
74
|
+
value >> 24 & 255
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
function leBytesToBigint(bytes) {
|
|
78
|
+
let out = 0n;
|
|
79
|
+
for (let i = bytes.length - 1; i >= 0; i--) out = out << 8n | BigInt(bytes[i]);
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Public (soft) child of a BIP32-Ed25519 extended public key — the scheme
|
|
84
|
+
* Cardano wallets share account xpubs under (CIP-3/V2):
|
|
85
|
+
*
|
|
86
|
+
* Z = HMAC-SHA512(chainCode, 0x02 || A || le32(index))
|
|
87
|
+
* childA = A + [8 * ZL[0..28]] * B
|
|
88
|
+
* childCC = HMAC-SHA512(chainCode, 0x03 || A || le32(index))[32..]
|
|
89
|
+
*
|
|
90
|
+
* Only non-hardened indices are derivable publicly, which is exactly what the
|
|
91
|
+
* role/index tail of a CIP-1852 path uses.
|
|
92
|
+
*/
|
|
93
|
+
function cardanoSoftDeriveChild(publicKey, chainCode, index) {
|
|
94
|
+
if (publicKey.length !== 32 || chainCode.length !== 32) throw new EraSdkError("invalid-props", "Cardano derivation needs a 32-byte key and chain code");
|
|
95
|
+
if (!Number.isSafeInteger(index) || index < 0 || index >= 2147483648) throw new EraSdkError("invalid-props", "Cardano public derivation is soft-index only");
|
|
96
|
+
const z = hmac(sha512, chainCode, concatBytes(new Uint8Array([2]), publicKey, u32le(index)));
|
|
97
|
+
const cc = hmac(sha512, chainCode, concatBytes(new Uint8Array([3]), publicKey, u32le(index))).slice(32);
|
|
98
|
+
const scalar = 8n * leBytesToBigint(z.slice(0, 28));
|
|
99
|
+
const parent = ed25519.ExtendedPoint.fromHex(bytesToHex(publicKey));
|
|
100
|
+
return {
|
|
101
|
+
publicKey: (scalar === 0n ? parent : parent.add(ed25519.ExtendedPoint.BASE.multiply(scalar))).toRawBytes(),
|
|
102
|
+
chainCode: cc
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** Soft-derive along several indices (e.g. role, then address index). */
|
|
106
|
+
function cardanoSoftDerivePath(publicKey, chainCode, indices) {
|
|
107
|
+
let node = {
|
|
108
|
+
publicKey,
|
|
109
|
+
chainCode
|
|
110
|
+
};
|
|
111
|
+
for (const index of indices) node = cardanoSoftDeriveChild(node.publicKey, node.chainCode, index);
|
|
112
|
+
return node.publicKey;
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { cardanoSoftDerivePath as a, serializeExtendedPublicKey as c, btcP2wpkhAddressFromPublicKey as i, solanaAddressFromPublicKey as l, btcNestedSegwitAddressFromPublicKey as n, derivePublicKey as o, btcP2pkhAddressFromPublicKey as r, evmAddressFromPublicKey as s, ZPUB_VERSION as t, tronAddressFromPublicKey as u };
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=derive-C4JdycUh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-C4JdycUh.js","names":[],"sources":["../src/accounts/derive.ts"],"sourcesContent":["import { secp256k1 } from '@noble/curves/secp256k1';\nimport { ripemd160 } from '@noble/hashes/ripemd160';\nimport { sha256 } from '@noble/hashes/sha2';\nimport { keccak_256 } from '@noble/hashes/sha3';\nimport { base58, bech32, createBase58check } from '@scure/base';\nimport { HDKey } from '@scure/bip32';\nimport { bytesToHex, concatBytes, u32be } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\n\nconst base58check = createBase58check(sha256);\n\n/** Non-hardened BIP-32 child public key from an account-level (publicKey, chainCode). */\nexport function derivePublicKey(\n publicKey: Uint8Array,\n chainCode: Uint8Array,\n change: number,\n index: number,\n): Uint8Array {\n const node = new HDKey({ publicKey, chainCode });\n const child = node.deriveChild(change).deriveChild(index);\n if (!child.publicKey) {\n throw new EraSdkError('invalid-props', 'child derivation produced no public key');\n }\n return child.publicKey;\n}\n\nfunction uncompressed(publicKey33: Uint8Array): Uint8Array {\n return secp256k1.ProjectivePoint.fromHex(publicKey33).toRawBytes(false);\n}\n\n/** EIP-55 checksummed address from a compressed secp256k1 public key. */\nexport function evmAddressFromPublicKey(publicKey33: Uint8Array): `0x${string}` {\n const hash = keccak_256(uncompressed(publicKey33).slice(1));\n const addr = bytesToHex(hash.slice(12));\n const check = keccak_256(new Uint8Array([...addr].map((c) => c.charCodeAt(0))));\n let out = '';\n for (let i = 0; i < addr.length; i++) {\n const nibble = i % 2 === 0 ? check[i >> 1]! >> 4 : check[i >> 1]! & 0x0f;\n out += nibble >= 8 ? addr[i]!.toUpperCase() : addr[i]!;\n }\n return `0x${out}`;\n}\n\nfunction hash160(data: Uint8Array): Uint8Array {\n return ripemd160(sha256(data));\n}\n\n/** P2WPKH (witness v0) bech32 address. */\nexport function btcP2wpkhAddressFromPublicKey(\n publicKey33: Uint8Array,\n hrp: 'bc' | 'tb' = 'bc',\n): string {\n return bech32.encode(hrp, [0, ...bech32.toWords(hash160(publicKey33))]);\n}\n\n/** Legacy P2PKH base58check address (`1...`) — the kind the device signs messages for. */\nexport function btcP2pkhAddressFromPublicKey(publicKey33: Uint8Array, testnet = false): string {\n return base58check.encode(\n concatBytes(new Uint8Array([testnet ? 0x6f : 0x00]), hash160(publicKey33)),\n );\n}\n\n/** Nested segwit (P2SH-P2WPKH) base58check address (`3...`). */\nexport function btcNestedSegwitAddressFromPublicKey(\n publicKey33: Uint8Array,\n testnet = false,\n): string {\n const redeemScript = concatBytes(new Uint8Array([0x00, 0x14]), hash160(publicKey33));\n return base58check.encode(\n concatBytes(new Uint8Array([testnet ? 0xc4 : 0x05]), hash160(redeemScript)),\n );\n}\n\n/** Tron base58check address (0x41-prefixed keccak hash). */\nexport function tronAddressFromPublicKey(publicKey33: Uint8Array): string {\n const hash = keccak_256(uncompressed(publicKey33).slice(1));\n return base58check.encode(concatBytes(new Uint8Array([0x41]), hash.slice(12)));\n}\n\n/** A Solana address IS the Ed25519 public key, base58. */\nexport function solanaAddressFromPublicKey(publicKey32: Uint8Array): string {\n return base58.encode(publicKey32);\n}\n\nconst XPUB_VERSION = 0x0488b21e;\nconst ZPUB_VERSION = 0x04b24746; // SLIP-132, BIP-84 P2WPKH\n\n/** BIP-32 extended public key serialization. */\nexport function serializeExtendedPublicKey(args: {\n version?: number;\n depth: number;\n parentFingerprint: number;\n childNumber: number;\n chainCode: Uint8Array;\n publicKey: Uint8Array;\n}): string {\n const {\n version = XPUB_VERSION,\n depth,\n parentFingerprint,\n childNumber,\n chainCode,\n publicKey,\n } = args;\n if (chainCode.length !== 32 || publicKey.length !== 33) {\n throw new EraSdkError(\n 'invalid-props',\n 'extended key needs a 32-byte chain code and 33-byte key',\n );\n }\n return base58check.encode(\n concatBytes(\n u32be(version),\n new Uint8Array([depth & 0xff]),\n u32be(parentFingerprint),\n u32be(childNumber),\n chainCode,\n publicKey,\n ),\n );\n}\n\nexport { XPUB_VERSION, ZPUB_VERSION };\n\n// ---------------------------------------------------------------------------\n// Cardano (BIP32-Ed25519 / CIP-3 \"V2\") soft public derivation\n// ---------------------------------------------------------------------------\n\nimport { ed25519 } from '@noble/curves/ed25519';\nimport { hmac } from '@noble/hashes/hmac';\nimport { sha512 } from '@noble/hashes/sha2';\n\nfunction u32le(value: number): Uint8Array {\n return new Uint8Array([\n value & 0xff,\n (value >> 8) & 0xff,\n (value >> 16) & 0xff,\n (value >> 24) & 0xff,\n ]);\n}\n\nfunction leBytesToBigint(bytes: Uint8Array): bigint {\n let out = 0n;\n for (let i = bytes.length - 1; i >= 0; i--) out = (out << 8n) | BigInt(bytes[i]!);\n return out;\n}\n\n/**\n * Public (soft) child of a BIP32-Ed25519 extended public key — the scheme\n * Cardano wallets share account xpubs under (CIP-3/V2):\n *\n * Z = HMAC-SHA512(chainCode, 0x02 || A || le32(index))\n * childA = A + [8 * ZL[0..28]] * B\n * childCC = HMAC-SHA512(chainCode, 0x03 || A || le32(index))[32..]\n *\n * Only non-hardened indices are derivable publicly, which is exactly what the\n * role/index tail of a CIP-1852 path uses.\n */\nexport function cardanoSoftDeriveChild(\n publicKey: Uint8Array,\n chainCode: Uint8Array,\n index: number,\n): { publicKey: Uint8Array; chainCode: Uint8Array } {\n if (publicKey.length !== 32 || chainCode.length !== 32) {\n throw new EraSdkError('invalid-props', 'Cardano derivation needs a 32-byte key and chain code');\n }\n if (!Number.isSafeInteger(index) || index < 0 || index >= 0x80000000) {\n throw new EraSdkError('invalid-props', 'Cardano public derivation is soft-index only');\n }\n const z = hmac(sha512, chainCode, concatBytes(new Uint8Array([0x02]), publicKey, u32le(index)));\n const cc = hmac(\n sha512,\n chainCode,\n concatBytes(new Uint8Array([0x03]), publicKey, u32le(index)),\n ).slice(32);\n const scalar = 8n * leBytesToBigint(z.slice(0, 28));\n const parent = ed25519.ExtendedPoint.fromHex(bytesToHex(publicKey));\n const child = scalar === 0n ? parent : parent.add(ed25519.ExtendedPoint.BASE.multiply(scalar));\n return { publicKey: child.toRawBytes(), chainCode: cc };\n}\n\n/** Soft-derive along several indices (e.g. role, then address index). */\nexport function cardanoSoftDerivePath(\n publicKey: Uint8Array,\n chainCode: Uint8Array,\n indices: readonly number[],\n): Uint8Array {\n let node = { publicKey, chainCode };\n for (const index of indices) {\n node = cardanoSoftDeriveChild(node.publicKey, node.chainCode, index);\n }\n return node.publicKey;\n}\n"],"mappings":"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"}
|