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

Implementing initial version of Wallet Class #13

Merged
merged 11 commits into from
May 16, 2024
32 changes: 30 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
"plugin:prettier/recommended",
"plugin:jsdoc/recommended"
],
"plugins": ["@typescript-eslint", "prettier"],
"env": {
Expand All @@ -16,7 +17,34 @@
},
"rules": {
"multiline-comment-style": ["error", "starred-block"],
"prettier/prettier": "error"
"prettier/prettier": "error",
"jsdoc/tag-lines": ["error", "any", { "startLines": 1 }],
"jsdoc/check-alignment": "error",
"jsdoc/no-undefined-types": "off",
"jsdoc/check-param-names": "error",
"jsdoc/check-tag-names": "error",
"jsdoc/check-types": "error",
"jsdoc/implements-on-classes": "error",
"jsdoc/require-description": "error",
"jsdoc/require-jsdoc": [
"error",
{
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": true,
"ArrowFunctionExpression": false,
"FunctionExpression": false
}
}
],
"jsdoc/require-param": "error",
"jsdoc/require-param-description": "error",
"jsdoc/require-param-type": "off",
"jsdoc/require-returns": "error",
"jsdoc/require-returns-description": "error",
"jsdoc/require-returns-type": "off",
"jsdoc/require-hyphen-before-param-description": ["error", "always"]
},
"ignorePatterns": ["src/**/__tests__/**", "src/**/*.test.ts"]
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"axios-mock-adapter": "^1.22.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsdoc": "^48.2.5",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"mock-fs": "^5.2.0",
Expand Down
7 changes: 7 additions & 0 deletions src/coinbase/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export class Address {

/**
* Initializes a new Address instance.
*
* @param {AddressModel} model - The address model data.
* @param {AddressAPIClient} client - The API client to interact with address-related endpoints.
* @throws {InternalError} If the model or client is empty.
Expand All @@ -30,6 +31,7 @@ export class Address {
/**
* Requests faucet funds for the address.
* Only supported on testnet networks.
*
* @returns {Promise<FaucetTransaction>} The faucet transaction object.
* @throws {InternalError} If the request does not return a transaction hash.
* @throws {Error} If the request fails.
Expand All @@ -44,6 +46,7 @@ export class Address {

/**
* Returns the address ID.
*
* @returns {string} The address ID.
*/
public getId(): string {
Expand All @@ -52,6 +55,7 @@ export class Address {

/**
* Returns the network ID.
*
* @returns {string} The network ID.
*/
public getNetworkId(): string {
Expand All @@ -60,6 +64,7 @@ export class Address {

/**
* Returns the public key.
*
* @returns {string} The public key.
*/
public getPublicKey(): string {
Expand All @@ -68,6 +73,7 @@ export class Address {

/**
* Returns the wallet ID.
*
* @returns {string} The wallet ID.
*/
public getWalletId(): string {
Expand All @@ -76,6 +82,7 @@ export class Address {

/**
* Returns a string representation of the address.
*
* @returns {string} A string representing the address.
*/
public toString(): string {
Expand Down
6 changes: 5 additions & 1 deletion src/coinbase/api_error.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable jsdoc/require-jsdoc */
import { AxiosError } from "axios";
import { InternalError } from "./errors";

Expand All @@ -19,7 +20,8 @@ export class APIError extends AxiosError {

/**
* Initializes a new APIError object.
* @constructor
*
* @class
* @param {AxiosError} error - The Axios error.
*/
constructor(error) {
Expand All @@ -38,6 +40,7 @@ export class APIError extends AxiosError {

/**
* Creates a specific APIError based on the API error code.
*
* @param {AxiosError} error - The underlying error object.
* @returns {APIError} A specific APIError instance.
*/
Expand Down Expand Up @@ -100,6 +103,7 @@ export class APIError extends AxiosError {

/**
* Returns a String representation of the APIError.
*
* @returns {string} a String representation of the APIError
*/
toString() {
Expand Down
10 changes: 8 additions & 2 deletions src/coinbase/authenticator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import { InvalidAPIKeyFormat } from "./errors";
const pemHeader = "-----BEGIN EC PRIVATE KEY-----";
const pemFooter = "-----END EC PRIVATE KEY-----";

/* A class that builds JWTs for authenticating with the Coinbase Platform APIs. */
/**
* A class that builds JWTs for authenticating with the Coinbase Platform APIs.
*/
export class CoinbaseAuthenticator {
private apiKey: string;
private privateKey: string;

/**
* Initializes the Authenticator.
* @constructor
*
* @param {string} apiKey - The API key name.
* @param {string} privateKey - The private key associated with the API key.
*/
Expand All @@ -23,6 +25,7 @@ export class CoinbaseAuthenticator {

/**
* Middleware to intercept requests and add JWT to Authorization header.
*
* @param {InternalAxiosRequestConfig} config - The request configuration.
* @param {boolean} debugging - Flag to enable debugging.
* @returns {Promise<InternalAxiosRequestConfig>} The request configuration with the Authorization header added.
Expand All @@ -44,6 +47,7 @@ export class CoinbaseAuthenticator {

/**
* Builds the JWT for the given API endpoint URL.
*
* @param {string} url - URL of the API endpoint.
* @param {string} method - HTTP method of the request.
* @returns {Promise<string>} JWT token.
Expand Down Expand Up @@ -93,6 +97,7 @@ export class CoinbaseAuthenticator {

/**
* Extracts the PEM key from the given private key string.
*
* @param {string} privateKeyString - The private key string.
* @returns {string} The PEM key.
* @throws {InvalidAPIKeyFormat} If the private key string is not in the correct format.
Expand All @@ -109,6 +114,7 @@ export class CoinbaseAuthenticator {

/**
* Generates a random nonce for the JWT.
*
* @returns {string} The generated nonce.
*/
private nonce(): string {
Expand Down
30 changes: 23 additions & 7 deletions src/coinbase/coinbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,29 @@ import { ApiClients } from "./types";
import { User } from "./user";
import { logApiResponse, registerAxiosInterceptors } from "./utils";

// The Coinbase SDK.
/**
* The Coinbase SDK.
*/
export class Coinbase {
/**
* The list of supported networks.
*
* @constant
*/
static networkList = {
BaseSepolia: "base-sepolia",
};

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.
* @param {boolean} debugging - If true, logs API requests and responses to the console.
* @param {string} basePath - The base path for the API.
*
* @class
* @param apiKeyName - The API key name.
* @param privateKey - The private key associated with the API key.
* @param debugging - If true, logs API requests and responses to the console.
* @param basePath - The base path for the API.
* @throws {InternalError} If the configuration is invalid.
* @throws {InvalidAPIKeyFormat} If not able to create JWT token.
*/
Expand Down Expand Up @@ -51,7 +63,10 @@ export class Coinbase {

/**
* Reads the API key and private key from a JSON file and initializes the Coinbase SDK.
* @param {string} filePath - The path to the JSON file containing the API key and private key.
*
* @param filePath - The path to the JSON file containing the API key and private key.
* @param debugging - If true, logs API requests and responses to the console.
* @param basePath - The base path for the API.
* @returns {Coinbase} 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.
Expand Down Expand Up @@ -86,6 +101,7 @@ export class Coinbase {

/**
* Returns User object for the default user.
*
* @returns {User} The default user.
* @throws {APIError} If the request fails.
*/
Expand Down
24 changes: 23 additions & 1 deletion src/coinbase/errors.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* InvalidaAPIKeyFormat error is thrown when the API key format is invalid.
* @extends {Error}
*/
export class InvalidAPIKeyFormat extends Error {
static DEFAULT_MESSAGE = "Invalid API key format";

/**
* Initializes a new InvalidAPIKeyFormat instance.
*
* @param message - The error message.
*/
constructor(message: string = InvalidAPIKeyFormat.DEFAULT_MESSAGE) {
Expand All @@ -18,6 +18,26 @@ export class InvalidAPIKeyFormat extends Error {
}
}

/**
* ArgumentError is thrown when an argument is invalid.
*/
export class ArgumentError extends Error {
static DEFAULT_MESSAGE = "Argument Error";

/**
* Initializes a new ArgumentError instance.
*
* @param message - The error message.
*/
constructor(message: string = ArgumentError.DEFAULT_MESSAGE) {
super(message);
this.name = "ArgumentError";
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ArgumentError);
}
}
}

/**
* InternalError is thrown when there is an internal error in the SDK.
*/
Expand All @@ -26,6 +46,7 @@ export class InternalError extends Error {

/**
* Initializes a new InternalError instance.
*
* @param message - The error message.
*/
constructor(message: string = InternalError.DEFAULT_MESSAGE) {
Expand All @@ -45,6 +66,7 @@ export class InvalidConfiguration extends Error {

/**
* Initializes a new InvalidConfiguration instance.
*
* @param message - The error message.
*/
constructor(message: string = InvalidConfiguration.DEFAULT_MESSAGE) {
Expand Down
6 changes: 5 additions & 1 deletion src/coinbase/faucet_transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export class FaucetTransaction {
/**
* Creates a new FaucetTransaction instance.
* Do not use this method directly - instead, use Address.faucet().
* @constructor
*
* @class
* @param {FaucetTransactionModel} model - The FaucetTransaction model.
* @throws {InternalError} If the model does not exist.
*/
Expand All @@ -23,6 +24,7 @@ export class FaucetTransaction {

/**
* Returns the transaction hash.
*
* @returns {string} The transaction hash.
*/
public getTransactionHash(): string {
Expand All @@ -31,6 +33,7 @@ export class FaucetTransaction {

/**
* Returns the link to the transaction on the blockchain explorer.
*
* @returns {string} The link to the transaction on the blockchain explorer
*/
public getTransactionLink(): string {
Expand All @@ -40,6 +43,7 @@ export class FaucetTransaction {

/**
* Returns a string representation of the FaucetTransaction.
*
* @returns {string} A string representation of the FaucetTransaction.
*/
public toString(): string {
Expand Down
5 changes: 3 additions & 2 deletions src/coinbase/tests/address_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { FaucetTransaction } from "./../faucet_transaction";
import MockAdapter from "axios-mock-adapter";
import { randomUUID } from "crypto";
import { APIError, FaucetLimitReachedError } from "../api_error";
import { createAxiosMock } from "./utils";
import { Coinbase } from "../coinbase";
import { InternalError } from "../errors";
import { createAxiosMock } from "./utils";

const newEthAddress = ethers.Wallet.createRandom();

const VALID_ADDRESS_MODEL: AddressModel = {
address_id: newEthAddress.address,
network_id: "base-sepolia",
network_id: Coinbase.networkList.BaseSepolia,
public_key: newEthAddress.publicKey,
wallet_id: randomUUID(),
};
Expand Down
Loading
Loading