Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial version of E2E Test Cases and setSeed address loading issue #50

Merged
merged 20 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/e2e_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Run E2E Tests

on: [pull_request]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 18

- name: Run E2E Tests
env:
NAME: ${{ secrets.NAME }}
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
WALLET_DATA: ${{ secrets.WALLET_DATA }}
run: npm run test:dry-run && npm run test:e2e
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ To start, [create a CDP API Key](https://portal.cdp.coinbase.com/access/api). Th
```typescript
const apiKeyName = "Copy your API Key name here.";

const privatekey = "Copy your API Key's private key here.";
const privateKey = "Copy your API Key's private key here.";

const coinbase = new Coinbase({ apiKeyName: apiKeyName, privateKey: privateKey });
```
Expand Down Expand Up @@ -165,16 +165,16 @@ In order to persist the data for the Wallet, you will need to implement a store
await store(data);
```

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.
For convenience during testing, we provide a `saveSeed` 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saveSeed method that stores the wallet's seed in your local file system


```typescript
user.saveWallet(wallet);
user.saveSeed(wallet);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wallet.saveSeed

```

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`.
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 `saveSeed` and `loadWallets`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saveSeed and loadSeed.


```typescript
user.saveWallet(wallet, true);
user.saveSeed(wallet, true);
```

The below code demonstrates how to re-instantiate a Wallet from the data export.
Expand Down Expand Up @@ -239,6 +239,11 @@ To run a specific test, run (for example):
```bash
npx jest ./src/coinbase/tests/wallet_test.ts
```
To run e2e tests, run:

```bash
npm run test:dry-run && NAME="placeholder" PRIVATE_KEY="placeholder" WALLET_DATA="placeholder" && npm run test:e2e
```

### Generating Documentation

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
"format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
"format-check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
"check": "tsc --noEmit",
"test": "npx jest --no-cache",
"test": "npx jest --no-cache --testMatch=**/*_test.ts",
"test:dry-run": "npm install && npm ci && npm publish --dry-run",
"test:e2e": "npx jest --no-cache --testMatch=**/e2e.ts",
"clean": "rm -rf dist/*",
"build": "tsc",
"prepack": "tsc",
Expand All @@ -28,6 +30,7 @@
"bip32": "^4.0.0",
"bip39": "^3.1.0",
"decimal.js": "^10.4.3",
"dotenv": "^16.4.5",
"ethers": "^6.12.1",
"node-jose": "^2.2.0",
"secp256k1": "^5.0.0"
Expand Down Expand Up @@ -69,7 +72,6 @@
"text"
],
"verbose": true,
"testRegex": ".test.ts$",
"maxWorkers": 1
}
}
112 changes: 112 additions & 0 deletions src/coinbase/tests/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import fs from "fs";
import dotenv from "dotenv";
import { Coinbase } from "../coinbase";
import { TransferStatus } from "../types";

