@ledgerhq/hw-app-near
Version:
Ledger Hardware Wallet Near Application API
86 lines • 3.25 kB
JavaScript
import { PublicKey } from "near-api-js/lib/utils";
import { KeyType } from "near-api-js/lib/utils/key_pair";
import { bip32PathToBytes } from "./utils";
// Based on https://github.com/LedgerHQ/ledger-secure-sdk/blob/master/include/os_io.h#L16
// 255B + CLA + INS + P1 + P2 + Lc
const CHUNK_SIZE = 255;
const CLA = 0x80;
const INS_GET_PUBLIC_KEY = 4;
const INS_GET_ADDRESS = 5;
const INS_SIGN = 2;
const P1_LAST_CHUNK = 0x80;
const NETWORK_ID = "W".charCodeAt(0);
/**
* NEAR API
*
* @example
* import Near from "@ledgerhq/hw-app-near";
* const near = new Near(transport)
*/
export default class Near {
transport;
constructor(transport) {
this.transport = transport;
transport.decorateAppAPIMethods(this, ["getPublicKey", "getAddress", "sign"], "NEAR");
}
/**
* @param path
* @option verify - if true, user must verify if the address is correct on the device
* @return an object with a publicKey and address
* @example
* near.getAddress("44'/397'/0'/0'/0'", true).then(o => o.address)
*/
async getAddress(path, verify) {
const client = await createClient(this.transport);
if (verify) {
await client.getAddress(path);
}
const rawPublicKey = await client.getPublicKey(path, false);
const publicKey = new PublicKey({
keyType: KeyType.ED25519,
data: rawPublicKey,
});
return {
address: rawPublicKey.toString("hex"),
publicKey: publicKey.toString(),
};
}
/**
* @param transaction
* @param path
* @return a signature to be broadcasted to the chain
*/
async signTransaction(transaction, path) {
const client = await createClient(this.transport);
const signature = await client.sign(transaction, path);
return signature;
}
}
async function createClient(transport) {
return {
transport,
async getPublicKey(path, verify) {
const response = await this.transport.send(CLA, INS_GET_PUBLIC_KEY, verify ? 0 : 1, NETWORK_ID, bip32PathToBytes(path));
return Buffer.from(response.subarray(0, -2));
},
async getAddress(path) {
const response = await this.transport.send(CLA, INS_GET_ADDRESS, 0, NETWORK_ID, bip32PathToBytes(path));
return Buffer.from(response.subarray(0, -2));
},
async sign(transactionData, path) {
transactionData = Buffer.from(transactionData);
const concatenatedData = Buffer.concat([bip32PathToBytes(path), transactionData]);
const chunks = new Array(Math.ceil(concatenatedData.length / CHUNK_SIZE))
.fill("")
.map((_, i) => concatenatedData.subarray(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE));
for (const chunk of chunks) {
const isLastChunk = chunks.indexOf(chunk) === chunks.length - 1;
const response = await this.transport.send(CLA, INS_SIGN, isLastChunk ? P1_LAST_CHUNK : 0, NETWORK_ID, chunk);
if (isLastChunk) {
return Buffer.from(response.subarray(0, -2));
}
}
},
};
}
//# sourceMappingURL=Near.js.map