Skip to content

Commit

Permalink
Adding initial version of Coinbase Class
Browse files Browse the repository at this point in the history
  • Loading branch information
erdimaden committed May 13, 2024
1 parent 7ca5e2e commit 623997a
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,4 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.DS_Store
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@types/secp256k1": "^4.0.6",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"axios-mock-adapter": "^1.22.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
Expand Down
73 changes: 73 additions & 0 deletions src/coinbase/coinbase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import globalAxios from "axios";
import fs from "fs";
import { UsersApiFactory } from "../client";
import { BASE_PATH } from "./../client/base";
import { Configuration } from "./../client/configuration";
import { CoinbaseAuthenticator } from "./authenticator";
import { InvalidConfiguration } from "./errors";
import { ApiClients } from "./types";
import { User } from "./user";

// The Coinbase SDK.
export class Coinbase {
apiClients: ApiClients = {};

/**
* Initializes the Coinbase SDK.
* @constructor
* @param {string} apiKeyName - The API key name.
* @param {string} privateKey - The private key associated with the API key.
*/
constructor(apiKeyName: string, privateKey: string) {
if (apiKeyName === "" || privateKey === "") {
throw InvalidConfiguration;
}
const coinbaseAuthenticator = new CoinbaseAuthenticator(apiKeyName, privateKey);
const config = new Configuration({
basePath: BASE_PATH,
});
const axiosInstance = globalAxios.create();
axiosInstance.interceptors.request.use(config =>
coinbaseAuthenticator.authenticateRequest(config),
);
this.apiClients.user = UsersApiFactory(config, BASE_PATH, axiosInstance);
}

/**
* Reads the API key and private key from a JSON file and returns a new instance of Coinbase.
* @param {string} filePath - The path to the JSON file containing the API key and private key.
* @returns {Coinbase} A new instance of the Coinbase SDK.
*/
static fromJsonConfig(filePath: string = "coinbase_cloud_api_key.json"): Coinbase {
/* Read the JSON file for a given path and return a new instance of Coinbase check if the file exists */
if (!fs.existsSync(filePath)) {
// throw an error if the file does not exist
throw InvalidConfiguration;
}
// read the file and parse the JSON data
try {
// read the file and parse the JSON data
const data = fs.readFileSync(filePath, "utf8");

// parse the JSON data
const config = JSON.parse(data);

// return a new instance of Coinbase
if (!config.apiKeyName || !config.privateKey) {
throw InvalidConfiguration;
}
return new Coinbase(config.apiKeyName, config.privateKey);
} catch (e) {
throw InvalidConfiguration;
}
}

/**
* Returns the default user.
* @returns {User} The default user.
*/
async defaultUser(): Promise<User> {
const user = await this.apiClients.user?.getCurrentUser();
return new User(user?.data?.id || "", this.apiClients);
}
}
53 changes: 53 additions & 0 deletions src/coinbase/tests/coinbase_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Coinbase } from "../coinbase";
import MockAdapter from "axios-mock-adapter";
import axios from "axios";

const axiosMock = new MockAdapter(axios);

describe("Coinbase tests", () => {
const PATH_PREFIX = "./src/coinbase/tests/config";
it("should throw an error if the API key name or private key is empty", () => {
expect(() => new Coinbase("", "")).toThrow("Invalid configuration");
});

it("should throw an error if the file does not exist", () => {
expect(() => Coinbase.fromJsonConfig(`${PATH_PREFIX}/does-not-exist.json`)).toThrow(
"Invalid configuration",
);
});

it("should initialize the Coinbase SDK from a JSON file", () => {
const cbInstance = Coinbase.fromJsonConfig(`${PATH_PREFIX}/coinbase_cloud_api_key.json`);
expect(cbInstance).toBeInstanceOf(Coinbase);
});

it("should throw an error if there is an issue reading the file or parsing the JSON data", () => {
expect(() => Coinbase.fromJsonConfig(`${PATH_PREFIX}/invalid.json`)).toThrow(
"Invalid configuration",
);
});

it("should throw an error if the JSON file is not parseable", () => {
expect(() => Coinbase.fromJsonConfig(`${PATH_PREFIX}/not_parseable.json`)).toThrow(
"Invalid configuration",
);
});

it("should able to get the default user", async () => {
axiosMock.onGet().reply(200, {
id: 123,
});
const cbInstance = Coinbase.fromJsonConfig(`${PATH_PREFIX}/coinbase_cloud_api_key.json`);
const user = await cbInstance.defaultUser();
expect(user.getUserId()).toBe(123);
expect(user.toString()).toBe("Coinbase:User{userId: 123}");
});

it("should raise an error if the user is not found", async () => {
axiosMock.onGet().reply(404, {
id: 123,
});
const cbInstance = Coinbase.fromJsonConfig(`${PATH_PREFIX}/coinbase_cloud_api_key.json`);
expect(cbInstance.defaultUser()).rejects.toThrow("Request failed with status code 404");
});
});
4 changes: 4 additions & 0 deletions src/coinbase/tests/config/coinbase_cloud_api_key.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"apiKeyName": "organizations/0c3bbe72-ac81-46ec-946a-7cd019d6d86b/apiKeys/db813705-bf33-4e33-816c-4c3f1f54672b",
"privateKey": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIBPl8LBKrDw2Is+bxQEXa2eHhDmvIgArOhSAdmYpYQrCoAoGCCqGSM49\nAwEHoUQDQgAEQSoVSr8ImpS18thpGe3KuL9efy+L+AFdFFfCVwGgCsKvTYVDKaGo\nVmN5Bl6EJkeIQjyarEtWbmY6komwEOdnHA==\n-----END EC PRIVATE KEY-----\n"
}
4 changes: 4 additions & 0 deletions src/coinbase/tests/config/invalid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"apiKey": 0,
"apiSecret": ""
}
1 change: 1 addition & 0 deletions src/coinbase/tests/config/not_parseable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
not parseable content
8 changes: 8 additions & 0 deletions src/coinbase/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { AxiosPromise } from "axios";
import { User as UserModel } from "./../client/api";