describe("Coinbase SDK E2E Test", () => {
let coinbase: Coinbase;
beforeAll(() => {
dotenv.config();
});

beforeEach(() => {
coinbase = new Coinbase({
apiKeyName: process.env.NAME,
privateKey: process.env.PRIVATE_KEY,
});
});

it("should be able to access environment variables", () => {
expect(process.env.NAME).toBeDefined();
expect(process.env.PRIVATE_KEY).toBeDefined();
});

it("should have created a dist folder for NPM", () => {
expect(fs.existsSync("./dist")).toBe(true);
expect(fs.existsSync("./dist/index.js")).toBe(true);
expect(fs.existsSync("./dist/client/index.js")).toBe(true);
expect(fs.existsSync("./dist/coinbase/coinbase.js")).toBe(true);
});

it("should be able to interact with the Coinbase SDK", async () => {
console.log("Fetching default user...");
const user = await coinbase.getDefaultUser();
expect(user.getId()).toBeDefined();
console.log(`Fetched default user with ID: ${user.getId()}`);

console.log("Creating new wallet...");
const wallet = await user.createWallet();
expect(wallet?.getId()).toBeDefined();
console.log(
`Created new wallet with ID: ${wallet.getId()}, default address: ${wallet.getDefaultAddress()}`,
);

console.log("Importing wallet with balance...");
const seedFile = JSON.parse(process.env.WALLET_DATA || "");
const walletId = Object.keys(seedFile)[0];
const seed = seedFile[walletId].seed;

const userWallet = await user.importWallet({ seed, walletId });
expect(userWallet).toBeDefined();
expect(userWallet.getId()).toBe(walletId);
console.log(
`Imported wallet with ID: ${userWallet.getId()}, default address: ${userWallet.getDefaultAddress()}`,
);
await userWallet.saveSeed("test_seed.json");

try {
await userWallet.faucet();
} catch {
console.log("Faucet request failed. Skipping...");
}
console.log("Listing wallet addresses...");
const addresses = userWallet.listAddresses();
expect(addresses.length).toBeGreaterThan(0);
console.log(`Listed addresses: ${userWallet.listAddresses().join(", ")}`);

console.log("Fetching wallet balances...");
const balances = await userWallet.listBalances();
expect(Array.from([...balances.keys()]).length).toBeGreaterThan(0);
console.log(`Fetched balances: ${balances.toString()}`);

console.log("Exporting wallet...");
const exportedWallet = await wallet.export();
expect(exportedWallet.walletId).toBeDefined();
expect(exportedWallet.seed).toBeDefined();

console.log("Saving seed to file...");
await wallet.saveSeed("test_seed.json");
expect(fs.existsSync("test_seed.json")).toBe(true);
console.log("Saved seed to test_seed.json");

const unhydratedWallet = await user.getWallet(walletId);
expect(unhydratedWallet.canSign()).toBe(false);
await unhydratedWallet.loadSeed("test_seed.json");
expect(unhydratedWallet.canSign()).toBe(true);
expect(unhydratedWallet.getId()).toBe(walletId);

console.log("Transfering 1 Gwei from default address to second address...");
const transfer = await unhydratedWallet.createTransfer(1, Coinbase.assets.Gwei, wallet);
expect(await transfer.getStatus()).toBe(TransferStatus.COMPLETE);
console.log(`Transferred 1 Gwei from ${unhydratedWallet} to ${wallet}`);

console.log("Fetching updated balances...");
Copy link
Contributor

@jazz-cb jazz-cb Jun 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add one more transfer request that transfers remaining funds back to the CB faucet 0x6Cd01c0F55ce9E0Bf78f5E90f72b4345b16d515d ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synced on this but the faucet contract is updated. we will have a followup

const firstBalance = await unhydratedWallet.listBalances();
const secondBalance = await wallet.listBalances();
expect(firstBalance.get(Coinbase.assets.Eth)).not.toEqual("0");
expect(secondBalance.get(Coinbase.assets.Eth)).not.toEqual("0");
console.log(`First address balances: ${firstBalance}`);
console.log(`Second address balances: ${secondBalance}`);

const savedSeed = JSON.parse(fs.readFileSync("test_seed.json", "utf-8"));
fs.unlinkSync("test_seed.json");

expect(exportedWallet.seed.length).toBe(64);
expect(savedSeed[exportedWallet.walletId]).toEqual({
seed: exportedWallet.seed,
encrypted: false,
authTag: "",
iv: "",
});
}, 60000);
});
11 changes: 11 additions & 0 deletions src/coinbase/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,17 @@ export class Wallet {
public setSeed(seed: string) {
if (this.master === undefined && (this.seed === undefined || this.seed === "")) {
this.master = HDKey.fromMasterSeed(Buffer.from(seed, "hex"));
this.addresses = [];
this.addressModels.map((addressModel: AddressModel) => {
const derivedKey = this.deriveKey();
const etherKey = new ethers.Wallet(convertStringToHex(derivedKey.privateKey!));
if (etherKey.address != addressModel.address_id) {
throw new InternalError(
`Seed does not match wallet; cannot find address ${etherKey.address}`,
);
}
this.cacheAddress(addressModel, etherKey);
});
} else {
throw new InternalError("Cannot set seed on Wallet with existing seed");
}
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,11 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"

dotenv@^16.4.5:
version "16.4.5"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==

electron-to-chromium@^1.4.668:
version "1.4.773"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.773.tgz#49741af9bb4e712ad899e35d8344d8d59cdb7e12"
Expand Down
Loading