Skip to content

Commit c0699ba

Browse files
[PSDK-111] Wallet Import/Export/Save/Load
1 parent 5ab03a9 commit c0699ba

File tree

8 files changed

+610
-21
lines changed

8 files changed

+610
-21
lines changed

README.md

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# Coinbase Node.js SDK
22

3-
The Coinbase Node.js SDK enables the simple integration of crypto into your app.
4-
By calling Coinbase's Platform APIs, the SDK allows you to provision crypto wallets,
5-
send crypto into/out of those wallets, track wallet balances, and trade crypto from
6-
one asset into another.
3+
The Coinbase Node.js SDK enables the simple integration of crypto into your app. By calling Coinbase's Platform APIs, the SDK allows you to provision crypto wallets, send crypto into/out of those wallets, track wallet balances, and trade crypto from one asset into another.
74

85
The SDK currently supports Customer-custodied Wallets on the Base Sepolia test network.
96

@@ -12,8 +9,7 @@ The SDK currently supports Customer-custodied Wallets on the Base Sepolia test n
129
- **may make backwards-incompatible changes between releases**
1310
- **should not be used on Mainnet (i.e. with real funds)**
1411

15-
Currently, the SDK is intended for use on testnet for quick bootstrapping of crypto wallets at
16-
hackathons, code academies, and other development settings.
12+
Currently, the SDK is intended for use on testnet for quick bootstrapping of crypto wallets at hackathons, code academies, and other development settings.
1713

1814
## Documentation
1915

@@ -38,8 +34,9 @@ yarn install @coinbase/coinbase-sdk
3834
After running `npx ts-node` to start the REPL, you can import the SDK as follows:
3935

4036
```typescript
41-
import { Coinbase } from '@coinbase/coinbase-sdk';
37+
import { Coinbase } from "@coinbase/coinbase-sdk";
4238
```
39+
4340
### Requirements
4441

