New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@near-wallet-selector/ledger

Package Overview
Dependencies
Maintainers
6
Versions
77
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@near-wallet-selector/ledger - npm Package Compare versions

Comparing version 3.1.0 to 4.0.0-alpha.0

11

package.json
{
"name": "@near-wallet-selector/ledger",
"version": "3.1.0",
"version": "4.0.0-alpha.0",
"main": "./src/index.js",

@@ -11,9 +11,8 @@ "typings": "./src/index.d.ts",

"bn.js": "^5.2.0",
"@ledgerhq/logs": "^6.10.0",
"@near-wallet-selector/core": "3.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"is-mobile": "^3.0.0"
"@near-wallet-selector/core": "4.0.0-alpha.0",
"rxjs": "~7.4.0",
"is-mobile": "^3.0.0",
"@near-wallet-selector/wallet-utils": "4.0.0-alpha.0"
},
"peerDependencies": {}
}

@@ -20,3 +20,3 @@ # @near-wallet-selector/ledger

```ts
import NearWalletSelector from "@near-wallet-selector/core";
import { setupWalletSelector } from "@near-wallet-selector/core";
import { setupLedger } from "@near-wallet-selector/ledger";

@@ -29,6 +29,6 @@

const selector = await NearWalletSelector.init({
const selector = await setupWalletSelector({
network: "testnet",
contractId: "guest-book.testnet",
wallets: [ledger],
modules: [ledger],
});

@@ -35,0 +35,0 @@ ```

/// <reference types="node" />
import { Log } from "@ledgerhq/logs";
export declare const CLA = 128;

