Skip to content

Commit

Permalink
Implementing initial version of Wallet Class (#13)
Browse files Browse the repository at this point in the history
- Adding Address Class with tests and updating faucet_transaction test file name
- Adding initial version of Wallet class
- Updating JSDoc with the latest eslint rules
- new eslint rules for JSDocs
- base_sepolia usage replaced with Coinbase.networkList.BaseSepolia
- Adding base Wallet class and test cases
  • Loading branch information
erdimaden authored May 16, 2024
1 parent b34089d commit b419443
Show file tree
Hide file tree
Showing 15 changed files with 509 additions and 39 deletions.
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

0 comments on commit b419443

Please sign in to comment.