diff --git a/src/coinbase/coinbase.ts b/src/coinbase/coinbase.ts index 1c4a53b8..bd22ba2f 100644 --- a/src/coinbase/coinbase.ts +++ b/src/coinbase/coinbase.ts @@ -7,12 +7,11 @@ import { AddressesApiFactory, WalletsApiFactory, } from "../client"; -import { ethers } from "ethers"; import { BASE_PATH } from "./../client/base"; import { Configuration } from "./../client/configuration"; import { CoinbaseAuthenticator } from "./authenticator"; import { InternalError, InvalidAPIKeyFormat, InvalidConfiguration } from "./errors"; -import { ApiClients } from "./types"; +import { ApiClients, CoinbaseConfigureFromJsonOptions, CoinbaseOptions } from "./types"; import { User } from "./user"; import { logApiResponse, registerAxiosInterceptors } from "./utils"; @@ -62,28 +61,24 @@ export class Coinbase { * Initializes the Coinbase SDK. * * @class - * @param apiKeyName - The API key name. - * @param privateKey - The private key associated with the API key. - * @param useServerSigner - Whether to use server signer or not. - * @param debugging - If true, logs API requests and responses to the console. - * @param basePath - The base path for the API. + * @param options - The constructor options. * @throws {InternalError} If the configuration is invalid. * @throws {InvalidAPIKeyFormat} If not able to create JWT token. */ - constructor( - apiKeyName: string, - privateKey: string, - useServerSigner: boolean = false, - debugging = false, - basePath: string = BASE_PATH, - ) { + constructor(options: CoinbaseOptions) { + const apiKeyName = options.apiKeyName ?? ""; + const privateKey = options.privateKey ?? ""; + const useServerSigner = options.useServerSigner === true; + const debugging = options.debugging === true; + const basePath = options.basePath ?? BASE_PATH; + if (apiKeyName === "") { throw new InternalError("Invalid configuration: apiKeyName is empty"); } if (privateKey === "") { throw new InternalError("Invalid configuration: privateKey is empty"); } - const coinbaseAuthenticator = new CoinbaseAuthenticator(apiKeyName, privateKey); + const coinbaseAuthenticator = new CoinbaseAuthenticator(apiKeyName!, privateKey!); const config = new Configuration({ basePath: basePath, }); @@ -94,13 +89,10 @@ export class Coinbase { response => logApiResponse(response, debugging), ); - Coinbase.apiClients.user = UsersApiFactory(config, BASE_PATH, axiosInstance); - Coinbase.apiClients.wallet = WalletsApiFactory(config, BASE_PATH, axiosInstance); - Coinbase.apiClients.address = AddressesApiFactory(config, BASE_PATH, axiosInstance); - Coinbase.apiClients.transfer = TransfersApiFactory(config, BASE_PATH, axiosInstance); - Coinbase.apiClients.baseSepoliaProvider = new ethers.JsonRpcProvider( - "https://sepolia.base.org", - ); + Coinbase.apiClients.user = UsersApiFactory(config, basePath, axiosInstance); + Coinbase.apiClients.wallet = WalletsApiFactory(config, basePath, axiosInstance); + Coinbase.apiClients.address = AddressesApiFactory(config, basePath, axiosInstance); + Coinbase.apiClients.transfer = TransfersApiFactory(config, basePath, axiosInstance); Coinbase.apiKeyPrivateKey = privateKey; Coinbase.useServerSigner = useServerSigner; } @@ -108,21 +100,18 @@ export class Coinbase { /** * Reads the API key and private key from a JSON file and initializes the Coinbase SDK. * - * @param filePath - The path to the JSON file containing the API key and private key. - * @param useServerSigner - Whether to use server signer or not. - * @param debugging - If true, logs API requests and responses to the console. - * @param basePath - The base path for the API. + * @param options - The configuration options. * @returns A new instance of the Coinbase SDK. * @throws {InvalidAPIKeyFormat} If the file does not exist or the configuration values are missing/invalid. * @throws {InvalidConfiguration} If the configuration is invalid. * @throws {InvalidAPIKeyFormat} If not able to create JWT token. */ - static configureFromJson( - filePath: string = "coinbase_cloud_api_key.json", - useServerSigner: boolean = false, - debugging: boolean = false, - basePath: string = BASE_PATH, - ): Coinbase { + static configureFromJson(options: CoinbaseConfigureFromJsonOptions): Coinbase { + const filePath = options.filePath ?? "coinbase_cloud_api_key.json"; + const useServerSigner = options.useServerSigner === true; + const debugging = options.debugging === true; + const basePath = options.basePath ?? BASE_PATH; + if (!fs.existsSync(filePath)) { throw new InvalidConfiguration(`Invalid configuration: file not found at ${filePath}`); } @@ -133,7 +122,13 @@ export class Coinbase { throw new InvalidAPIKeyFormat("Invalid configuration: missing configuration values"); } - return new Coinbase(config.name, config.privateKey, useServerSigner, debugging, basePath); + return new Coinbase({ + apiKeyName: config.name, + privateKey: config.privateKey, + useServerSigner: useServerSigner, + debugging: debugging, + basePath: basePath, + }); } catch (e) { if (e instanceof SyntaxError) { throw new InvalidAPIKeyFormat("Not able to parse the configuration file"); diff --git a/src/coinbase/tests/coinbase_test.ts b/src/coinbase/tests/coinbase_test.ts index d6f75010..17879adf 100644 --- a/src/coinbase/tests/coinbase_test.ts +++ b/src/coinbase/tests/coinbase_test.ts @@ -16,39 +16,46 @@ const PATH_PREFIX = "./src/coinbase/tests/config"; describe("Coinbase tests", () => { it("should throw an error if the API key name or private key is empty", () => { - expect(() => new Coinbase("", "test")).toThrow("Invalid configuration: apiKeyName is empty"); - expect(() => new Coinbase("test", "")).toThrow("Invalid configuration: privateKey is empty"); + expect(() => new Coinbase({ privateKey: "test" })).toThrow( + "Invalid configuration: apiKeyName is empty", + ); + expect(() => new Coinbase({ apiKeyName: "test" })).toThrow( + "Invalid configuration: privateKey is empty", + ); }); it("should throw an error if the file does not exist", () => { - expect(() => Coinbase.configureFromJson(`${PATH_PREFIX}/does-not-exist.json`)).toThrow( + expect(() => + Coinbase.configureFromJson({ filePath: `${PATH_PREFIX}/does-not-exist.json` }), + ).toThrow( "Invalid configuration: file not found at ./src/coinbase/tests/config/does-not-exist.json", ); }); it("should initialize the Coinbase SDK from a JSON file", () => { - const cbInstance = Coinbase.configureFromJson(`${PATH_PREFIX}/coinbase_cloud_api_key.json`); + const cbInstance = Coinbase.configureFromJson({ + filePath: `${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.configureFromJson(`${PATH_PREFIX}/invalid.json`)).toThrow( + expect(() => Coinbase.configureFromJson({ filePath: `${PATH_PREFIX}/invalid.json` })).toThrow( "Invalid configuration: missing configuration values", ); }); it("should throw an error if the JSON file is not parseable", () => { - expect(() => Coinbase.configureFromJson(`${PATH_PREFIX}/not_parseable.json`)).toThrow( - "Not able to parse the configuration file", - ); + expect(() => + Coinbase.configureFromJson({ filePath: `${PATH_PREFIX}/not_parseable.json` }), + ).toThrow("Not able to parse the configuration file"); }); describe("should able to interact with the API", () => { let user, walletId, publicKey, addressId, transactionHash; - const cbInstance = Coinbase.configureFromJson( - `${PATH_PREFIX}/coinbase_cloud_api_key.json`, - true, - ); + const cbInstance = Coinbase.configureFromJson({ + filePath: `${PATH_PREFIX}/coinbase_cloud_api_key.json`, + }); beforeAll(async () => { Coinbase.apiClients = { @@ -96,7 +103,9 @@ describe("Coinbase tests", () => { }); it("should raise an error if the user is not found", async () => { - const cbInstance = Coinbase.configureFromJson(`${PATH_PREFIX}/coinbase_cloud_api_key.json`); + const cbInstance = Coinbase.configureFromJson({ + filePath: `${PATH_PREFIX}/coinbase_cloud_api_key.json`, + }); Coinbase.apiClients.user!.getCurrentUser = mockReturnRejectedValue( new APIError("User not found"), ); diff --git a/src/coinbase/types.ts b/src/coinbase/types.ts index 24e5ad2c..1260f54d 100644 --- a/src/coinbase/types.ts +++ b/src/coinbase/types.ts @@ -1,6 +1,5 @@ import { Decimal } from "decimal.js"; import { AxiosPromise, AxiosRequestConfig, RawAxiosRequestConfig } from "axios"; -import { ethers } from "ethers"; import { Address as AddressModel, AddressList, @@ -298,7 +297,6 @@ export type ApiClients = { wallet?: WalletAPIClient; address?: AddressAPIClient; transfer?: TransferAPIClient; - baseSepoliaProvider?: ethers.Provider; }; /** @@ -347,3 +345,55 @@ export enum ServerSignerStatus { PENDING = "pending_seed_creation", ACTIVE = "active_seed", } + +/** + * CoinbaseOptions type definition. + */ +export type CoinbaseOptions = { + /** + * The API key name. + */ + apiKeyName?: string; + + /** + * The private key associated with the API key. + */ + privateKey?: string; + + /** + * Whether to use a Server-Signer or not. + */ + useServerSigner?: boolean; + + /** + * If true, logs API requests and responses to the console. + */ + debugging?: boolean; + + /** + * The base path for the API. + */ + basePath?: string; +}; + +export type CoinbaseConfigureFromJsonOptions = { + /** + * The path to the JSON file containing the API key and private key. + */ + filePath: string; + + /** + * Whether to use a Server-Signer or not. + */ + useServerSigner?: boolean; + + /** + * If true, logs API requests and responses to the console. + */ + debugging?: boolean; + + /** + * The base path for the API. + */ + basePath?: string; +};