@shapeshiftoss/hdwallet-gridplus 1.62.10-alpha.1 → 1.62.10-alpha.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshiftoss/hdwallet-gridplus",
3
- "version": "1.62.10-alpha.1",
3
+ "version": "1.62.10-alpha.2",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -20,7 +20,7 @@
20
20
  "@ethereumjs/rlp": "5.0.2",
21
21
  "@ethereumjs/tx": "5.4.0",
22
22
  "@metamask/eth-sig-util": "^7.0.0",
23
- "@shapeshiftoss/hdwallet-core": "1.62.10-alpha.1",
23
+ "@shapeshiftoss/hdwallet-core": "1.62.10-alpha.2",
24
24
  "bech32": "^1.1.4",
25
25
  "bs58": "^5.0.0",
26
26
  "crypto-js": "^4.2.0",
@@ -35,5 +35,5 @@
35
35
  "@ethereumjs/common",
36
36
  "@ethereumjs/tx"
37
37
  ],
38
- "gitHead": "86c4f311d027cbd873538328071c83e37620dba1"
38
+ "gitHead": "1939f8fa5ec5b2a4e3053ecf9fa88b64efc6f88e"
39
39
  }
package/src/adapter.ts CHANGED
@@ -20,16 +20,11 @@ export class GridPlusAdapter {
20
20
  return new GridPlusAdapter(keyring);
21
21
  }
22
22
 
23
- public async connectDevice(
24
- deviceId: string,
25
- password = ""
26
- ): Promise<{ wallet?: GridPlusHDWallet; sessionId: string }> {
23
+ public async connectDevice(deviceId: string, password = ""): Promise<GridPlusHDWallet | undefined> {
27
24
  const privKey = createHash("sha256")
28
25
  .update(deviceId + password + name)
29
26
  .digest();
30
27
 
31
- const sessionId = privKey.toString("hex");
32
-
33
28
  if (!this.client) {
34
29
  this.client = new Client({ name, baseUrl, privKey, deviceId });
35
30
  } else {
@@ -39,9 +34,7 @@ export class GridPlusAdapter {
39
34
  }
40
35
 
41
36
  const isPaired = await this.client.connect(deviceId);
42
- if (isPaired) return { wallet: new GridPlusHDWallet(this.client), sessionId };
43
-
44
- return { sessionId: privKey.toString("hex") };
37
+ if (isPaired) return new GridPlusHDWallet(this.client);
45
38
  }
46
39
 
47
40
  public async pairDevice(pairingCode: string): Promise<GridPlusHDWallet> {
package/src/bitcoin.ts CHANGED
@@ -53,6 +53,8 @@ export async function btcSignTx(client: Client, msg: core.BTCSignTx): Promise<co
53
53
  });
54
54
 
55
55
  if (!address) throw new Error("No address returned from device");
56
+
57
+ return address;
56
58
  }
57
59
 
58
60
  throw new Error("Invalid output (no address or addressNList specified).");
@@ -182,14 +184,11 @@ export async function btcSignTx(client: Client, msg: core.BTCSignTx): Promise<co
182
184
 
183
185
  const tx = psbt.extractTransaction(true);
184
186
 
185
- const signatures = tx.ins.map((input) => {
186
- if (input.witness.length > 0) {
187
- return Buffer.from(input.witness[0]).toString("hex");
188
- } else {
189
- const sigLen = input.script[0];
190
- return Buffer.from(input.script.slice(1, sigLen)).toString("hex");
191
- }
192
- });
187
+ console.log({ msg, tx, psbt });
188
+
189
+ const signatures = psbt.data.inputs.map((input) =>
190
+ input.partialSig ? Buffer.from(input.partialSig[0].signature).toString("hex") : ""
191
+ );
193
192
 
194
193
  return { signatures, serializedTx: tx.toHex() };
195
194
  }
package/src/gridplus.ts CHANGED
@@ -9,6 +9,8 @@ import * as mayachain from "./mayachain";
9
9
  import * as solana from "./solana";