export type UserAPIClient = { getCurrentUser(options?): AxiosPromise<UserModel> };

export type ApiClients = {
user?: UserAPIClient;
};
19 changes: 19 additions & 0 deletions src/coinbase/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ApiClients } from "./types";

export class User {
private userId: string = "";
private client: ApiClients;

constructor(userId: string, client: ApiClients) {
this.userId = userId;
this.client = client;
}

public getUserId(): string {
return this.userId;
}

toString(): string {
return `Coinbase:User{userId: ${this.userId}}`;
}
}
65 changes: 64 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,11 @@ ansi-regex@^5.0.1:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==

ansi-sequence-parser@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz#e0aa1cdcbc8f8bb0b5bca625aac41f5f056973cf"
integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==

ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
Expand Down Expand Up @@ -1019,6 +1024,14 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==

axios-mock-adapter@^1.22.0:
version "1.22.0"
resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz#0f3e6be0fc9b55baab06f2d49c0b71157e7c053d"
integrity sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==
dependencies:
fast-deep-equal "^3.1.3"
is-buffer "^2.0.5"

axios@^1.6.8:
version "1.6.8"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66"
Expand Down Expand Up @@ -1877,6 +1890,11 @@ is-arrayish@^0.2.1:
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==

is-buffer@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==

is-core-module@^2.13.0:
version "2.13.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
Expand Down Expand Up @@ -2387,6 +2405,11 @@ json5@^2.2.3:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==

jsonc-parser@^3.2.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a"
integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==

keyv@^4.5.3:
version "4.5.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
Expand Down Expand Up @@ -2458,6 +2481,11 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"

lunr@^2.3.9:
version "2.3.9"
resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==

make-dir@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
Expand All @@ -2477,6 +2505,11 @@ [email protected]:
dependencies:
tmpl "1.0.5"

marked@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3"
integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==

merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
Expand Down Expand Up @@ -2529,7 +2562,7 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"

minimatch@^9.0.4:
minimatch@^9.0.3, minimatch@^9.0.4:
version "9.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51"
integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==
Expand Down Expand Up @@ -2881,6 +2914,16 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==

shiki@^0.14.7:
version "0.14.7"
resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.7.tgz#c3c9e1853e9737845f1d2ef81b31bcfb07056d4e"
integrity sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==
dependencies:
ansi-sequence-parser "^1.1.0"
jsonc-parser "^3.2.0"
vscode-oniguruma "^1.7.0"
vscode-textmate "^8.0.0"

signal-exit@^3.0.3, signal-exit@^3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
Expand Down Expand Up @@ -3095,6 +3138,16 @@ type-fest@^0.21.3:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==

typedoc@^0.25.13:
version "0.25.13"
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.13.tgz#9a98819e3b2d155a6d78589b46fa4c03768f0922"
integrity sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==
dependencies:
lunr "^2.3.9"
marked "^4.3.0"
minimatch "^9.0.3"
shiki "^0.14.7"

typescript@^5.4.5:
version "5.4.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611"
Expand Down Expand Up @@ -3139,6 +3192,16 @@ v8-to-istanbul@^9.0.1:
"@types/istanbul-lib-coverage" "^2.0.1"
convert-source-map "^2.0.0"

vscode-oniguruma@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b"
integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==

vscode-textmate@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d"
integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==

walker@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
Expand Down

0 comments on commit 623997a

Please sign in to comment.