@shapeshiftoss/hdwallet-coinbase 1.49.1-alpha.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.
@@ -0,0 +1,383 @@
1
+ import * as core from "@shapeshiftoss/hdwallet-core";
2
+ import { AddEthereumChainParameter } from "@shapeshiftoss/hdwallet-core";
3
+ import { ethErrors, serializeError } from "eth-rpc-errors";
4
+
5
+ import * as eth from "./ethereum";
6
+
7
+ export class CoinbaseHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInfo {
8
+ readonly _supportsBTCInfo = false;
9
+ readonly _supportsETHInfo = true;
10
+ readonly _supportsCosmosInfo = false;
11
+ readonly _supportsBinanceInfo = false;
12
+ readonly _supportsRippleInfo = false;
13
+ readonly _supportsEosInfo = false;
14
+ readonly _supportsFioInfo = false;
15
+ readonly _supportsThorchainInfo = false;
16
+ readonly _supportsSecretInfo = false;
17
+ readonly _supportsKavaInfo = false;
18
+ readonly _supportsTerraInfo = false;
19
+
20
+ public getVendor(): string {
21
+ return "Coinbase";
22
+ }
23
+
24
+ public hasOnDevicePinEntry(): boolean {
25
+ return false;
26
+ }
27
+
28
+ public hasOnDevicePassphrase(): boolean {
29
+ return true;
30
+ }
31
+
32
+ public hasOnDeviceDisplay(): boolean {
33
+ return true;
34
+ }
35
+
36
+ public hasOnDeviceRecovery(): boolean {
37
+ return true;
38
+ }
39
+
40
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
41
+ public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean {
42
+ return false;
43
+ }
44
+
45
+ public supportsBip44Accounts(): boolean {
46
+ return false;
47
+ }
48
+
49
+ public supportsOfflineSigning(): boolean {
50
+ return false;
51
+ }
52
+
53
+ public supportsBroadcast(): boolean {
54
+ return true;
55
+ }
56
+
57
+ public describePath(msg: core.DescribePath): core.PathDescription {
58
+ switch (msg.coin) {
59
+ case "Ethereum":
60
+ // return eth.describeETHPath(msg.path);
61
+ return core.describeETHPath(msg.path);
62
+ default:
63
+ throw new Error("Unsupported path");
64
+ }
65
+ }
66
+
67
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
68
+ public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined {
69
+ // TODO: What do we do here?
70
+ return undefined;
71
+ }
72
+
73
+ public async ethSupportsNetwork(chainId: number): Promise<boolean> {
74
+ return chainId === 1;
75
+ }
76
+
77
+ public async ethSupportsSecureTransfer(): Promise<boolean> {
78
+ return false;
79
+ }
80
+
81
+ public ethSupportsNativeShapeShift(): boolean {
82
+ return false;
83
+ }
84
+
85
+ public async ethSupportsEIP1559(): Promise<boolean> {
86
+ return true;
87
+ }
88
+
89
+ public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array<core.ETHAccountPath> {
90
+ return eth.ethGetAccountPaths(msg);
91
+ }
92
+ }
93
+
94
+ export class CoinbaseHDWallet implements core.HDWallet, core.ETHWallet {
95
+ readonly _supportsETH = true;
96
+ readonly _supportsETHInfo = true;
97
+ readonly _supportsBTCInfo = false;
98
+ readonly _supportsBTC = false;
99
+ readonly _supportsCosmosInfo = false;
100
+ readonly _supportsCosmos = false;
101
+ readonly _supportsEthSwitchChain = true; // maybe?
102
+ readonly _supportsAvalanche = false;
103
+ readonly _supportsOptimism = false;
104
+ readonly _supportsBSC = false;
105
+ readonly _supportsPolygon = true;
106
+ readonly _supportsOsmosisInfo = false;
107
+ readonly _supportsOsmosis = false;
108
+ readonly _supportsBinanceInfo = false;
109
+ readonly _supportsBinance = false;
110
+ readonly _supportsDebugLink = false;
111
+ readonly _isPortis = false;
112
+ readonly _isMetaMask = false;
113
+ readonly _isCoinbase = true;
114
+ readonly _supportsRippleInfo = false;
115
+ readonly _supportsRipple = false;
116
+ readonly _supportsEosInfo = false;
117
+ readonly _supportsEos = false;
118
+ readonly _supportsFioInfo = false;
119
+ readonly _supportsFio = false;
120
+ readonly _supportsThorchainInfo = false;
121
+ readonly _supportsThorchain = false;
122
+ readonly _supportsSecretInfo = false;
123
+ readonly _supportsSecret = false;
124
+ readonly _supportsKava = false;
125
+ readonly _supportsKavaInfo = false;
126
+ readonly _supportsTerra = false;
127
+ readonly _supportsTerraInfo = false;
128
+
129
+ info: CoinbaseHDWalletInfo & core.HDWalletInfo;
130
+ ethAddress?: string | null;
131
+ provider: any;
132
+
133
+ constructor(provider: unknown) {
134
+ this.info = new CoinbaseHDWalletInfo();
135
+ this.provider = provider;
136
+ }
137
+
138
+ async getFeatures(): Promise<Record<string, any>> {
139
+ return {};
140
+ }
141
+
142
+ public async isLocked(): Promise<boolean> {
143
+ return !this.provider._coinbase.isUnlocked();
144
+ }
145
+
146
+ public getVendor(): string {
147
+ return "Coinbase";
148
+ }
149
+
150
+ public async getModel(): Promise<string> {
151
+ return "Coinbase";
152
+ }
153
+
154
+ public async getLabel(): Promise<string> {
155
+ return "Coinbase";
156
+ }
157
+
158
+ public async initialize(): Promise<void> {
159
+ // nothing to initialize
160
+ }
161
+
162
+ public hasOnDevicePinEntry(): boolean {
163
+ return this.info.hasOnDevicePinEntry();
164
+ }
165
+
166
+ public hasOnDevicePassphrase(): boolean {
167
+ return this.info.hasOnDevicePassphrase();
168
+ }
169
+
170
+ public hasOnDeviceDisplay(): boolean {
171
+ return this.info.hasOnDeviceDisplay();
172
+ }
173
+
174
+ public hasOnDeviceRecovery(): boolean {
175
+ return this.info.hasOnDeviceRecovery();
176
+ }
177
+
178
+ public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean {
179
+ return this.info.hasNativeShapeShift(srcCoin, dstCoin);
180
+ }
181
+
182
+ public supportsBip44Accounts(): boolean {
183
+ return this.info.supportsBip44Accounts();
184
+ }
185
+
186
+ public supportsOfflineSigning(): boolean {
187
+ return false;
188
+ }
189
+
190
+ public supportsBroadcast(): boolean {
191
+ return true;
192
+ }
193
+
194
+ public async clearSession(): Promise<void> {
195
+ // TODO: Can we lock Coinbase from here?
196
+ }
197
+
198
+ public async ping(msg: core.Ping): Promise<core.Pong> {
199
+ // no ping function for Coinbase, so just returning Core.Pong
200
+ return { msg: msg.msg };
201
+ }
202
+
203
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
204
+ public async sendPin(pin: string): Promise<void> {
205
+ // no concept of pin in Coinbase
206
+ }
207
+
208
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
209
+ public async sendPassphrase(passphrase: string): Promise<void> {
210
+ // cannot send passphrase to Coinbase. Could show the widget?
211
+ }
212
+
213
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
214
+ public async sendCharacter(charater: string): Promise<void> {
215
+ // no concept of sendCharacter in Coinbase
216
+ }
217
+
218
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
219
+ public async sendWord(word: string): Promise<void> {
220
+ // no concept of sendWord in Coinbase
221
+ }
222
+
223
+ public async cancel(): Promise<void> {
224
+ // no concept of cancel in Coinbase
225
+ }
226
+
227
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
228
+ public async wipe(): Promise<void> {}
229
+
230
+ // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
231
+ public async reset(msg: core.ResetDevice): Promise<void> {}
232
+
233
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
234
+ public async recover(msg: core.RecoverDevice): Promise<void> {
235
+ // no concept of recover in Coinbase
236
+ }
237
+
238
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
239
+ public async loadDevice(msg: core.LoadDevice): Promise<void> {
240
+ // TODO: Does Coinbase allow this to be done programatically?
241
+ }
242
+
243
+ public describePath(msg: core.DescribePath): core.PathDescription {
244
+ return this.info.describePath(msg);
245
+ }
246
+
247
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
248
+ public async getPublicKeys(msg: Array<core.GetPublicKey>): Promise<Array<core.PublicKey | null>> {
249
+ // Ethereum public keys are not exposed by the RPC API
250
+ return [];
251
+ }
252
+
253
+ public async isInitialized(): Promise<boolean> {
254
+ return true;
255
+ }
256
+
257
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
258
+ public async disconnect(): Promise<void> {}
259
+
260
+ public async ethSupportsNetwork(chainId = 1): Promise<boolean> {
261
+ return chainId === 1;
262
+ }
263
+
264
+ public async ethGetChainId(): Promise<number | null> {
265
+ try {
266
+ // chainId as hex string
267
+ const chainId: string = await this.provider.request({ method: "eth_chainId" });
268
+ return parseInt(chainId, 16);
269
+ } catch (e) {
270
+ console.error(e);
271
+ return null;
272
+ }
273
+ }
274
+
275
+ public async ethAddChain(params: AddEthereumChainParameter): Promise<void> {
276
+ // at this point, we know that we're in the context of a valid Coinbase provider
277
+ await this.provider.request({ method: "wallet_addEthereumChain", params: [params] });
278
+ }
279
+
280
+ public async ethSwitchChain(params: AddEthereumChainParameter): Promise<void> {
281
+ try {
282
+ // at this point, we know that we're in the context of a valid Coinbase provider
283
+ await this.provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: params.chainId }] });
284
+ } catch (e: any) {
285
+ const error = serializeError(e);
286
+ // https://docs.metamask.io/guide/ethereum-provider.html#errors
287
+ // Internal error, which in the case of wallet_switchEthereumChain call means the chain isn't currently added to the wallet
288
+ if (error.code === -32603) {
289
+ // We only support Avalanche C-Chain currently. It is supported natively in XDEFI, and unsupported in Tally, both with no capabilities to add a new chain
290
+ // TODO(gomes): Find a better home for these. When that's done, we'll want to call ethSwitchChain with (params: AddEthereumChainParameter) instead
291
+ try {
292
+ await this.ethAddChain(params);
293
+ return;
294
+ } catch (addChainE: any) {
295
+ const addChainError = serializeError(addChainE);
296
+
297
+ if (addChainError.code === 4001) {
298
+ throw ethErrors.provider.userRejectedRequest();
299
+ }
300
+
301
+ throw (addChainError.data as any).originalError as {
302
+ code: number;
303
+ message: string;
304
+ stack: string;
305
+ };
306
+ }
307
+ }
308
+
309
+ if (error.code === 4001) {
310
+ throw ethErrors.provider.userRejectedRequest();
311
+ }
312
+
313
+ throw (error.data as any).originalError as {
314
+ code: number;
315
+ message: string;
316
+ stack: string;
317
+ };
318
+ }
319
+ }
320
+
321
+ public async ethSupportsSecureTransfer(): Promise<boolean> {
322
+ return false;
323
+ }
324
+
325
+ public ethSupportsNativeShapeShift(): boolean {
326
+ return false;
327
+ }
328
+
329
+ public async ethSupportsEIP1559(): Promise<boolean> {
330
+ return true;
331
+ }
332
+
333
+ public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array<core.ETHAccountPath> {
334
+ return eth.ethGetAccountPaths(msg);
335
+ }
336
+
337
+ public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined {
338
+ return this.info.ethNextAccountPath(msg);
339
+ }
340
+
341
+ // TODO: Respect msg.addressNList!
342
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
343
+ public async ethGetAddress(msg: core.ETHGetAddress): Promise<string | null> {
344
+ if (this.ethAddress) {
345
+ return this.ethAddress;
346
+ }
347
+ const address = await eth.ethGetAddress(this.provider);
348
+ if (address) {
349
+ this.ethAddress = address;
350
+ return address;
351
+ } else {
352
+ this.ethAddress = null;
353
+ return null;
354
+ }
355
+ }
356
+
357
+ public async ethSignTx(msg: core.ETHSignTx): Promise<core.ETHSignedTx | null> {
358
+ const address = await this.ethGetAddress(this.provider);
359
+ return address ? eth.ethSignTx(msg, this.provider, address) : null;
360
+ }
361
+
362
+ public async ethSendTx(msg: core.ETHSignTx): Promise<core.ETHTxHash | null> {
363
+ const address = await this.ethGetAddress(this.provider);
364
+ return address ? eth.ethSendTx(msg, this.provider, address) : null;
365
+ }
366
+
367
+ public async ethSignMessage(msg: core.ETHSignMessage): Promise<core.ETHSignedMessage | null> {
368
+ const address = await this.ethGetAddress(this.provider);
369
+ return address ? eth.ethSignMessage(msg, this.provider, address) : null;
370
+ }
371
+
372
+ public async ethVerifyMessage(msg: core.ETHVerifyMessage): Promise<boolean | null> {
373
+ return eth.ethVerifyMessage(msg, this.provider);
374
+ }
375
+
376
+ public async getDeviceID(): Promise<string> {
377
+ return "coinbase:" + (await this.ethGetAddress(this.provider));
378
+ }
379
+
380
+ public async getFirmwareVersion(): Promise<string> {
381
+ return "coinbase";
382
+ }
383
+ }
@@ -0,0 +1,105 @@
1
+ import * as core from "@shapeshiftoss/hdwallet-core";
2
+ import { ETHSignedMessage } from "@shapeshiftoss/hdwallet-core";
3
+ import * as ethers from "ethers";
4
+
5
+ export function describeETHPath(path: core.BIP32Path): core.PathDescription {
6
+ return core.describeETHPath(path);
7
+ }
8
+
9
+ export function ethGetAccountPaths(msg: core.ETHGetAccountPath): Array<core.ETHAccountPath> {
10
+ const slip44 = core.slip44ByCoin(msg.coin);
11
+ if (slip44 === undefined) return [];
12
+ return [
13
+ {
14
+ addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0],
15
+ hardenedPath: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx],
16
+ relPath: [0, 0],
17
+ description: "Coinbase",
18
+ },
19
+ ];
20
+ }
21
+
22
+ export async function ethGetAddress(ethereum: any): Promise<string | null> {
23
+ if (!(ethereum && ethereum.request)) {
24
+ return null;
25
+ }
26
+ try {
27
+ const ethAccounts = await ethereum.request({
28
+ method: "eth_accounts",
29
+ });
30
+ return ethAccounts[0];
31
+ } catch (error) {
32
+ console.error(error);
33
+ return null;
34
+ }
35
+ }
36
+
37
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
38
+ export async function ethSignTx(msg: core.ETHSignTx, ethereum: any, from: string): Promise<core.ETHSignedTx | null> {
39
+ console.error("Method ethSignTx unsupported for Coinbase wallet");
40
+ return null;
41
+ }
42
+
43
+ export async function ethSendTx(msg: core.ETHSignTx, ethereum: any, from: string): Promise<core.ETHTxHash | null> {
44
+ try {
45
+ const utxBase = {
46
+ from: from,
47
+ to: msg.to,
48
+ value: msg.value,
49
+ data: msg.data,
50
+ chainId: msg.chainId,
51
+ nonce: msg.nonce,
52
+ gas: msg.gasLimit,
53
+ };
54
+
55
+ const utx = msg.maxFeePerGas
56
+ ? {
57
+ ...utxBase,
58
+ maxFeePerGas: msg.maxFeePerGas,
59
+ maxPriorityFeePerGas: msg.maxPriorityFeePerGas,
60
+ }
61
+ : { ...utxBase, gasPrice: msg.gasPrice };
62
+
63
+ const signedTx = await ethereum.request({
64
+ method: "eth_sendTransaction",
65
+ params: [utx],
66
+ });
67
+
68
+ return {
69
+ hash: signedTx,
70
+ } as core.ETHTxHash;
71
+ } catch (error) {
72
+ console.error(error);
73
+ return null;
74
+ }
75
+ }
76
+
77
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
78
+ export async function ethVerifyMessage(msg: core.ETHVerifyMessage, ethereum: any): Promise<boolean | null> {
79
+ console.error("Method ethVerifyMessage unsupported for Coinbase wallet!");
80
+ return null;
81
+ }
82
+
83
+ export async function ethSignMessage(
84
+ msg: core.ETHSignMessage,
85
+ ethereum: any,
86
+ address: string
87
+ ): Promise<core.ETHSignedMessage | null> {
88
+ try {
89
+ const buffer = ethers.utils.isBytes(msg.message)
90
+ ? Buffer.from(ethers.utils.arrayify(msg.message))
91
+ : Buffer.from(msg.message);
92
+ const signedMsg = await ethereum.request({
93
+ method: "personal_sign",
94
+ params: [buffer.toString("hex"), address],
95
+ });
96
+
97
+ return {
98
+ address: address,
99
+ signature: signedMsg,
100
+ } as ETHSignedMessage;
101
+ } catch (error) {
102
+ console.error(error);
103
+ return null;
104
+ }
105
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./adapter";
2
+ export * from "./coinbase";
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist"],
9
+ "references": [{ "path": "../hdwallet-core" }]
10
+ }