Skip to content

Commit

Permalink
Merge master
Browse files Browse the repository at this point in the history
  • Loading branch information
yuga-cb committed May 17, 2024
2 parents 70c10ce + 2670112 commit cf1885d
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 46 deletions.
9 changes: 0 additions & 9 deletions src/coinbase/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,6 @@ export class Address {
return Balance.fromModelAndAssetId(response.data, assetId).amount;
}

/**
* Returns the public key.
*
* @returns {string} The public key.
*/
public getPublicKey(): string {
return this.model.public_key;
}

/**
* Returns the wallet ID.
*
Expand Down
10 changes: 9 additions & 1 deletion src/coinbase/coinbase.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import globalAxios from "axios";
import fs from "fs";
import { User as UserModel, UsersApiFactory, TransfersApiFactory } from "../client";
import {
AddressesApiFactory,
User as UserModel,
UsersApiFactory,
TransfersApiFactory,
WalletsApiFactory,
} from "../client";
import { ethers } from "ethers";
import { BASE_PATH } from "./../client/base";
import { Configuration } from "./../client/configuration";
Expand Down Expand Up @@ -80,6 +86,8 @@ export class Coinbase {
);

this.apiClients.user = UsersApiFactory(config, BASE_PATH, axiosInstance);
this.apiClients.wallet = WalletsApiFactory(config, BASE_PATH, axiosInstance);
this.apiClients.address = AddressesApiFactory(config, BASE_PATH, axiosInstance);
this.apiClients.transfer = TransfersApiFactory(config, BASE_PATH, axiosInstance);
this.apiClients.baseSepoliaProvider = new ethers.JsonRpcProvider("https://sepolia.base.org");
}
Expand Down
1 change: 1 addition & 0 deletions src/coinbase/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Coinbase } from "./coinbase";
4 changes: 0 additions & 4 deletions src/coinbase/tests/address_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ describe("Address", () => {
}
});

it("should return the public key", () => {
expect(address.getPublicKey()).toBe(newEthAddress.publicKey);
});

it("should return the wallet ID", () => {
expect(address.getWalletId()).toBe(VALID_ADDRESS_MODEL.wallet_id);
});
Expand Down
41 changes: 34 additions & 7 deletions src/coinbase/tests/coinbase_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Coinbase } from "../coinbase";
import MockAdapter from "axios-mock-adapter";
import axios from "axios";
import { APIError } from "../api_error";
import { VALID_WALLET_MODEL } from "./wallet_test";

const axiosMock = new MockAdapter(axios);
const PATH_PREFIX = "./src/coinbase/tests/config";
Expand Down Expand Up @@ -39,17 +40,43 @@ describe("Coinbase tests", () => {
);
});

it("should be able to get the default user", async () => {
axiosMock.onGet().reply(200, {
id: 123,
});
describe("should able to interact with the API", () => {
const cbInstance = Coinbase.configureFromJson(
`${PATH_PREFIX}/coinbase_cloud_api_key.json`,
true,
);
const user = await cbInstance.getDefaultUser();
expect(user.getId()).toBe(123);
expect(user.toString()).toBe("Coinbase:User{userId: 123}");
let user;
beforeEach(async () => {
axiosMock.reset();
axiosMock
.onPost(/\/v1\/wallets\/.*\/addresses\/.*\/faucet/)
.reply(200, { transaction_hash: "0xdeadbeef" })
.onGet(/\/me/)
.reply(200, {
id: 123,
})
.onPost(/\/v1\/wallets/)
.reply(200, VALID_WALLET_MODEL)
.onGet(/\/v1\/wallets\/.*/)
.reply(200, VALID_WALLET_MODEL);
user = await cbInstance.getDefaultUser();
});

it("should return the correct user ID", () => {
expect(user.getId()).toBe(123);
expect(user.toString()).toBe("User{ userId: 123 }");
});

it("should be able to get faucet funds", async () => {
const wallet = await user.createWallet();
expect(wallet.getId()).toBe(VALID_WALLET_MODEL.id);

const defaultAddress = wallet.defaultAddress();
expect(defaultAddress?.getId()).toBe(VALID_WALLET_MODEL.default_address.address_id);

const faucetTransaction = await wallet?.faucet();
expect(faucetTransaction.getTransactionHash()).toBe("0xdeadbeef");
});
});

it("should raise an error if the user is not found", async () => {
Expand Down
2 changes: 1 addition & 1 deletion src/coinbase/tests/user_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ describe("User Class", () => {

it("should return a correctly formatted string representation of the User instance", () => {
const user = new User(mockUserModel, mockApiClients);
expect(user.toString()).toBe(`Coinbase:User{userId: ${mockUserModel.id}}`);
expect(user.toString()).toBe(`User{ userId: ${mockUserModel.id} }`);
});
});
13 changes: 6 additions & 7 deletions src/coinbase/tests/wallet_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Wallet } from "../wallet";
import { createAxiosMock } from "./utils";

const walletId = randomUUID();
const VALID_WALLET_MODEL = {
export const VALID_WALLET_MODEL = {
id: randomUUID(),
network_id: Coinbase.networkList.BaseSepolia,
default_address: {
Expand Down Expand Up @@ -56,13 +56,12 @@ describe("Wallet Class", () => {
it("should derive the correct number of addresses", async () => {
expect(wallet.addresses.length).toBe(2);
});
});

it("should return the correct string representation", async () => {
const wallet = await Wallet.init(VALID_WALLET_MODEL, client);
expect(wallet.toString()).toBe(
`Wallet{id: '${VALID_WALLET_MODEL.id}', networkId: 'base-sepolia'}`,
);
it("should return the correct string representation", async () => {
expect(wallet.toString()).toBe(
`Wallet{id: '${VALID_WALLET_MODEL.id}', networkId: '${Coinbase.networkList.BaseSepolia}'}`,
);
});
});

it("should throw an ArgumentError when the API client is not provided", async () => {
Expand Down
32 changes: 28 additions & 4 deletions src/coinbase/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { AxiosPromise, AxiosRequestConfig, RawAxiosRequestConfig } from "axios";
import { ethers } from "ethers";
import {
Address,
Address as AddressModel,
AddressBalanceList,
Balance,
CreateAddressRequest,
CreateWalletRequest,
BroadcastTransferRequest,
CreateTransferRequest,
Expand All @@ -29,6 +30,15 @@ export type WalletAPIClient = {
createWalletRequest?: CreateWalletRequest,
options?: RawAxiosRequestConfig,
) => AxiosPromise<WalletModel>;

/**
* Returns the wallet model with the given ID.
*
* @param walletId - The ID of the wallet to fetch.
* @param options - Override http request option.
* @throws {APIError} If the request fails.
*/
getWallet: (walletId: string, options?: RawAxiosRequestConfig) => AxiosPromise<WalletModel>;
};

/**
Expand All @@ -40,7 +50,7 @@ export type AddressAPIClient = {
*
* @param walletId - The wallet ID.
* @param addressId - The address ID.
* @returns The transaction hash
* @returns The transaction hash.
* @throws {APIError} If the request fails.
*/
requestFaucetFunds(
Expand All @@ -49,7 +59,7 @@ export type AddressAPIClient = {
): Promise<{ data: { transaction_hash: string } }>;

/**
* Get address by onchain address
* Get address by onchain address.
*
* @param walletId - The ID of the wallet the address belongs to.
* @param addressId - The onchain address of the address that is being fetched.
Expand All @@ -60,7 +70,7 @@ export type AddressAPIClient = {
walletId: string,
addressId: string,
options?: AxiosRequestConfig,
): AxiosPromise<Address>;
): AxiosPromise<AddressModel>;

/**
* Get address balance
Expand Down Expand Up @@ -94,6 +104,20 @@ export type AddressAPIClient = {
page?: string,
options?: AxiosRequestConfig,
): AxiosPromise<AddressBalanceList>;

/*
* Create a new address scoped to the wallet.
*
* @param walletId - The ID of the wallet to create the address in.
* @param createAddressRequest - The address creation request.
* @param options - Axios request options.
* @throws {APIError} If the request fails.
*/
createAddress(
walletId: string,
createAddressRequest?: CreateAddressRequest,
options?: AxiosRequestConfig,
): AxiosPromise<AddressModel>;
};

/**
Expand Down
33 changes: 28 additions & 5 deletions src/coinbase/user.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { ApiClients } from "./types";
import { User as UserModel } from "./../client/api";
import { Coinbase } from "./coinbase";
import { Wallet } from "./wallet";

/**
* A representation of a User.
Expand All @@ -13,18 +15,39 @@ export class User {
/**
* Initializes a new User instance.
*
* @param {UserModel} user - The user model.
* @param {ApiClients} client - The API clients.
* @param user - The user model.
* @param client - The API clients.
*/
constructor(user: UserModel, client: ApiClients) {
this.client = client;
this.model = user;
}

/**
* Creates a new Wallet belonging to the User.
*
* @throws {APIError} - If the request fails.
* @throws {ArgumentError} - If the model or client is not provided.
* @throws {InternalError} - If address derivation or caching fails.
* @returns the new Wallet
*/
async createWallet(): Promise<Wallet> {
const payload = {
wallet: {
network_id: Coinbase.networkList.BaseSepolia,
},
};
const walletData = await this.client.wallet!.createWallet(payload);
return Wallet.init(walletData.data!, {
wallet: this.client.wallet!,
address: this.client.address!,
});
}

/**
* Returns the user's ID.
*
* @returns {string} The user's ID.
* @returns The user's ID.
*/
public getId(): string {
return this.model.id;
Expand All @@ -33,9 +56,9 @@ export class User {
/**
* Returns a string representation of the User.
*
* @returns {string} The string representation of the User.
* @returns The string representation of the User.
*/
toString(): string {
return `Coinbase:User{userId: ${this.model.id}}`;
return `User{ userId: ${this.model.id} }`;
}
}
Loading

0 comments on commit cf1885d

Please sign in to comment.