@mysten/ledger-signer 0.0.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/dist/index.d.mts +73 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +110 -0
- package/dist/index.mjs.map +1 -0
- package/dist/objects.d.mts +10 -0
- package/dist/objects.d.mts.map +1 -0
- package/dist/objects.mjs +16 -0
- package/dist/objects.mjs.map +1 -0
- package/package.json +60 -0
- package/src/index.ts +163 -0
- package/src/objects.ts +32 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { getInputObjects } from "./objects.mjs";
|
|
2
|
+
import { SignatureWithBytes, Signer } from "@mysten/sui/cryptography";
|
|
3
|
+
import { Ed25519PublicKey } from "@mysten/sui/keypairs/ed25519";
|
|
4
|
+
import SuiLedgerClient, { Resolution } from "@mysten/ledgerjs-hw-app-sui";
|
|
5
|
+
import { ClientWithCoreApi } from "@mysten/sui/client";
|
|
6
|
+
|
|
7
|
+
//#region src/index.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Configuration options for initializing the LedgerSigner.
|
|
10
|
+
*/
|
|
11
|
+
interface LedgerSignerOptions {
|
|
12
|
+
publicKey: Ed25519PublicKey;
|
|
13
|
+
derivationPath: string;
|
|
14
|
+
ledgerClient: SuiLedgerClient;
|
|
15
|
+
suiClient: ClientWithCoreApi;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Ledger integrates with the Sui blockchain to provide signing capabilities using Ledger devices.
|
|
19
|
+
*/
|
|
20
|
+
declare class LedgerSigner extends Signer {
|
|
21
|
+
#private;
|
|
22
|
+
/**
|
|
23
|
+
* Creates an instance of LedgerSigner. It's expected to call the static `fromDerivationPath` method to create an instance.
|
|
24
|
+
* @example
|
|
25
|
+
* ```
|
|
26
|
+
* const signer = await LedgerSigner.fromDerivationPath(derivationPath, options);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
constructor({
|
|
30
|
+
publicKey,
|
|
31
|
+
derivationPath,
|
|
32
|
+
ledgerClient,
|
|
33
|
+
suiClient
|
|
34
|
+
}: LedgerSignerOptions);
|
|
35
|
+
/**
|
|
36
|
+
* Retrieves the key scheme used by this signer.
|
|
37
|
+
*/
|
|
38
|
+
getKeyScheme(): "ED25519";
|
|
39
|
+
/**
|
|
40
|
+
* Retrieves the public key associated with this signer.
|
|
41
|
+
* @returns The Ed25519PublicKey instance.
|
|
42
|
+
*/
|
|
43
|
+
getPublicKey(): Ed25519PublicKey;
|
|
44
|
+
/**
|
|
45
|
+
* Signs the provided transaction bytes.
|
|
46
|
+
* @returns The signed transaction bytes and signature.
|
|
47
|
+
*/
|
|
48
|
+
signTransaction(bytes: Uint8Array, bcsObjects?: Uint8Array[], resolution?: Resolution): Promise<SignatureWithBytes>;
|
|
49
|
+
/**
|
|
50
|
+
* Signs the provided personal message.
|
|
51
|
+
* @returns The signed message bytes and signature.
|
|
52
|
+
*/
|
|
53
|
+
signPersonalMessage(bytes: Uint8Array): Promise<SignatureWithBytes>;
|
|
54
|
+
/**
|
|
55
|
+
* Prepares the signer by fetching and setting the public key from a Ledger device.
|
|
56
|
+
* It is recommended to initialize an `LedgerSigner` instance using this function.
|
|
57
|
+
* @returns A promise that resolves once a `LedgerSigner` instance is prepared (public key is set).
|
|
58
|
+
*/
|
|
59
|
+
static fromDerivationPath(derivationPath: string, ledgerClient: SuiLedgerClient, suiClient: ClientWithCoreApi): Promise<LedgerSigner>;
|
|
60
|
+
/**
|
|
61
|
+
* Generic signing is not supported by Ledger.
|
|
62
|
+
* @throws Always throws an error indicating generic signing is unsupported.
|
|
63
|
+
*/
|
|
64
|
+
sign(): never;
|
|
65
|
+
/**
|
|
66
|
+
* Generic signing is not supported by Ledger.
|
|
67
|
+
* @throws Always throws an error indicating generic signing is unsupported.
|
|
68
|
+
*/
|
|
69
|
+
signWithIntent(): never;
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { LedgerSigner, LedgerSignerOptions, getInputObjects };
|
|
73
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;AAoBA;;;AAAA,UAAiB,mBAAA;EAChB,SAAA,EAAW,gBAAA;EACX,cAAA;EACA,YAAA,EAAc,eAAA;EACd,SAAA,EAAW,iBAAA;AAAA;;;;cAMC,YAAA,SAAqB,MAAA;EAAA;EANtB;;;AAMZ;;;;;IAae,SAAA;IAAW,cAAA;IAAgB,YAAA;IAAc;EAAA,GAAa,mBAAA;EAA3B;;;EAWhC,YAAA,CAAA;EAiBD;;;;EATC,YAAA,CAAA,GAAY,gBAAA;EA0CqB;;;;EAlC3B,eAAA,CACd,KAAA,EAAO,UAAA,EACP,UAAA,GAAa,UAAA,IACb,UAAA,GAAa,UAAA,GACX,OAAA,CAAQ,kBAAA;EA0DkB;;;;EA5Bd,mBAAA,CAAoB,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,kBAAA;EA1E9B;;;;;EAAA,OAmGpB,kBAAA,CACZ,cAAA,UACA,YAAA,EAAc,eAAA,EACd,SAAA,EAAW,iBAAA,GAAiB,OAAA,CAAA,YAAA;EAzFJ;;;;EA4GhB,IAAA,CAAA;EA5G2D;;;;EAoH3D,cAAA,CAAA;AAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { getInputObjects } from "./objects.mjs";
|
|
2
|
+
import { Signer, messageWithIntent, toSerializedSignature } from "@mysten/sui/cryptography";
|
|
3
|
+
import { Ed25519PublicKey } from "@mysten/sui/keypairs/ed25519";
|
|
4
|
+
import { Transaction } from "@mysten/sui/transactions";
|
|
5
|
+
import { toBase64 } from "@mysten/sui/utils";
|
|
6
|
+
import { bcs } from "@mysten/sui/bcs";
|
|
7
|
+
|
|
8
|
+
//#region src/index.ts
|
|
9
|
+
/**
|
|
10
|
+
* Ledger integrates with the Sui blockchain to provide signing capabilities using Ledger devices.
|
|
11
|
+
*/
|
|
12
|
+
var LedgerSigner = class LedgerSigner extends Signer {
|
|
13
|
+
#derivationPath;
|
|
14
|
+
#publicKey;
|
|
15
|
+
#ledgerClient;
|
|
16
|
+
#suiClient;
|
|
17
|
+
/**
|
|
18
|
+
* Creates an instance of LedgerSigner. It's expected to call the static `fromDerivationPath` method to create an instance.
|
|
19
|
+
* @example
|
|
20
|
+
* ```
|
|
21
|
+
* const signer = await LedgerSigner.fromDerivationPath(derivationPath, options);
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
constructor({ publicKey, derivationPath, ledgerClient, suiClient }) {
|
|
25
|
+
super();
|
|
26
|
+
this.#publicKey = publicKey;
|
|
27
|
+
this.#derivationPath = derivationPath;
|
|
28
|
+
this.#ledgerClient = ledgerClient;
|
|
29
|
+
this.#suiClient = suiClient;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Retrieves the key scheme used by this signer.
|
|
33
|
+
*/
|
|
34
|
+
getKeyScheme() {
|
|
35
|
+
return "ED25519";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Retrieves the public key associated with this signer.
|
|
39
|
+
* @returns The Ed25519PublicKey instance.
|
|
40
|
+
*/
|
|
41
|
+
getPublicKey() {
|
|
42
|
+
return this.#publicKey;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Signs the provided transaction bytes.
|
|
46
|
+
* @returns The signed transaction bytes and signature.
|
|
47
|
+
*/
|
|
48
|
+
async signTransaction(bytes, bcsObjects, resolution) {
|
|
49
|
+
const transactionOptions = bcsObjects ? { bcsObjects } : await getInputObjects(Transaction.from(bytes), this.#suiClient).catch(() => ({ bcsObjects: [] }));
|
|
50
|
+
const intentMessage = messageWithIntent("TransactionData", bytes);
|
|
51
|
+
const { signature } = await this.#ledgerClient.signTransaction(this.#derivationPath, intentMessage, transactionOptions, resolution);
|
|
52
|
+
return {
|
|
53
|
+
bytes: toBase64(bytes),
|
|
54
|
+
signature: toSerializedSignature({
|
|
55
|
+
signature,
|
|
56
|
+
signatureScheme: this.getKeyScheme(),
|
|
57
|
+
publicKey: this.#publicKey
|
|
58
|
+
})
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Signs the provided personal message.
|
|
63
|
+
* @returns The signed message bytes and signature.
|
|
64
|
+
*/
|
|
65
|
+
async signPersonalMessage(bytes) {
|
|
66
|
+
const intentMessage = messageWithIntent("PersonalMessage", bcs.byteVector().serialize(bytes).toBytes());
|
|
67
|
+
const { signature } = await this.#ledgerClient.signTransaction(this.#derivationPath, intentMessage);
|
|
68
|
+
return {
|
|
69
|
+
bytes: toBase64(bytes),
|
|
70
|
+
signature: toSerializedSignature({
|
|
71
|
+
signature,
|
|
72
|
+
signatureScheme: this.getKeyScheme(),
|
|
73
|
+
publicKey: this.#publicKey
|
|
74
|
+
})
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Prepares the signer by fetching and setting the public key from a Ledger device.
|
|
79
|
+
* It is recommended to initialize an `LedgerSigner` instance using this function.
|
|
80
|
+
* @returns A promise that resolves once a `LedgerSigner` instance is prepared (public key is set).
|
|
81
|
+
*/
|
|
82
|
+
static async fromDerivationPath(derivationPath, ledgerClient, suiClient) {
|
|
83
|
+
const { publicKey } = await ledgerClient.getPublicKey(derivationPath);
|
|
84
|
+
if (!publicKey) throw new Error("Failed to get public key from Ledger.");
|
|
85
|
+
return new LedgerSigner({
|
|
86
|
+
derivationPath,
|
|
87
|
+
publicKey: new Ed25519PublicKey(publicKey),
|
|
88
|
+
ledgerClient,
|
|
89
|
+
suiClient
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Generic signing is not supported by Ledger.
|
|
94
|
+
* @throws Always throws an error indicating generic signing is unsupported.
|
|
95
|
+
*/
|
|
96
|
+
sign() {
|
|
97
|
+
throw new Error("Ledger Signer does not support generic signing.");
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Generic signing is not supported by Ledger.
|
|
101
|
+
* @throws Always throws an error indicating generic signing is unsupported.
|
|
102
|
+
*/
|
|
103
|
+
signWithIntent() {
|
|
104
|
+
throw new Error("Ledger Signer does not support generic signing.");
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
export { LedgerSigner, getInputObjects };
|
|
110
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["#publicKey","#derivationPath","#ledgerClient","#suiClient"],"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type SuiLedgerClient from '@mysten/ledgerjs-hw-app-sui';\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport type { SignatureWithBytes } from '@mysten/sui/cryptography';\nimport { messageWithIntent, Signer, toSerializedSignature } from '@mysten/sui/cryptography';\nimport { Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519';\nimport { Transaction } from '@mysten/sui/transactions';\nimport { toBase64 } from '@mysten/sui/utils';\n\nimport { bcs } from '@mysten/sui/bcs';\nimport { getInputObjects } from './objects.js';\nimport type { Resolution } from '@mysten/ledgerjs-hw-app-sui';\n\nexport { getInputObjects } from './objects.js';\n\n/**\n * Configuration options for initializing the LedgerSigner.\n */\nexport interface LedgerSignerOptions {\n\tpublicKey: Ed25519PublicKey;\n\tderivationPath: string;\n\tledgerClient: SuiLedgerClient;\n\tsuiClient: ClientWithCoreApi;\n}\n\n/**\n * Ledger integrates with the Sui blockchain to provide signing capabilities using Ledger devices.\n */\nexport class LedgerSigner extends Signer {\n\t#derivationPath: string;\n\t#publicKey: Ed25519PublicKey;\n\t#ledgerClient: SuiLedgerClient;\n\t#suiClient: ClientWithCoreApi;\n\n\t/**\n\t * Creates an instance of LedgerSigner. It's expected to call the static `fromDerivationPath` method to create an instance.\n\t * @example\n\t * ```\n\t * const signer = await LedgerSigner.fromDerivationPath(derivationPath, options);\n\t * ```\n\t */\n\tconstructor({ publicKey, derivationPath, ledgerClient, suiClient }: LedgerSignerOptions) {\n\t\tsuper();\n\t\tthis.#publicKey = publicKey;\n\t\tthis.#derivationPath = derivationPath;\n\t\tthis.#ledgerClient = ledgerClient;\n\t\tthis.#suiClient = suiClient;\n\t}\n\n\t/**\n\t * Retrieves the key scheme used by this signer.\n\t */\n\toverride getKeyScheme() {\n\t\treturn 'ED25519' as const;\n\t}\n\n\t/**\n\t * Retrieves the public key associated with this signer.\n\t * @returns The Ed25519PublicKey instance.\n\t */\n\toverride getPublicKey() {\n\t\treturn this.#publicKey;\n\t}\n\n\t/**\n\t * Signs the provided transaction bytes.\n\t * @returns The signed transaction bytes and signature.\n\t */\n\toverride async signTransaction(\n\t\tbytes: Uint8Array,\n\t\tbcsObjects?: Uint8Array[],\n\t\tresolution?: Resolution,\n\t): Promise<SignatureWithBytes> {\n\t\tconst transactionOptions = bcsObjects\n\t\t\t? { bcsObjects }\n\t\t\t: await getInputObjects(Transaction.from(bytes), this.#suiClient).catch(() => ({\n\t\t\t\t\t// Fail gracefully so network errors or serialization issues don't break transaction signing:\n\t\t\t\t\tbcsObjects: [],\n\t\t\t\t}));\n\n\t\tconst intentMessage = messageWithIntent('TransactionData', bytes);\n\t\tconst { signature } = await this.#ledgerClient.signTransaction(\n\t\t\tthis.#derivationPath,\n\t\t\tintentMessage,\n\t\t\ttransactionOptions,\n\t\t\tresolution,\n\t\t);\n\n\t\treturn {\n\t\t\tbytes: toBase64(bytes),\n\t\t\tsignature: toSerializedSignature({\n\t\t\t\tsignature,\n\t\t\t\tsignatureScheme: this.getKeyScheme(),\n\t\t\t\tpublicKey: this.#publicKey,\n\t\t\t}),\n\t\t};\n\t}\n\n\t/**\n\t * Signs the provided personal message.\n\t * @returns The signed message bytes and signature.\n\t */\n\toverride async signPersonalMessage(bytes: Uint8Array): Promise<SignatureWithBytes> {\n\t\tconst intentMessage = messageWithIntent(\n\t\t\t'PersonalMessage',\n\t\t\tbcs.byteVector().serialize(bytes).toBytes(),\n\t\t);\n\t\tconst { signature } = await this.#ledgerClient.signTransaction(\n\t\t\tthis.#derivationPath,\n\t\t\tintentMessage,\n\t\t);\n\n\t\treturn {\n\t\t\tbytes: toBase64(bytes),\n\t\t\tsignature: toSerializedSignature({\n\t\t\t\tsignature,\n\t\t\t\tsignatureScheme: this.getKeyScheme(),\n\t\t\t\tpublicKey: this.#publicKey,\n\t\t\t}),\n\t\t};\n\t}\n\n\t/**\n\t * Prepares the signer by fetching and setting the public key from a Ledger device.\n\t * It is recommended to initialize an `LedgerSigner` instance using this function.\n\t * @returns A promise that resolves once a `LedgerSigner` instance is prepared (public key is set).\n\t */\n\tstatic async fromDerivationPath(\n\t\tderivationPath: string,\n\t\tledgerClient: SuiLedgerClient,\n\t\tsuiClient: ClientWithCoreApi,\n\t) {\n\t\tconst { publicKey } = await ledgerClient.getPublicKey(derivationPath);\n\t\tif (!publicKey) {\n\t\t\tthrow new Error('Failed to get public key from Ledger.');\n\t\t}\n\n\t\treturn new LedgerSigner({\n\t\t\tderivationPath,\n\t\t\tpublicKey: new Ed25519PublicKey(publicKey),\n\t\t\tledgerClient,\n\t\t\tsuiClient,\n\t\t});\n\t}\n\n\t/**\n\t * Generic signing is not supported by Ledger.\n\t * @throws Always throws an error indicating generic signing is unsupported.\n\t */\n\toverride sign(): never {\n\t\tthrow new Error('Ledger Signer does not support generic signing.');\n\t}\n\n\t/**\n\t * Generic signing is not supported by Ledger.\n\t * @throws Always throws an error indicating generic signing is unsupported.\n\t */\n\toverride signWithIntent(): never {\n\t\tthrow new Error('Ledger Signer does not support generic signing.');\n\t}\n}\n"],"mappings":";;;;;;;;;;;AA8BA,IAAa,eAAb,MAAa,qBAAqB,OAAO;CACxC;CACA;CACA;CACA;;;;;;;;CASA,YAAY,EAAE,WAAW,gBAAgB,cAAc,aAAkC;AACxF,SAAO;AACP,QAAKA,YAAa;AAClB,QAAKC,iBAAkB;AACvB,QAAKC,eAAgB;AACrB,QAAKC,YAAa;;;;;CAMnB,AAAS,eAAe;AACvB,SAAO;;;;;;CAOR,AAAS,eAAe;AACvB,SAAO,MAAKH;;;;;;CAOb,MAAe,gBACd,OACA,YACA,YAC8B;EAC9B,MAAM,qBAAqB,aACxB,EAAE,YAAY,GACd,MAAM,gBAAgB,YAAY,KAAK,MAAM,EAAE,MAAKG,UAAW,CAAC,aAAa,EAE7E,YAAY,EAAE,EACd,EAAE;EAEL,MAAM,gBAAgB,kBAAkB,mBAAmB,MAAM;EACjE,MAAM,EAAE,cAAc,MAAM,MAAKD,aAAc,gBAC9C,MAAKD,gBACL,eACA,oBACA,WACA;AAED,SAAO;GACN,OAAO,SAAS,MAAM;GACtB,WAAW,sBAAsB;IAChC;IACA,iBAAiB,KAAK,cAAc;IACpC,WAAW,MAAKD;IAChB,CAAC;GACF;;;;;;CAOF,MAAe,oBAAoB,OAAgD;EAClF,MAAM,gBAAgB,kBACrB,mBACA,IAAI,YAAY,CAAC,UAAU,MAAM,CAAC,SAAS,CAC3C;EACD,MAAM,EAAE,cAAc,MAAM,MAAKE,aAAc,gBAC9C,MAAKD,gBACL,cACA;AAED,SAAO;GACN,OAAO,SAAS,MAAM;GACtB,WAAW,sBAAsB;IAChC;IACA,iBAAiB,KAAK,cAAc;IACpC,WAAW,MAAKD;IAChB,CAAC;GACF;;;;;;;CAQF,aAAa,mBACZ,gBACA,cACA,WACC;EACD,MAAM,EAAE,cAAc,MAAM,aAAa,aAAa,eAAe;AACrE,MAAI,CAAC,UACJ,OAAM,IAAI,MAAM,wCAAwC;AAGzD,SAAO,IAAI,aAAa;GACvB;GACA,WAAW,IAAI,iBAAiB,UAAU;GAC1C;GACA;GACA,CAAC;;;;;;CAOH,AAAS,OAAc;AACtB,QAAM,IAAI,MAAM,kDAAkD;;;;;;CAOnE,AAAS,iBAAwB;AAChC,QAAM,IAAI,MAAM,kDAAkD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Transaction } from "@mysten/sui/transactions";
|
|
2
|
+
import { ClientWithCoreApi } from "@mysten/sui/client";
|
|
3
|
+
|
|
4
|
+
//#region src/objects.d.ts
|
|
5
|
+
declare const getInputObjects: (transaction: Transaction, client: ClientWithCoreApi) => Promise<{
|
|
6
|
+
bcsObjects: Uint8Array<ArrayBuffer>[];
|
|
7
|
+
}>;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { getInputObjects };
|
|
10
|
+
//# sourceMappingURL=objects.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objects.d.mts","names":[],"sources":["../src/objects.ts"],"mappings":";;;;cAMa,eAAA,GAAyB,WAAA,EAAa,WAAA,EAAa,MAAA,EAAQ,iBAAA,KAAiB,OAAA"}
|
package/dist/objects.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/objects.ts
|
|
2
|
+
const getInputObjects = async (transaction, client) => {
|
|
3
|
+
const data = transaction.getData();
|
|
4
|
+
const gasObjectIds = data.gasData.payment?.map((object) => object.objectId) ?? [];
|
|
5
|
+
const inputObjectIds = data.inputs.map((input) => {
|
|
6
|
+
return input.$kind === "Object" && input.Object.$kind === "ImmOrOwnedObject" ? input.Object.ImmOrOwnedObject.objectId : null;
|
|
7
|
+
}).filter((objectId) => !!objectId);
|
|
8
|
+
return { bcsObjects: (await client.core.getObjects({
|
|
9
|
+
objectIds: [...gasObjectIds, ...inputObjectIds],
|
|
10
|
+
include: { objectBcs: true }
|
|
11
|
+
})).objects.filter((obj) => !(obj instanceof Error)).map((object) => object.objectBcs).filter((bytes) => !!bytes) };
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { getInputObjects };
|
|
16
|
+
//# sourceMappingURL=objects.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objects.mjs","names":[],"sources":["../src/objects.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Transaction } from '@mysten/sui/transactions';\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\n\nexport const getInputObjects = async (transaction: Transaction, client: ClientWithCoreApi) => {\n\tconst data = transaction.getData();\n\n\tconst gasObjectIds = data.gasData.payment?.map((object) => object.objectId) ?? [];\n\tconst inputObjectIds = data.inputs\n\t\t.map((input) => {\n\t\t\treturn input.$kind === 'Object' && input.Object.$kind === 'ImmOrOwnedObject'\n\t\t\t\t? input.Object.ImmOrOwnedObject.objectId\n\t\t\t\t: null;\n\t\t})\n\t\t.filter((objectId): objectId is string => !!objectId);\n\n\tconst response = await client.core.getObjects({\n\t\tobjectIds: [...gasObjectIds, ...inputObjectIds],\n\t\tinclude: {\n\t\t\tobjectBcs: true,\n\t\t},\n\t});\n\n\tconst bcsObjects = response.objects\n\t\t.filter((obj): obj is Exclude<typeof obj, Error> => !(obj instanceof Error))\n\t\t.map((object) => object.objectBcs)\n\t\t.filter((bytes): bytes is Uint8Array<ArrayBuffer> => !!bytes);\n\n\treturn { bcsObjects };\n};\n"],"mappings":";AAMA,MAAa,kBAAkB,OAAO,aAA0B,WAA8B;CAC7F,MAAM,OAAO,YAAY,SAAS;CAElC,MAAM,eAAe,KAAK,QAAQ,SAAS,KAAK,WAAW,OAAO,SAAS,IAAI,EAAE;CACjF,MAAM,iBAAiB,KAAK,OAC1B,KAAK,UAAU;AACf,SAAO,MAAM,UAAU,YAAY,MAAM,OAAO,UAAU,qBACvD,MAAM,OAAO,iBAAiB,WAC9B;GACF,CACD,QAAQ,aAAiC,CAAC,CAAC,SAAS;AActD,QAAO,EAAE,aAZQ,MAAM,OAAO,KAAK,WAAW;EAC7C,WAAW,CAAC,GAAG,cAAc,GAAG,eAAe;EAC/C,SAAS,EACR,WAAW,MACX;EACD,CAAC,EAE0B,QAC1B,QAAQ,QAA2C,EAAE,eAAe,OAAO,CAC3E,KAAK,WAAW,OAAO,UAAU,CACjC,QAAQ,UAA4C,CAAC,CAAC,MAAM,EAEzC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mysten/ledger-signer",
|
|
3
|
+
"author": "Mysten Labs <build@mystenlabs.com>",
|
|
4
|
+
"description": "Sui signer for Ledger hardware wallets",
|
|
5
|
+
"homepage": "https://sdk.mystenlabs.com",
|
|
6
|
+
"version": "0.0.0",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.mts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"default": "./dist/index.mjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"CHANGELOG.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"README.md",
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/MystenLabs/ts-sdks.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/mystenlabs/ts-sdks/issues"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@mysten/ledgerjs-hw-app-sui": "^0.8.1"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@mysten/sui": "^2.16.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^25.0.8",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"vitest": "^4.0.17",
|
|
47
|
+
"@mysten/sui": "^2.16.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"clean": "rm -rf tsconfig.tsbuildinfo ./dist",
|
|
51
|
+
"build": "rm -rf dist && tsc --noEmit && tsdown",
|
|
52
|
+
"prettier:check": "prettier -c --ignore-unknown .",
|
|
53
|
+
"prettier:fix": "prettier -w --ignore-unknown .",
|
|
54
|
+
"oxlint:check": "oxlint .",
|
|
55
|
+
"oxlint:fix": "oxlint --fix",
|
|
56
|
+
"lint": "pnpm run oxlint:check && pnpm run prettier:check",
|
|
57
|
+
"lint:fix": "pnpm run oxlint:fix && pnpm run prettier:fix",
|
|
58
|
+
"test": "vitest run --passWithNoTests"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Copyright (c) Mysten Labs, Inc.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import type SuiLedgerClient from '@mysten/ledgerjs-hw-app-sui';
|
|
5
|
+
import type { ClientWithCoreApi } from '@mysten/sui/client';
|
|
6
|
+
import type { SignatureWithBytes } from '@mysten/sui/cryptography';
|
|
7
|
+
import { messageWithIntent, Signer, toSerializedSignature } from '@mysten/sui/cryptography';
|
|
8
|
+
import { Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519';
|
|
9
|
+
import { Transaction } from '@mysten/sui/transactions';
|
|
10
|
+
import { toBase64 } from '@mysten/sui/utils';
|
|
11
|
+
|
|
12
|
+
import { bcs } from '@mysten/sui/bcs';
|
|
13
|
+
import { getInputObjects } from './objects.js';
|
|
14
|
+
import type { Resolution } from '@mysten/ledgerjs-hw-app-sui';
|
|
15
|
+
|
|
16
|
+
export { getInputObjects } from './objects.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Configuration options for initializing the LedgerSigner.
|
|
20
|
+
*/
|
|
21
|
+
export interface LedgerSignerOptions {
|
|
22
|
+
publicKey: Ed25519PublicKey;
|
|
23
|
+
derivationPath: string;
|
|
24
|
+
ledgerClient: SuiLedgerClient;
|
|
25
|
+
suiClient: ClientWithCoreApi;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Ledger integrates with the Sui blockchain to provide signing capabilities using Ledger devices.
|
|
30
|
+
*/
|
|
31
|
+
export class LedgerSigner extends Signer {
|
|
32
|
+
#derivationPath: string;
|
|
33
|
+
#publicKey: Ed25519PublicKey;
|
|
34
|
+
#ledgerClient: SuiLedgerClient;
|
|
35
|
+
#suiClient: ClientWithCoreApi;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates an instance of LedgerSigner. It's expected to call the static `fromDerivationPath` method to create an instance.
|
|
39
|
+
* @example
|
|
40
|
+
* ```
|
|
41
|
+
* const signer = await LedgerSigner.fromDerivationPath(derivationPath, options);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
constructor({ publicKey, derivationPath, ledgerClient, suiClient }: LedgerSignerOptions) {
|
|
45
|
+
super();
|
|
46
|
+
this.#publicKey = publicKey;
|
|
47
|
+
this.#derivationPath = derivationPath;
|
|
48
|
+
this.#ledgerClient = ledgerClient;
|
|
49
|
+
this.#suiClient = suiClient;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Retrieves the key scheme used by this signer.
|
|
54
|
+
*/
|
|
55
|
+
override getKeyScheme() {
|
|
56
|
+
return 'ED25519' as const;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Retrieves the public key associated with this signer.
|
|
61
|
+
* @returns The Ed25519PublicKey instance.
|
|
62
|
+
*/
|
|
63
|
+
override getPublicKey() {
|
|
64
|
+
return this.#publicKey;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Signs the provided transaction bytes.
|
|
69
|
+
* @returns The signed transaction bytes and signature.
|
|
70
|
+
*/
|
|
71
|
+
override async signTransaction(
|
|
72
|
+
bytes: Uint8Array,
|
|
73
|
+
bcsObjects?: Uint8Array[],
|
|
74
|
+
resolution?: Resolution,
|
|
75
|
+
): Promise<SignatureWithBytes> {
|
|
76
|
+
const transactionOptions = bcsObjects
|
|
77
|
+
? { bcsObjects }
|
|
78
|
+
: await getInputObjects(Transaction.from(bytes), this.#suiClient).catch(() => ({
|
|
79
|
+
// Fail gracefully so network errors or serialization issues don't break transaction signing:
|
|
80
|
+
bcsObjects: [],
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
const intentMessage = messageWithIntent('TransactionData', bytes);
|
|
84
|
+
const { signature } = await this.#ledgerClient.signTransaction(
|
|
85
|
+
this.#derivationPath,
|
|
86
|
+
intentMessage,
|
|
87
|
+
transactionOptions,
|
|
88
|
+
resolution,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
bytes: toBase64(bytes),
|
|
93
|
+
signature: toSerializedSignature({
|
|
94
|
+
signature,
|
|
95
|
+
signatureScheme: this.getKeyScheme(),
|
|
96
|
+
publicKey: this.#publicKey,
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Signs the provided personal message.
|
|
103
|
+
* @returns The signed message bytes and signature.
|
|
104
|
+
*/
|
|
105
|
+
override async signPersonalMessage(bytes: Uint8Array): Promise<SignatureWithBytes> {
|
|
106
|
+
const intentMessage = messageWithIntent(
|
|
107
|
+
'PersonalMessage',
|
|
108
|
+
bcs.byteVector().serialize(bytes).toBytes(),
|
|
109
|
+
);
|
|
110
|
+
const { signature } = await this.#ledgerClient.signTransaction(
|
|
111
|
+
this.#derivationPath,
|
|
112
|
+
intentMessage,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
bytes: toBase64(bytes),
|
|
117
|
+
signature: toSerializedSignature({
|
|
118
|
+
signature,
|
|
119
|
+
signatureScheme: this.getKeyScheme(),
|
|
120
|
+
publicKey: this.#publicKey,
|
|
121
|
+
}),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Prepares the signer by fetching and setting the public key from a Ledger device.
|
|
127
|
+
* It is recommended to initialize an `LedgerSigner` instance using this function.
|
|
128
|
+
* @returns A promise that resolves once a `LedgerSigner` instance is prepared (public key is set).
|
|
129
|
+
*/
|
|
130
|
+
static async fromDerivationPath(
|
|
131
|
+
derivationPath: string,
|
|
132
|
+
ledgerClient: SuiLedgerClient,
|
|
133
|
+
suiClient: ClientWithCoreApi,
|
|
134
|
+
) {
|
|
135
|
+
const { publicKey } = await ledgerClient.getPublicKey(derivationPath);
|
|
136
|
+
if (!publicKey) {
|
|
137
|
+
throw new Error('Failed to get public key from Ledger.');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return new LedgerSigner({
|
|
141
|
+
derivationPath,
|
|
142
|
+
publicKey: new Ed25519PublicKey(publicKey),
|
|
143
|
+
ledgerClient,
|
|
144
|
+
suiClient,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Generic signing is not supported by Ledger.
|
|
150
|
+
* @throws Always throws an error indicating generic signing is unsupported.
|
|
151
|
+
*/
|
|
152
|
+
override sign(): never {
|
|
153
|
+
throw new Error('Ledger Signer does not support generic signing.');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Generic signing is not supported by Ledger.
|
|
158
|
+
* @throws Always throws an error indicating generic signing is unsupported.
|
|
159
|
+
*/
|
|
160
|
+
override signWithIntent(): never {
|
|
161
|
+
throw new Error('Ledger Signer does not support generic signing.');
|
|
162
|
+
}
|
|
163
|
+
}
|
package/src/objects.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Copyright (c) Mysten Labs, Inc.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import type { Transaction } from '@mysten/sui/transactions';
|
|
5
|
+
import type { ClientWithCoreApi } from '@mysten/sui/client';
|
|
6
|
+
|
|
7
|
+
export const getInputObjects = async (transaction: Transaction, client: ClientWithCoreApi) => {
|
|
8
|
+
const data = transaction.getData();
|
|
9
|
+
|
|
10
|
+
const gasObjectIds = data.gasData.payment?.map((object) => object.objectId) ?? [];
|
|
11
|
+
const inputObjectIds = data.inputs
|
|
12
|
+
.map((input) => {
|
|
13
|
+
return input.$kind === 'Object' && input.Object.$kind === 'ImmOrOwnedObject'
|
|
14
|
+
? input.Object.ImmOrOwnedObject.objectId
|
|
15
|
+
: null;
|
|
16
|
+
})
|
|
17
|
+
.filter((objectId): objectId is string => !!objectId);
|
|
18
|
+
|
|
19
|
+
const response = await client.core.getObjects({
|
|
20
|
+
objectIds: [...gasObjectIds, ...inputObjectIds],
|
|
21
|
+
include: {
|
|
22
|
+
objectBcs: true,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const bcsObjects = response.objects
|
|
27
|
+
.filter((obj): obj is Exclude<typeof obj, Error> => !(obj instanceof Error))
|
|
28
|
+
.map((object) => object.objectBcs)
|
|
29
|
+
.filter((bytes): bytes is Uint8Array<ArrayBuffer> => !!bytes);
|
|
30
|
+
|
|
31
|
+
return { bcsObjects };
|
|
32
|
+
};
|