10
10
  import * as thorchain from "./thorchain";
11
11
 
12
+ const ZERO_BUFFER = Buffer.alloc(32)
13
+
12
14
  export function isGridPlus(wallet: core.HDWallet): wallet is GridPlusHDWallet {
13
15
  return isObject(wallet) && (wallet as any)._isGridPlus;
14
16
  }
@@ -349,7 +351,6 @@ export class GridPlusHDWallet
349
351
  }
350
352
 
351
353
  async isLocked(): Promise<boolean> {
352
- if (!this.client) throw new Error("Device not connected");
353
354
  return false;
354
355
  }
355
356
 
@@ -361,9 +362,13 @@ export class GridPlusHDWallet
361
362
  this.client = undefined;
362
363
  }
363
364
 
364
- getSessionId(): string | undefined {
365
+ async getActiveWallet(): Promise<string | undefined> {
365
366
  if (!this.client) throw new Error("Device not connected");
366
- return JSON.parse(this.client.getStateData())["privKey"];
367
+
368
+ const { external, internal } = await this.client.fetchActiveWallet();
369
+
370
+ if (!external.uid.equals(ZERO_BUFFER)) return external.uid.toString("hex")
371
+ if (!internal.uid.equals(ZERO_BUFFER)) return internal.uid.toString("hex")
367
372
  }
368
373
 
369
374
  async getPublicKeys(msg: Array<core.GetPublicKey>): Promise<Array<core.PublicKey | null>> {
@@ -1,28 +0,0 @@
1
- import * as core from "@shapeshiftoss/hdwallet-core";
2
- import { Client } from "gridplus-sdk";
3
- export type GridPlusTransportConfig = {
4
- deviceId: string;
5
- password?: string;
6
- };
7
- export declare class GridPlusTransport extends core.Transport {
8
- deviceId?: string;
9
- password?: string;
10
- connected: boolean;
11
- private client?;
12
- private sessionId?;
13
- constructor(config: GridPlusTransportConfig);
14
- getDeviceID(): Promise<string>;
15
- connect(): Promise<void>;
16
- connectGridPlus(deviceId: string, password?: string): Promise<void>;
17
- disconnect(): Promise<void>;
18
- isConnected(): boolean;
19
- setup(deviceId: string, password?: string, existingSessionId?: string): Promise<{
20
- isPaired: boolean;
21
- sessionId: string;
22
- }>;
23
- pair(pairingCode: string): Promise<boolean>;
24
- getClient(): Client | undefined;
25
- getSessionId(): string | undefined;
26
- call(): Promise<any>;
27
- }
28
- //# sourceMappingURL=transport.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,8BAA8B,CAAC;AAErD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,IAAI,CAAC,SAAS;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAS;IAClC,OAAO,CAAC,MAAM,CAAC,CAAS;IAIxB,OAAO,CAAC,SAAS,CAAC,CAAS;gBAEf,MAAM,EAAE,uBAAuB;IAMpC,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAYxB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,WAAW,IAAI,OAAO;IAIhB,KAAK,CAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAgDvC,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUjD,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B,YAAY,IAAI,MAAM,GAAG,SAAS;IAI5B,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC;CAGlC"}
package/dist/transport.js DELETED
@@ -1,148 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
34
- Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.GridPlusTransport = void 0;
36
- const core = __importStar(require("@shapeshiftoss/hdwallet-core"));
37
- const crypto_1 = require("crypto");
38
- const gridplus_sdk_1 = require("gridplus-sdk");
39
- class GridPlusTransport extends core.Transport {
40
- constructor(config) {
41
- super(new core.Keyring());
42
- this.connected = false;
43
- this.deviceId = config.deviceId;
44
- this.password = config.password;
45
- }
46
- getDeviceID() {
47
- return Promise.resolve(this.deviceId || "");
48
- }
49
- connect() {
50
- return __awaiter(this, void 0, void 0, function* () {
51
- if (!this.deviceId) {
52
- throw new Error("Device ID is required to connect to GridPlus");
53
- }
54
- const { isPaired } = yield this.setup(this.deviceId, this.password);
55
- if (!isPaired) {
56
- throw new Error("Device is not paired");
57
- }
58
- });
59
- }
60
- connectGridPlus(deviceId, password) {
61
- return __awaiter(this, void 0, void 0, function* () {
62
- this.deviceId = deviceId;
63
- this.password = password || "shapeshift-default";
64
- yield this.connect();
65
- });
66
- }
67
- disconnect() {
68
- return __awaiter(this, void 0, void 0, function* () {
69
- this.connected = false;
70
- this.deviceId = undefined;
71
- this.password = undefined;
72
- });
73
- }
74
- isConnected() {
75
- return this.connected;
76
- }
77
- setup(deviceId, password, existingSessionId) {
78
- return __awaiter(this, void 0, void 0, function* () {
79
- this.deviceId = deviceId;
80
- this.password = password || "shapeshift-default";
81
- // Use existing sessionId if provided, otherwise generate new one
82
- if (existingSessionId) {
83
- this.sessionId = existingSessionId;
84
- }
85
- else if (!this.sessionId) {
86
- this.sessionId = (0, crypto_1.randomBytes)(32).toString("hex");
87
- }
88
- // Create Client instance directly (Frame pattern) - no localStorage!
89
- // This ensures we always get fresh activeWallets from device
90
- if (!this.client) {
91
- this.client = new gridplus_sdk_1.Client({
92
- name: "ShapeShift",
93
- baseUrl: "https://signing.gridpl.us",
94
- privKey: Buffer.from(this.sessionId, "hex"),
95
- retryCount: 3,
96
- timeout: 60000,
97
- skipRetryOnWrongWallet: true,
98
- });
99
- try {
100
- // Connect to device - returns true if paired, false if needs pairing
101
- const isPaired = yield this.client.connect(deviceId);
102
- this.connected = true;
103
- return { isPaired, sessionId: this.sessionId };
104
- }
105
- catch (error) {
106
- // Handle "Device Locked" error - treat as unpaired
107
- const errorMessage = error instanceof Error ? error.message : String(error);
108
- if (errorMessage.toLowerCase().includes("device locked")) {
109
- this.connected = true;
110
- return { isPaired: false, sessionId: this.sessionId };
111
- }
112
- throw error;
113
- }
114
- }
115
- else {
116
- // Client already exists, reset active wallets to clear stale state before reconnecting
117
- // This is critical when switching between SafeCards - ensures fresh wallet state from device
118
- this.client.resetActiveWallets();
119
- const isPaired = yield this.client.connect(deviceId);
120
- this.connected = true;
121
- return { isPaired, sessionId: this.sessionId };
122
- }
123
- });
124
- }
125
- pair(pairingCode) {
126
- return __awaiter(this, void 0, void 0, function* () {
127
- if (!this.client) {
128
- throw new Error("Client not initialized. Call setup() first.");
129
- }
130
- const result = yield this.client.pair(pairingCode);
131
- this.connected = !!result;
132
- return !!result;
133
- });
134
- }
135
- getClient() {
136
- return this.client;
137
- }
138
- getSessionId() {
139
- return this.sessionId;
140
- }
141
- call() {
142
- return __awaiter(this, void 0, void 0, function* () {
143
- throw new Error("GridPlus transport call not implemented");
144
- });
145
- }
146
- }
147
- exports.GridPlusTransport = GridPlusTransport;
148
- //# sourceMappingURL=transport.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
package/src/transport.ts DELETED
@@ -1,131 +0,0 @@
1
- import * as core from "@shapeshiftoss/hdwallet-core";
2
- import { randomBytes } from "crypto";
3
- import { Client } from "gridplus-sdk";
4
-
5
- export type GridPlusTransportConfig = {
6
- deviceId: string;
7
- password?: string;
8
- };
9
-
10
- export class GridPlusTransport extends core.Transport {
11
- public deviceId?: string;
12
- public password?: string;
13
- public connected: boolean = false;
14
- private client?: Client;
15
- // Session identifier used to track reconnections. When present, we can skip
16
- // passing deviceId to SDK setup() which avoids triggering the pairing screen
17
- // on the device and enables faster reconnection from localStorage.
18
- private sessionId?: string;
19
-
20
- constructor(config: GridPlusTransportConfig) {
21
- super(new core.Keyring());
22
- this.deviceId = config.deviceId;
23
- this.password = config.password;
24
- }
25
-
26
- public getDeviceID(): Promise<string> {
27
- return Promise.resolve(this.deviceId || "");
28
- }
29
-
30
- public async connect(): Promise<void> {
31
- if (!this.deviceId) {
32
- throw new Error("Device ID is required to connect to GridPlus");
33
- }
34
-
35
- const { isPaired } = await this.setup(this.deviceId, this.password);
36
-
37
- if (!isPaired) {
38
- throw new Error("Device is not paired");
39
- }
40
- }
41
-
42
- public async connectGridPlus(deviceId: string, password?: string): Promise<void> {
43
- this.deviceId = deviceId;
44
- this.password = password || "shapeshift-default";
45
- await this.connect();
46
- }
47
-
48
- public async disconnect(): Promise<void> {
49
- this.connected = false;
50
- this.deviceId = undefined;
51
- this.password = undefined;
52
- }
53
-
54
- public isConnected(): boolean {
55
- return this.connected;
56
- }
57
-
58
- public async setup(
59
- deviceId: string,
60
- password?: string,
61
- existingSessionId?: string
62
- ): Promise<{ isPaired: boolean; sessionId: string }> {
63
- this.deviceId = deviceId;
64
- this.password = password || "shapeshift-default";
65
-
66
- // Use existing sessionId if provided, otherwise generate new one
67
- if (existingSessionId) {
68
- this.sessionId = existingSessionId;
69
- } else if (!this.sessionId) {
70
- this.sessionId = randomBytes(32).toString("hex");
71
- }
72
-
73
- // Create Client instance directly (Frame pattern) - no localStorage!
74
- // This ensures we always get fresh activeWallets from device
75
- if (!this.client) {
76
- this.client = new Client({
77
- name: "ShapeShift",
78
- baseUrl: "https://signing.gridpl.us",
79
- privKey: Buffer.from(this.sessionId, "hex"),
80
- retryCount: 3,
81
- timeout: 60000,
82
- skipRetryOnWrongWallet: true,
83
- });
84
-
85
- try {
86
- // Connect to device - returns true if paired, false if needs pairing
87
- const isPaired = await this.client.connect(deviceId);
88
- this.connected = true;
89
- return { isPaired, sessionId: this.sessionId };
90
- } catch (error) {
91
- // Handle "Device Locked" error - treat as unpaired
92
- const errorMessage = error instanceof Error ? error.message : String(error);
93
- if (errorMessage.toLowerCase().includes("device locked")) {
94
- this.connected = true;
95
- return { isPaired: false, sessionId: this.sessionId };
96
- }
97
-
98
- throw error;
99
- }
100
- } else {
101
- // Client already exists, reset active wallets to clear stale state before reconnecting
102
- // This is critical when switching between SafeCards - ensures fresh wallet state from device
103
- this.client.resetActiveWallets();
104
- const isPaired = await this.client.connect(deviceId);
105
- this.connected = true;
106
- return { isPaired, sessionId: this.sessionId };
107
- }
108
- }
109
-
110
- public async pair(pairingCode: string): Promise<boolean> {
111
- if (!this.client) {
112
- throw new Error("Client not initialized. Call setup() first.");
113
- }
114
-
115
- const result = await this.client.pair(pairingCode);
116
- this.connected = !!result;
117
- return !!result;
118
- }
119
-
120
- public getClient(): Client | undefined {
121
- return this.client;
122
- }
123
-
124
- public getSessionId(): string | undefined {
125
- return this.sessionId;
126
- }
127
-
128
- public async call(): Promise<any> {
129
- throw new Error("GridPlus transport call not implemented");
130
- }
131
- }