From 849e2418af927b491ee6cf44653563166522a127 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Tue, 28 May 2024 10:22:48 -0500 Subject: [PATCH 01/16] Version changed to 0.0.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 77203c30..f23b519d 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "author": "Coinbase Inc.", "license": "ISC", "description": "Coinbase Platform SDK", - "version": "0.0.1", + "version": "0.0.5", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { From 4e283f4854a97017c96fa3e69706ac8945315b72 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Tue, 28 May 2024 12:58:28 -0500 Subject: [PATCH 02/16] Fix: changing wallet seed length (#35) --- src/coinbase/wallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coinbase/wallet.ts b/src/coinbase/wallet.ts index 6804e903..2499580a 100644 --- a/src/coinbase/wallet.ts +++ b/src/coinbase/wallet.ts @@ -445,7 +445,7 @@ export class Wallet { switch (seed) { case undefined: { const mnemonic = bip39.generateMnemonic(); - const seedBuffer = bip39.mnemonicToSeedSync(mnemonic); + const seedBuffer = bip39.mnemonicToSeedSync(mnemonic).subarray(0, 32); return { seed: seedBuffer.toString("hex"), master: HDKey.fromMasterSeed(seedBuffer), From da0ac497c1ec51a0e2a60bcab69dc5c224e05f30 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Tue, 28 May 2024 13:31:11 -0500 Subject: [PATCH 03/16] Updating NPM publish workflow (#36) --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a3ca0dd6..4d457805 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,6 +11,7 @@ jobs: with: node-version: 18 registry-url: "https://registry.npmjs.org" + - run: npm install - run: npm ci - run: npm publish --provenance --access public env: From 792d57f0de9a8c1c8c761c6d83ed3572bed3e75a Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Tue, 28 May 2024 13:43:38 -0500 Subject: [PATCH 04/16] Updating NPM release workflow permission (#37) --- .github/workflows/publish.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4d457805..e09110d5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,6 +5,9 @@ on: jobs: build: runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v3 From bd5ee2ce0cc3bc555ad3d295f8b80588b6a27986 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Tue, 28 May 2024 14:14:54 -0500 Subject: [PATCH 05/16] Updating Repo URL in package.json (#38) --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f23b519d..8508637c 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "author": "Coinbase Inc.", "license": "ISC", "description": "Coinbase Platform SDK", + "repository": "https://github.com/coinbase/coinbase-sdk-nodejs", "version": "0.0.5", "main": "dist/index.js", "types": "dist/index.d.ts", From 0a8a897d72413e2dcd229641dfc110b144386a10 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Tue, 28 May 2024 19:43:38 -0500 Subject: [PATCH 06/16] Adding index for dist folder (#39) (#40) --- src/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/index.ts diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..6aec71de --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export * from "./coinbase/coinbase"; From 8d4a0a148bf724b47d35aa9aa0151517de0af7c0 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Tue, 28 May 2024 19:59:40 -0500 Subject: [PATCH 07/16] Downgrading package version (#41) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8508637c..cc916826 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "ISC", "description": "Coinbase Platform SDK", "repository": "https://github.com/coinbase/coinbase-sdk-nodejs", - "version": "0.0.5", + "version": "0.0.4", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { From c0eebd26ea1296a5a64abcda4591f1ec7eaf554e Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Mon, 3 Jun 2024 13:35:39 -0500 Subject: [PATCH 08/16] Adding initial version of e2e test cases --- .github/workflows/test.yml | 4 ++ package.json | 5 +- src/coinbase/tests/e2e.ts | 101 +++++++++++++++++++++++++++++++++++++ yarn.lock | 5 ++ 4 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 src/coinbase/tests/e2e.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fcf1ffa2..ee9cf9b2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,3 +19,7 @@ jobs: - name: Run Unit Tests run: npm run test + + - name: Run E2E Tests + run: npm run test:e2e + diff --git a/package.json b/package.json index c818a4bb..f24ba2f9 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "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:e2e": "npx jest --no-cache --testMatch=**/e2e.ts", "clean": "rm -rf dist/*", "build": "tsc", "prepack": "tsc", @@ -28,6 +29,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" @@ -69,7 +71,6 @@ "text" ], "verbose": true, - "testRegex": ".test.ts$", "maxWorkers": 1 } } diff --git a/src/coinbase/tests/e2e.ts b/src/coinbase/tests/e2e.ts new file mode 100644 index 00000000..a6c4505b --- /dev/null +++ b/src/coinbase/tests/e2e.ts @@ -0,0 +1,101 @@ +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.PRIVATEKEY, + }); + }); + it("should be able to access environment variables", () => { + expect(process.env.NAME).toBeDefined(); + expect(process.env.PRIVATEKEY).toBeDefined(); + }); + 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.SEED || ""); + 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()}`, + ); + + console.log("Listing wallet addresses..."); + const addresses = userWallet.listAddresses(); + expect(addresses.length).toBeGreaterThan(0); + // puts "Listed addresses: #{addresses.map(&:to_s).join(', ')}" + 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("Transfering 1 Gwei from default address to second address..."); + // const [a1, a2] = addresses; + // const transfer = await a1.createTransfer(1, Coinbase.assets.Gwei, a2); + // expect(transfer.getStatus()).toBe(TransferStatus.COMPLETE); + // // puts "Transferred 1 Gwei from #{a1} to #{a2}" + // console.log(`Transferred 1 Gwei from ${a1} to ${a2}`); + + // console.log("Fetching updated balances..."); + // const firstBalance = await a1.listBalances(); + // const secondBalance = await a2.listBalances(); + // expect(firstBalance.get(Coinbase.assets.Eth)).toBeGreaterThan(0); + // expect(secondBalance.get(Coinbase.assets.Eth)).toBeGreaterThan(0); + // console.log(`First address balances: ${firstBalance}`); + // console.log(`Second address balances: ${secondBalance}`); + + 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(wallet.getId()!); + expect(unhydratedWallet.canSign()).toBe(false); + await unhydratedWallet.loadSeed("test_seed.json"); + expect(unhydratedWallet.canSign()).toBe(true); + expect(unhydratedWallet.getId()).toBe(wallet.getId()); + + const savedSeed = JSON.parse(fs.readFileSync("test_seed.json", "utf-8")); + fs.unlinkSync("test_seed.json"); + + expect(exportedWallet.seed.length).toBe(64); + expect(savedSeed).toEqual({ + [wallet.getId()!]: { + seed: exportedWallet.seed, + encrypted: false, + authTag: "", + iv: "", + }, + }); + }); +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 000b7cfa..395a7e5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" From f1db6b85c103c5caa351f7e9dac7e84ffaa71dc4 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Mon, 3 Jun 2024 13:35:39 -0500 Subject: [PATCH 09/16] Adding initial version of e2e test cases --- .github/workflows/test.yml | 8 +++ package.json | 5 +- src/coinbase/tests/e2e.ts | 106 +++++++++++++++++++++++++++++++++++++ yarn.lock | 5 ++ 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 src/coinbase/tests/e2e.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fcf1ffa2..2faf757c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,3 +19,11 @@ jobs: - name: Run Unit Tests run: npm run test + + - name: Run E2E Tests + env: + NAME: ${{ secrets.NAME }} + PRIVATEKEY: ${{ secrets.PRIVATEKEY }} + SEED: ${{ secrets.SEED }} + run: npm run test:e2e + diff --git a/package.json b/package.json index c818a4bb..f24ba2f9 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "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:e2e": "npx jest --no-cache --testMatch=**/e2e.ts", "clean": "rm -rf dist/*", "build": "tsc", "prepack": "tsc", @@ -28,6 +29,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" @@ -69,7 +71,6 @@ "text" ], "verbose": true, - "testRegex": ".test.ts$", "maxWorkers": 1 } } diff --git a/src/coinbase/tests/e2e.ts b/src/coinbase/tests/e2e.ts new file mode 100644 index 00000000..08828db8 --- /dev/null +++ b/src/coinbase/tests/e2e.ts @@ -0,0 +1,106 @@ +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.PRIVATEKEY, + }); + }); + it("should be able to access environment variables", () => { + expect(process.env.NAME).toBeDefined(); + expect(process.env.PRIVATEKEY).toBeDefined(); + }); + 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.SEED || ""); + 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()}`, + ); + + 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); + // puts "Listed addresses: #{addresses.map(&:to_s).join(', ')}" + 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("Transfering 1 Gwei from default address to second address..."); + const [firstAddress] = addresses; + const transfer = await firstAddress.createTransfer(1, Coinbase.assets.Gwei, wallet); + expect(await transfer.getStatus()).toBe(TransferStatus.COMPLETE); + // puts "Transferred 1 Gwei from #{a1} to #{a2}" + console.log(`Transferred 1 Gwei from ${firstAddress} to ${wallet}`); + + console.log("Fetching updated balances..."); + const firstBalance = await firstAddress.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}`); + + 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(wallet.getId()!); + expect(unhydratedWallet.canSign()).toBe(false); + await unhydratedWallet.loadSeed("test_seed.json"); + expect(unhydratedWallet.canSign()).toBe(true); + expect(unhydratedWallet.getId()).toBe(wallet.getId()); + + const savedSeed = JSON.parse(fs.readFileSync("test_seed.json", "utf-8")); + fs.unlinkSync("test_seed.json"); + + expect(exportedWallet.seed.length).toBe(64); + expect(savedSeed).toEqual({ + [wallet.getId()!]: { + seed: exportedWallet.seed, + encrypted: false, + authTag: "", + iv: "", + }, + }); + }, 60000); +}); diff --git a/yarn.lock b/yarn.lock index 000b7cfa..395a7e5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" From fe8254cc79279d446d7d66029198d2f54af83c8a Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Tue, 28 May 2024 14:14:54 -0500 Subject: [PATCH 10/16] Updating Repo URL in package.json (#38) --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index c818a4bb..f401fe1b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,11 @@ "license": "ISC", "description": "Coinbase Platform SDK", "repository": "https://github.com/coinbase/coinbase-sdk-nodejs", +<<<<<<< HEAD "version": "0.0.6", +======= + "version": "0.0.5", +>>>>>>> bd5ee2c (Updating Repo URL in package.json (#38)) "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { From 8fd23eb3d1f5260092bb40eaa4362b2c324e6b03 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Tue, 28 May 2024 19:59:40 -0500 Subject: [PATCH 11/16] Downgrading package version (#41) --- package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/package.json b/package.json index f401fe1b..c818a4bb 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,7 @@ "license": "ISC", "description": "Coinbase Platform SDK", "repository": "https://github.com/coinbase/coinbase-sdk-nodejs", -<<<<<<< HEAD "version": "0.0.6", -======= - "version": "0.0.5", ->>>>>>> bd5ee2c (Updating Repo URL in package.json (#38)) "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { From ceeea0d30e93988704288d28e925e5c87749a14d Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Mon, 3 Jun 2024 14:56:57 -0500 Subject: [PATCH 12/16] Updating E2E tests and fixing setSeed address loading issue --- src/coinbase/api_error.ts | 1 - src/coinbase/tests/e2e.ts | 46 ++++++++++++++++++--------------------- src/coinbase/wallet.ts | 11 ++++++++++ 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/coinbase/api_error.ts b/src/coinbase/api_error.ts index 66348642..7fc89e2a 100644 --- a/src/coinbase/api_error.ts +++ b/src/coinbase/api_error.ts @@ -45,7 +45,6 @@ export class APIError extends AxiosError { * @returns {APIError} A specific APIError instance. */ static fromError(error: AxiosError) { - console.log("error", error); const apiError = new APIError(error); if (!error.response || !error.response.data) { return apiError; diff --git a/src/coinbase/tests/e2e.ts b/src/coinbase/tests/e2e.ts index 08828db8..950529ab 100644 --- a/src/coinbase/tests/e2e.ts +++ b/src/coinbase/tests/e2e.ts @@ -42,6 +42,7 @@ describe("Coinbase SDK E2E Test", () => { console.log( `Imported wallet with ID: ${userWallet.getId()}, default address: ${userWallet.getDefaultAddress()}`, ); + await userWallet.saveSeed("test_seed.json"); try { await userWallet.faucet(); @@ -51,7 +52,6 @@ describe("Coinbase SDK E2E Test", () => { console.log("Listing wallet addresses..."); const addresses = userWallet.listAddresses(); expect(addresses.length).toBeGreaterThan(0); - // puts "Listed addresses: #{addresses.map(&:to_s).join(', ')}" console.log(`Listed addresses: ${userWallet.listAddresses().join(", ")}`); console.log("Fetching wallet balances..."); @@ -59,21 +59,6 @@ describe("Coinbase SDK E2E Test", () => { expect(Array.from([...balances.keys()]).length).toBeGreaterThan(0); console.log(`Fetched balances: ${balances.toString()}`); - console.log("Transfering 1 Gwei from default address to second address..."); - const [firstAddress] = addresses; - const transfer = await firstAddress.createTransfer(1, Coinbase.assets.Gwei, wallet); - expect(await transfer.getStatus()).toBe(TransferStatus.COMPLETE); - // puts "Transferred 1 Gwei from #{a1} to #{a2}" - console.log(`Transferred 1 Gwei from ${firstAddress} to ${wallet}`); - - console.log("Fetching updated balances..."); - const firstBalance = await firstAddress.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}`); - console.log("Exporting wallet..."); const exportedWallet = await wallet.export(); expect(exportedWallet.walletId).toBeDefined(); @@ -84,23 +69,34 @@ describe("Coinbase SDK E2E Test", () => { expect(fs.existsSync("test_seed.json")).toBe(true); console.log("Saved seed to test_seed.json"); - const unhydratedWallet = await user.getWallet(wallet.getId()!); + 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(wallet.getId()); + 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..."); + 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).toEqual({ - [wallet.getId()!]: { - seed: exportedWallet.seed, - encrypted: false, - authTag: "", - iv: "", - }, + expect(savedSeed[exportedWallet.walletId]).toEqual({ + seed: exportedWallet.seed, + encrypted: false, + authTag: "", + iv: "", }); }, 60000); }); diff --git a/src/coinbase/wallet.ts b/src/coinbase/wallet.ts index 197d9bda..c8bb3962 100644 --- a/src/coinbase/wallet.ts +++ b/src/coinbase/wallet.ts @@ -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"); } From 638faf402d961e92eb3ef2cffdee6326df79f4da Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Mon, 3 Jun 2024 15:14:35 -0500 Subject: [PATCH 13/16] Adding dry-run check --- .github/workflows/test.yml | 5 ++++- src/coinbase/tests/e2e.ts | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2faf757c..f94f6f6b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,5 +25,8 @@ jobs: NAME: ${{ secrets.NAME }} PRIVATEKEY: ${{ secrets.PRIVATEKEY }} SEED: ${{ secrets.SEED }} - run: npm run test:e2e + - run: npm install + - run: npm ci + - run: npm publish --dry-run + - run: npm run test:e2e diff --git a/src/coinbase/tests/e2e.ts b/src/coinbase/tests/e2e.ts index 950529ab..3d1b2924 100644 --- a/src/coinbase/tests/e2e.ts +++ b/src/coinbase/tests/e2e.ts @@ -8,16 +8,26 @@ describe("Coinbase SDK E2E Test", () => { beforeAll(() => { dotenv.config(); }); + beforeEach(() => { coinbase = new Coinbase({ apiKeyName: process.env.NAME, privateKey: process.env.PRIVATEKEY, }); }); + it("should be able to access environment variables", () => { expect(process.env.NAME).toBeDefined(); expect(process.env.PRIVATEKEY).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(); From ca82c89fd81abe55e36e25cfce196e15c5e95bbf Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Mon, 3 Jun 2024 15:59:22 -0500 Subject: [PATCH 14/16] Updating documentation and npm scripts --- .github/workflows/test.yml | 10 ++++------ README.md | 15 ++++++++++----- package.json | 1 + src/coinbase/tests/e2e.ts | 6 +++--- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f94f6f6b..a9c9d1e4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,10 +23,8 @@ jobs: - name: Run E2E Tests env: NAME: ${{ secrets.NAME }} - PRIVATEKEY: ${{ secrets.PRIVATEKEY }} - SEED: ${{ secrets.SEED }} - - run: npm install - - run: npm ci - - run: npm publish --dry-run - - run: npm run test:e2e + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + WALLET_DATA: ${{ secrets.WALLET_DATA }} + run: npm run test:dry-run && npm run test:e2e + diff --git a/README.md b/README.md index bbaea319..04ec83b1 100644 --- a/README.md +++ b/README.md @@ -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 }); ``` @@ -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. ```typescript -user.saveWallet(wallet); +user.saveSeed(wallet); ``` -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`. ```typescript -user.saveWallet(wallet, true); +user.saveSeed(wallet, true); ``` The below code demonstrates how to re-instantiate a Wallet from the data export. @@ -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 diff --git a/package.json b/package.json index f24ba2f9..68e87a2e 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "format-check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"", "check": "tsc --noEmit", "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", diff --git a/src/coinbase/tests/e2e.ts b/src/coinbase/tests/e2e.ts index 3d1b2924..ab748929 100644 --- a/src/coinbase/tests/e2e.ts +++ b/src/coinbase/tests/e2e.ts @@ -12,13 +12,13 @@ describe("Coinbase SDK E2E Test", () => { beforeEach(() => { coinbase = new Coinbase({ apiKeyName: process.env.NAME, - privateKey: process.env.PRIVATEKEY, + privateKey: process.env.PRIVATE_KEY, }); }); it("should be able to access environment variables", () => { expect(process.env.NAME).toBeDefined(); - expect(process.env.PRIVATEKEY).toBeDefined(); + expect(process.env.PRIVATE_KEY).toBeDefined(); }); it("should have created a dist folder for NPM", () => { @@ -42,7 +42,7 @@ describe("Coinbase SDK E2E Test", () => { ); console.log("Importing wallet with balance..."); - const seedFile = JSON.parse(process.env.SEED || ""); + const seedFile = JSON.parse(process.env.WALLET_DATA || ""); const walletId = Object.keys(seedFile)[0]; const seed = seedFile[walletId].seed; From 3ac1fedcd844cf683ba61f25d0068de4cc38f490 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Mon, 3 Jun 2024 16:45:35 -0500 Subject: [PATCH 15/16] Updating e2e workflow --- .github/workflows/e2e_test.yml | 22 ++++++++++++++++++++++ .github/workflows/test.yml | 9 --------- 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/e2e_test.yml diff --git a/.github/workflows/e2e_test.yml b/.github/workflows/e2e_test.yml new file mode 100644 index 00000000..1fd2affb --- /dev/null +++ b/.github/workflows/e2e_test.yml @@ -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 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a9c9d1e4..fcf1ffa2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,12 +19,3 @@ jobs: - name: Run Unit Tests run: npm run test - - - 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 - - From 2a738c6f798f0a2c0ad1529ed1bd09702b06f27c Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Mon, 3 Jun 2024 17:44:17 -0500 Subject: [PATCH 16/16] updated docs --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 04ec83b1..ea817715 100644 --- a/README.md +++ b/README.md @@ -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 `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. +For convenience during testing, we provide a `saveSeed` method that stores the wallet's seed in your local file system. This is an insecure method of storing wallet seeds and should only be used for development purposes. ```typescript -user.saveSeed(wallet); +wallet.saveSeed(wallet); ``` -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`. +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 `loadSeed`. ```typescript -user.saveSeed(wallet, true); +wallet.saveSeed(wallet, true); ``` The below code demonstrates how to re-instantiate a Wallet from the data export. @@ -184,12 +184,12 @@ The below code demonstrates how to re-instantiate a Wallet from the data export. const importedWallet = await user.importWallet(data); ``` -To import Wallets that were persisted to your local file system using `saveWallet`, use the below code. +To import Wallets that were persisted to your local file system using `saveSeed`, use the below code. ```typescript // The Wallet can be re-instantiated using the exported data. -const wallets = await user.loadWallets(); -const reinitWallet = wallets[wallet.getId()]; +const w = await user.getWallet(w.getId()); +w.loadSeed(filePath); ``` ## Development