4542
- Node.js 18 or higher
@@ -51,18 +48,17 @@ import { Coinbase } from '@coinbase/coinbase-sdk';
5148
To start, [create a CDP API Key](https://portal.cdp.coinbase.com/access/api). Then, initialize the Platform SDK by passing your API Key name and API Key's private key via the `Coinbase` constructor:
5249

5350
```typescript
54-
const apiKeyName = 'Copy your API Key name here.';
51+
const apiKeyName = "Copy your API Key name here.";
5552

56-
const apiKeyPrivateKey = 'Copy your API Key\'s private key here.';
53+
const apiKeyPrivateKey = "Copy your API Key's private key here.";
5754

5855
const coinbase = new Coinbase(apiKeyName, apiKeyPrivateKey);
5956
```
6057

61-
Another way to initialize the SDK is by sourcing the API key from the json file that contains your API key,
62-
downloaded from CDP portal.
58+
Another way to initialize the SDK is by sourcing the API key from the json file that contains your API key, downloaded from CDP portal.
6359

6460
```typescript
65-
const coinbase = Coinbase.configureFromJson('path/to/your/api-key.json');
61+
const coinbase = Coinbase.configureFromJson("path/to/your/api-key.json");
6662
```
6763

6864
This will allow you to authenticate with the Platform APIs and get access to the default `User`.
@@ -105,6 +101,50 @@ const anotherWallet = await user.createWallet();
105101
const transfer = await wallet.createTransfer(0.00001, Coinbase.assetList.Eth, anotherWallet);
106102
```
107103

104+
### Re-Instantiating Wallets
105+
106+
The SDK creates Wallets with developer managed keys, which means you are responsible for securely storing the keys required to re-instantiate Wallets. The below code walks you through how to export a Wallets and store it in a secure location.
107+
108+
```typescript
109+
// Export the data required to re-instantiate the Wallet.
110+
const data = wallet.export();
111+
```
112+
113+
In order to persist the data for the Wallet, you will need to implement a store method to store the data export in a secure location. If you do not store the Wallet in a secure location you will lose access to the Wallet and all of the funds on it.
114+
115+
```typescript
116+
// At this point, you should implement your own "store" method to securely persist
117+
// the data required to re-instantiate the Wallet at a later time.
118+
await store(data);
119+
```
120+
121+
For convenience during testing, we provide a `saveWallet` method that stores the Wallet data in your local file system. This is an insecure method of storing wallet seeds and should only be used for development purposes.
122+
123+
```typescript
124+
user.saveWallet(wallet);
125+
```
126+
127+
To encrypt the saved data, set encrypt to true. Note that your CDP API key also serves as the encryption key for the data persisted locally. To re-instantiate wallets with encrypted data, ensure that your SDK is configured with the same API key when invoking `saveWallet` and `loadWallets`.
128+
129+
```typescript
130+
user.saveWallet(wallet, true);
131+
```
132+
133+
The below code demonstrates how to re-instantiate a Wallet from the data export.
134+
135+
```typescript
136+
// The Wallet can be re-instantiated using the exported data.
137+
const importedWallet = await user.import(data);
138+
```
139+
140+
To import Wallets that were persisted to your local file system using `saveWallet`, use the below code.
141+
142+
```typescript
143+
// The Wallet can be re-instantiated using the exported data.
144+
const wallets = await user.loadWallets();
145+
const reinitWallet = wallets[wallet.getId()];
146+
```
147+
108148
## Development
109149

110150
### Node.js Version
@@ -155,8 +195,7 @@ npx jest ./src/coinbase/tests/wallet_test.ts
155195

156196
### REPL
157197

158-
The repository is equipped with a REPL to allow developers to play with the SDK. To start
159-
it, run:
198+
The repository is equipped with a REPL to allow developers to play with the SDK. To start it, run:
160199

161200
```bash
162201
npx ts-node

src/coinbase/coinbase.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import globalAxios from "axios";
2-
import fs from "fs";
2+
import * as fs from "fs";
33
import {
44
AddressesApiFactory,
55
User as UserModel,
@@ -51,6 +51,20 @@ export class Coinbase {
5151
*/
5252
static readonly WEI_PER_ETHER: bigint = BigInt("1000000000000000000");
5353

54+
/**
55+
* The backup file path for Wallet seeds.
56+
*
57+
* @constant
58+
*/
59+
static backupFilePath: string = "seed.json";
60+
61+
/**
62+
* The CDP API key Private Key.
63+
*
64+
* @constant
65+
*/
66+
static apiKeyPrivateKey: string;
67+
5468
/**
5569
* Initializes the Coinbase SDK.
5670
*
@@ -59,6 +73,7 @@ export class Coinbase {
5973
* @param privateKey - The private key associated with the API key.
6074
* @param debugging - If true, logs API requests and responses to the console.
6175
* @param basePath - The base path for the API.
76+
* @param backupFilePath - The path to the file containing the Wallet backup data.
6277
* @throws {InternalError} If the configuration is invalid.
6378
* @throws {InvalidAPIKeyFormat} If not able to create JWT token.
6479
*/
@@ -67,6 +82,7 @@ export class Coinbase {
6782
privateKey: string,
6883
debugging = false,
6984
basePath: string = BASE_PATH,
85+
backupFilePath?: string,
7086
) {
7187
if (apiKeyName === "") {
7288
throw new InternalError("Invalid configuration: apiKeyName is empty");
@@ -92,6 +108,7 @@ export class Coinbase {
92108
Coinbase.apiClients.baseSepoliaProvider = new ethers.JsonRpcProvider(
93109
"https://sepolia.base.org",
94110
);
111+
Coinbase.apiKeyPrivateKey = backupFilePath ? backupFilePath : privateKey;
95112
}
96113

97114
/**
@@ -100,6 +117,7 @@ export class Coinbase {
100117
* @param filePath - The path to the JSON file containing the API key and private key.
101118
* @param debugging - If true, logs API requests and responses to the console.
102119
* @param basePath - The base path for the API.
120+
* @param backupFilePath - The path to the file containing the Wallet backup data.
103121
* @returns A new instance of the Coinbase SDK.
104122
* @throws {InvalidAPIKeyFormat} If the file does not exist or the configuration values are missing/invalid.
105123
* @throws {InvalidConfiguration} If the configuration is invalid.
@@ -109,6 +127,7 @@ export class Coinbase {
109127
filePath: string = "coinbase_cloud_api_key.json",
110128
debugging: boolean = false,
111129
basePath: string = BASE_PATH,
130+
backupFilePath?: string,
112131
): Coinbase {
113132
if (!fs.existsSync(filePath)) {
114133
throw new InvalidConfiguration(`Invalid configuration: file not found at ${filePath}`);
@@ -120,7 +139,7 @@ export class Coinbase {
120139
throw new InvalidAPIKeyFormat("Invalid configuration: missing configuration values");
121140
}
122141

123-
return new Coinbase(config.name, config.privateKey, debugging, basePath);
142+
return new Coinbase(config.name, config.privateKey, debugging, basePath, backupFilePath);
124143
} catch (e) {
125144
if (e instanceof SyntaxError) {
126145
throw new InvalidAPIKeyFormat("Not able to parse the configuration file");

0 commit comments

Comments
 (0)