@@ -26,10 +25,8 @@ export declare const INS_SIGN = 2;

}
export declare const isLedgerSupported: () => boolean;
export declare class LedgerClient {
private transport;
static isSupported: () => boolean;
isConnected: () => boolean;
connect: () => Promise<void>;
disconnect: () => Promise<void>;
listen: (callback: (data: Log) => void) => {
remove: () => void;
};
setScrambleKey: (key: string) => void;

@@ -36,0 +33,0 @@ on: <Event_1 extends "disconnect">(event: Event_1, callback: (data: EventMap[Event_1]) => void) => Subscription;

import { __awaiter } from "tslib";
import TransportWebHID from "@ledgerhq/hw-transport-webhid";
import { listen } from "@ledgerhq/logs";
import { utils } from "near-api-js";

@@ -37,30 +36,54 @@ // Further reading regarding APDU Ledger API:

export const networkId = "W".charCodeAt(0);
// TODO: Needs a method to assert whether we're connected.
// Not using TransportWebHID.isSupported as it's chosen to use a Promise...
export const isLedgerSupported = () => {
var _a;
return !!((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.hid);
};
export class LedgerClient {
constructor() {
this.transport = null;
this.isConnected = () => {
return Boolean(this.transport);
};
this.connect = () => __awaiter(this, void 0, void 0, function* () {
this.transport = yield TransportWebHID.create();
const handleDisconnect = () => {
var _a;
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.off("disconnect", handleDisconnect);
this.transport = null;
};
this.transport.on("disconnect", handleDisconnect);
});
this.disconnect = () => {
return this.transport.close();
};
this.listen = (callback) => {
const unsubscribe = listen(callback);
return {
remove: () => unsubscribe(),
};
};
this.disconnect = () => __awaiter(this, void 0, void 0, function* () {
if (!this.transport) {
throw new Error("Device not connected");
}
yield this.transport.close();
this.transport = null;
});
this.setScrambleKey = (key) => {
if (!this.transport) {
throw new Error("Device not connected");
}
this.transport.setScrambleKey(key);
};
this.on = (event, callback) => {
if (!this.transport) {
throw new Error("Device not connected");
}
this.transport.on(event, callback);
return {
remove: () => this.transport.off(event, callback),
remove: () => { var _a; return (_a = this.transport) === null || _a === void 0 ? void 0 : _a.off(event, callback); },
};
};
this.off = (event, callback) => {
if (!this.transport) {
throw new Error("Device not connected");
}
this.transport.off(event, callback);
};
this.getVersion = () => __awaiter(this, void 0, void 0, function* () {
if (!this.transport) {
throw new Error("Device not connected");
}
const res = yield this.transport.send(CLA, INS_GET_APP_VERSION, P1_IGNORE, P2_IGNORE);

@@ -71,2 +94,5 @@ const [major, minor, patch] = Array.from(res);

this.getPublicKey = ({ derivationPath }) => __awaiter(this, void 0, void 0, function* () {
if (!this.transport) {
throw new Error("Device not connected");
}
const res = yield this.transport.send(CLA, INS_GET_PUBLIC_KEY, P2_IGNORE, networkId, parseDerivationPath(derivationPath));

@@ -76,2 +102,5 @@ return utils.serialize.base_encode(res.subarray(0, -2));

this.sign = ({ data, derivationPath }) => __awaiter(this, void 0, void 0, function* () {
if (!this.transport) {
throw new Error("Device not connected");
}
// NOTE: getVersion call resets state to avoid starting from partially filled buffer

@@ -96,7 +125,2 @@ yield this.getVersion();

}
// Not using TransportWebHID.isSupported as it's chosen to use a Promise...
LedgerClient.isSupported = () => {
var _a;
return !!((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.hid);
};
//# sourceMappingURL=ledger-client.js.map

@@ -1,6 +0,6 @@

import { HardwareWallet, WalletModule } from "@near-wallet-selector/core";
import type { WalletModuleFactory, HardwareWallet } from "@near-wallet-selector/core";
export interface LedgerParams {
iconUrl?: string;
}
export declare const LOCAL_STORAGE_AUTH_DATA = "ledger:authData";
export declare function setupLedger({ iconUrl, }?: LedgerParams): WalletModule<HardwareWallet>;
export declare const STORAGE_ACCOUNTS = "accounts";
export declare function setupLedger({ iconUrl, }?: LedgerParams): WalletModuleFactory<HardwareWallet>;
import { __awaiter } from "tslib";
import { transactions as nearTransactions, utils } from "near-api-js";
import { isMobile } from "is-mobile";
import { TypedError } from "near-api-js/lib/utils/errors";
import isMobile from "is-mobile";
import { transformActions, } from "@near-wallet-selector/core";
import { LedgerClient } from "./ledger-client";
export const LOCAL_STORAGE_AUTH_DATA = `ledger:authData`;
export function setupLedger({ iconUrl, } = {}) {
return function Ledger({ provider, emitter, logger, storage, updateState }) {
let client;
const subscriptions = {};
const state = { authData: null };
const debugMode = false;
const getAccounts = () => {
var _a;
const accountId = (_a = state.authData) === null || _a === void 0 ? void 0 : _a.accountId;
if (!accountId) {
return [];
import { signTransactions } from "@near-wallet-selector/wallet-utils";
import { isLedgerSupported, LedgerClient } from "./ledger-client";
import { utils } from "near-api-js";
export const STORAGE_ACCOUNTS = "accounts";
const setupLedgerState = (storage) => __awaiter(void 0, void 0, void 0, function* () {
const accounts = yield storage.getItem(STORAGE_ACCOUNTS);
return {
client: new LedgerClient(),
subscriptions: [],
accounts: accounts || [],
};
});
const Ledger = ({ options, store, provider, logger, storage, }) => __awaiter(void 0, void 0, void 0, function* () {
const _state = yield setupLedgerState(storage);
const signer = {
createKey: () => {
throw new Error("Not implemented");
},
getPublicKey: (accountId) => __awaiter(void 0, void 0, void 0, function* () {
const account = _state.accounts.find((a) => a.accountId === accountId);
if (!account) {
throw new Error("Failed to find public key for account");
}
return [{ accountId }];
};
const signOut = () => __awaiter(this, void 0, void 0, function* () {
for (const key in subscriptions) {
subscriptions[key].remove();
return utils.PublicKey.from(account.publicKey);
}),
signMessage: (message, accountId) => __awaiter(void 0, void 0, void 0, function* () {
const account = _state.accounts.find((a) => a.accountId === accountId);
if (!account) {
throw new Error("Failed to find account for signing");
}
storage.removeItem(LOCAL_STORAGE_AUTH_DATA);
// Only close if we've already connected.
if (client) {
yield client.disconnect();
}
updateState((prevState) => (Object.assign(Object.assign({}, prevState), { selectedWalletId: null })));
state.authData = null;
client = null;
const accounts = getAccounts();
emitter.emit("accountsChanged", { accounts });
emitter.emit("signOut", { accounts });
});
const getClient = () => __awaiter(this, void 0, void 0, function* () {
if (client) {
return client;
}
const ledgerClient = new LedgerClient();
yield ledgerClient.connect();
ledgerClient.setScrambleKey("NEAR");
subscriptions["disconnect"] = ledgerClient.on("disconnect", (err) => {
const signature = yield _state.client.sign({
data: message,
derivationPath: account.derivationPath,
});
return {
signature,
publicKey: utils.PublicKey.from(account.publicKey),
};
}),
};
const getAccounts = () => {
return _state.accounts.map((x) => ({
accountId: x.accountId,
}));
};
const cleanup = () => {
_state.subscriptions.forEach((subscription) => subscription.remove());
_state.subscriptions = [];
_state.accounts = [];
storage.removeItem(STORAGE_ACCOUNTS);
};
const signOut = () => __awaiter(void 0, void 0, void 0, function* () {
if (_state.client.isConnected()) {
yield _state.client.disconnect().catch((err) => {
logger.log("Failed to disconnect device");
logger.error(err);
signOut();
});
if (debugMode) {
subscriptions["logs"] = ledgerClient.listen((data) => {
logger.log("Ledger:init:logs", data);
});
}
cleanup();
});
const connectLedgerDevice = () => __awaiter(void 0, void 0, void 0, function* () {
if (_state.client.isConnected()) {
return;
}
yield _state.client.connect();
});
const validateAccessKey = ({ accountId, publicKey, }) => {
logger.log("validateAccessKey", { accountId, publicKey });
return provider.viewAccessKey({ accountId, publicKey }).then((accessKey) => {
logger.log("validateAccessKey:accessKey", { accessKey });
if (accessKey.permission !== "FullAccess") {
throw new Error("Public key requires 'FullAccess' permission");
}
client = ledgerClient;
return ledgerClient;
});
const validate = ({ accountId, derivationPath }) => __awaiter(this, void 0, void 0, function* () {
logger.log("Ledger:validate", { accountId, derivationPath });
const ledgerClient = yield getClient();
const publicKey = yield ledgerClient.getPublicKey({
derivationPath: derivationPath,
});
logger.log("Ledger:validate:publicKey", { publicKey });
try {
const accessKey = yield provider.viewAccessKey({
accountId,
publicKey,
});
logger.log("Ledger:validate:accessKey", { accessKey });
if (accessKey.permission !== "FullAccess") {
throw new Error("Public key requires 'FullAccess' permission");
}
return {
publicKey,
accessKey,
};
return accessKey;
}, (err) => {
if (err instanceof TypedError && err.type === "AccessKeyDoesNotExist") {
return null;
}
catch (err) {
if (err instanceof TypedError && err.type === "AccessKeyDoesNotExist") {
return {
publicKey,
accessKey: null,
};
}
throw err;
}
throw err;
});
const signTransaction = (transaction, ledgerClient, derivationPath) => __awaiter(this, void 0, void 0, function* () {
const serializedTx = utils.serialize.serialize(nearTransactions.SCHEMA, transaction);
const signature = yield ledgerClient.sign({
data: serializedTx,
derivationPath,
});
return new nearTransactions.SignedTransaction({
transaction,
signature: new nearTransactions.Signature({
keyType: transaction.publicKey.keyType,
data: signature,
}),
});
};
const getAccountIdFromPublicKey = ({ publicKey, }) => __awaiter(void 0, void 0, void 0, function* () {
const response = yield fetch(`${options.network.helperUrl}/publicKey/ed25519:${publicKey}/accounts`);
if (!response.ok) {
throw new Error("Failed to get account id from public key");
}
const accountIds = yield response.json();
if (!Array.isArray(accountIds) || !accountIds.length) {
throw new Error("Failed to find account linked for public key: " + publicKey);
}
return accountIds[0];
});
const transformTransactions = (transactions) => {
const accounts = getAccounts();
const { contract } = store.getState();
if (!accounts.length || !contract) {
throw new Error("Wallet not signed in");
}
return transactions.map((transaction) => {
return {
signerId: transaction.signerId || accounts[0].accountId,
receiverId: transaction.receiverId || contract.contractId,
actions: transaction.actions,
};
});
const signTransactions = (transactions) => __awaiter(this, void 0, void 0, function* () {
if (!state.authData) {
throw new Error("Not signed in");
}
const { accountId, derivationPath, publicKey } = state.authData;
const ledgerClient = yield getClient();
const [block, accessKey] = yield Promise.all([
provider.block({ finality: "final" }),
provider.viewAccessKey({ accountId, publicKey }),
]);
const signedTransactions = [];
for (let i = 0; i < transactions.length; i++) {
const actions = transformActions(transactions[i].actions);
const transaction = nearTransactions.createTransaction(accountId, utils.PublicKey.from(publicKey), transactions[i].receiverId, accessKey.nonce + i + 1, actions, utils.serialize.base_decode(block.header.hash));
const signedTx = yield signTransaction(transaction, ledgerClient, derivationPath);
signedTransactions.push(signedTx);
}
return signedTransactions;
});
return {
id: "ledger",
type: "hardware",
name: "Ledger",
description: null,
iconUrl: iconUrl || "./assets/ledger-icon.png",
isAvailable() {
if (!LedgerClient.isSupported()) {
return false;
};
return {
signIn({ derivationPaths }) {
return __awaiter(this, void 0, void 0, function* () {
const existingAccounts = getAccounts();
if (existingAccounts.length) {
return existingAccounts;
}
if (isMobile()) {
return false;
if (!derivationPaths.length) {
throw new Error("Invalid derivation paths");
}
return true;
},
init() {
return __awaiter(this, void 0, void 0, function* () {
state.authData = storage.getItem(LOCAL_STORAGE_AUTH_DATA);
});
},
signIn({ accountId, derivationPath }) {
return __awaiter(this, void 0, void 0, function* () {
if (yield this.isSignedIn()) {
return;
// Note: Connection must be triggered by user interaction.
yield connectLedgerDevice();
const accounts = [];
for (let i = 0; i < derivationPaths.length; i += 1) {
const derivationPath = derivationPaths[i];
const publicKey = yield _state.client.getPublicKey({ derivationPath });
const accountId = yield getAccountIdFromPublicKey({ publicKey });
if (accounts.some((x) => x.accountId === accountId)) {
throw new Error("Duplicate account id: " + accountId);
}
if (!accountId) {
throw new Error("Invalid account id");
}
if (!derivationPath) {
throw new Error("Invalid derivation path");
}
const { publicKey, accessKey } = yield validate({
accountId,
derivationPath,
});
const accessKey = yield validateAccessKey({ accountId, publicKey });
if (!accessKey) {
throw new Error(`Public key is not registered with the account '${accountId}'.`);
}
const authData = {
accounts.push({
accountId,
derivationPath,
publicKey,
};
storage.setItem(LOCAL_STORAGE_AUTH_DATA, authData);
state.authData = authData;
updateState((prevState) => (Object.assign(Object.assign({}, prevState), { showModal: false, selectedWalletId: this.id })));
const accounts = getAccounts();
emitter.emit("signIn", { accounts });
emitter.emit("accountsChanged", { accounts });
});
},
signOut,
isSignedIn() {
return __awaiter(this, void 0, void 0, function* () {
return !!state.authData;
});
},
getAccounts() {
return __awaiter(this, void 0, void 0, function* () {
return getAccounts();
});
},
signAndSendTransaction({ signerId, receiverId, actions }) {
return __awaiter(this, void 0, void 0, function* () {
logger.log("Ledger:signAndSendTransaction", {
signerId,
receiverId,
actions,
});
if (!state.authData) {
throw new Error("Not signed in");
}
const { accountId, derivationPath, publicKey } = state.authData;
const ledgerClient = yield getClient();
const [block, accessKey] = yield Promise.all([
provider.block({ finality: "final" }),
provider.viewAccessKey({ accountId, publicKey }),
]);
logger.log("Ledger:signAndSendTransaction:block", block);
logger.log("Ledger:signAndSendTransaction:accessKey", accessKey);
const transaction = nearTransactions.createTransaction(accountId, utils.PublicKey.from(publicKey), receiverId, accessKey.nonce + 1, transformActions(actions), utils.serialize.base_decode(block.header.hash));
const signedTx = yield signTransaction(transaction, ledgerClient, derivationPath);
return provider.sendTransaction(signedTx);
});
}
yield storage.setItem(STORAGE_ACCOUNTS, accounts);
_state.accounts = accounts;
return getAccounts();
});
},
signOut,
getAccounts() {
return __awaiter(this, void 0, void 0, function* () {
return getAccounts();
});
},
signAndSendTransaction({ signerId, receiverId, actions }) {
return __awaiter(this, void 0, void 0, function* () {
logger.log("signAndSendTransaction", { signerId, receiverId, actions });
if (!_state.accounts.length) {
throw new Error("Wallet not signed in");
}
// Note: Connection must be triggered by user interaction.
yield connectLedgerDevice();
const signedTransactions = yield signTransactions(transformTransactions([{ signerId, receiverId, actions }]), signer, options.network);
return provider.sendTransaction(signedTransactions[0]);
});
},
signAndSendTransactions({ transactions }) {
return __awaiter(this, void 0, void 0, function* () {
logger.log("signAndSendTransactions", { transactions });
if (!_state.accounts.length) {
throw new Error("Wallet not signed in");
}
// Note: Connection must be triggered by user interaction.
yield connectLedgerDevice();
const signedTransactions = yield signTransactions(transformTransactions(transactions), signer, options.network);
return Promise.all(signedTransactions.map((signedTx) => provider.sendTransaction(signedTx)));
});
},
};
});
export function setupLedger({ iconUrl = "./assets/ledger-icon.png", } = {}) {
return () => __awaiter(this, void 0, void 0, function* () {
const mobile = isMobile();
const supported = isLedgerSupported();
if (mobile || !supported) {
return null;
}
return {
id: "ledger",
type: "hardware",
metadata: {
name: "Ledger",
description: null,
iconUrl,
deprecated: false,
},
signAndSendTransactions({ transactions }) {
return __awaiter(this, void 0, void 0, function* () {
const signedTransactions = yield signTransactions(transactions);
return Promise.all(signedTransactions.map((signedTx) => provider.sendTransaction(signedTx)));
});
},
init: Ledger,
};
};
});
}
//# sourceMappingURL=ledger.js.map

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc