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 wallet.createWallet #16

Merged
merged 4 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions src/coinbase/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,6 @@ export class Address {
return this.model.network_id;
}

/**
* Returns the public key.
*
* @returns {string} The public key.
*/
public getPublicKey(): string {
Comment on lines -69 to -70
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yuga-cb's feedback

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 @@ -49,10 +49,6 @@ describe("Address", () => {
expect(address.getNetworkId()).toBe(VALID_ADDRESS_MODEL.network_id);
});

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
29 changes: 27 additions & 2 deletions src/coinbase/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { AxiosPromise, AxiosRequestConfig, RawAxiosRequestConfig } from "axios";
import { ethers } from "ethers";
import {
Address,
CreateAddressRequest,
CreateWalletRequest,
BroadcastTransferRequest,
CreateTransferRequest,
Expand All @@ -27,6 +29,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 @@ -38,7 +49,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 @@ -47,7 +58,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 @@ -58,6 +69,20 @@ export type AddressAPIClient = {
walletId: string,
addressId: string,
options?: AxiosRequestConfig,
): AxiosPromise<Address>;

/**
* 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
Comment on lines +31 to +32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets be consistent on if we add the - or not. Also for ending doc strings with period

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to use - for @throws or @return. However, it is necessary to use - for @params. I will investigate how we can extend this functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking with PSDK-135

*/
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
Loading