Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@coinbase/coinbase-sdk

Package Overview
Dependencies
Maintainers
0
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@coinbase/coinbase-sdk - npm Package Compare versions

Comparing version 0.0.4 to 0.0.6

dist/coinbase/tests/e2e.d.ts

211

dist/client/api.d.ts

@@ -33,2 +33,5 @@ import type { Configuration } from "./configuration";

}
export interface BroadcastTradeRequest {
signed_payload: string;
}
export interface BroadcastTransferRequest {

@@ -38,5 +41,14 @@ signed_payload: string;

export interface CreateAddressRequest {
public_key: string;
attestation: string;
public_key?: string;
attestation?: string;
}
export interface CreateServerSignerRequest {
server_signer_id: string;
enrollment_data: string;
}
export interface CreateTradeRequest {
amount: string;
from_asset_id: string;
to_asset_id: string;
}
export interface CreateTransferRequest {

@@ -49,4 +61,8 @@ amount: string;

export interface CreateWalletRequest {
wallet: Wallet;
wallet: CreateWalletRequestWallet;
}
export interface CreateWalletRequestWallet {
network_id: string;
use_server_signer?: boolean;
}
export interface FaucetTransaction {

@@ -59,2 +75,81 @@ transaction_hash: string;

}
export interface SeedCreationEvent {
wallet_id: string;
wallet_user_id: string;
}
export interface SeedCreationEventResult {
wallet_id: string;
wallet_user_id: string;
extended_public_key: string;
seed_id: string;
}
export interface ServerSigner {
server_signer_id: string;
wallets?: Array<string>;
}
export interface ServerSignerEvent {
server_signer_id: string;
event: ServerSignerEventEvent;
}
export type ServerSignerEventEvent = SeedCreationEvent | SignatureCreationEvent;
export interface ServerSignerEventList {
data: Array<ServerSignerEvent>;
has_more: boolean;
next_page: string;
total_count: number;
}
export interface SignatureCreationEvent {
seed_id: string;
wallet_id: string;
wallet_user_id: string;
address_id: string;
address_index: number;
signing_payload: string;
transaction_type: TransactionType;
transaction_id: string;
}
export interface SignatureCreationEventResult {
wallet_id: string;
wallet_user_id: string;
address_id: string;
transaction_type: TransactionType;
transaction_id: string;
signature: string;
}
export interface Trade {
network_id: string;
wallet_id: string;
address_id: string;
trade_id: string;
from_amount: string;
from_asset: Asset;
to_amount: string;
to_asset: Asset;
transaction: Transaction;
}
export interface TradeList {
data: Array<Trade>;
has_more: boolean;
next_page: string;
total_count: number;
}
export interface Transaction {
network_id: string;
from_address_id: string;
unsigned_payload: string;
signed_payload?: string;
transaction_hash?: string;
status: TransactionStatusEnum;
}
export declare const TransactionStatusEnum: {
readonly Pending: "pending";
readonly Broadcast: "broadcast";
readonly Complete: "complete";
readonly Failed: "failed";
};
export type TransactionStatusEnum = (typeof TransactionStatusEnum)[keyof typeof TransactionStatusEnum];
export declare const TransactionType: {
readonly Transfer: "transfer";
};
export type TransactionType = (typeof TransactionType)[keyof typeof TransactionType];
export interface Transfer {

@@ -91,6 +186,12 @@ network_id: string;

export interface Wallet {
id?: string;
id: string;
network_id: string;
default_address?: Address;
server_signer_status?: WalletServerSignerStatusEnum;
}
export declare const WalletServerSignerStatusEnum: {
readonly PendingSeedCreation: "pending_seed_creation";
readonly ActiveSeed: "active_seed";
};
export type WalletServerSignerStatusEnum = (typeof WalletServerSignerStatusEnum)[keyof typeof WalletServerSignerStatusEnum];
export interface WalletList {

@@ -126,3 +227,11 @@ data: Array<Wallet>;

};
export declare class AddressesApi extends BaseAPI {
export interface AddressesApiInterface {
createAddress(walletId: string, createAddressRequest?: CreateAddressRequest, options?: RawAxiosRequestConfig): AxiosPromise<Address>;
getAddress(walletId: string, addressId: string, options?: RawAxiosRequestConfig): AxiosPromise<Address>;
getAddressBalance(walletId: string, addressId: string, assetId: string, options?: RawAxiosRequestConfig): AxiosPromise<Balance>;
listAddressBalances(walletId: string, addressId: string, page?: string, options?: RawAxiosRequestConfig): AxiosPromise<AddressBalanceList>;
listAddresses(walletId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig): AxiosPromise<AddressList>;
requestFaucetFunds(walletId: string, addressId: string, options?: RawAxiosRequestConfig): AxiosPromise<FaucetTransaction>;
}
export declare class AddressesApi extends BaseAPI implements AddressesApiInterface {
createAddress(walletId: string, createAddressRequest?: CreateAddressRequest, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<Address, any>>;

@@ -135,2 +244,72 @@ getAddress(walletId: string, addressId: string, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<Address, any>>;

}
export declare const ServerSignersApiAxiosParamCreator: (configuration?: Configuration) => {
createServerSigner: (createServerSignerRequest?: CreateServerSignerRequest, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
getServerSigner: (serverSignerId: string, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
listServerSignerEvents: (serverSignerId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
listServerSigners: (options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
submitServerSignerSeedEventResult: (serverSignerId: string, seedCreationEventResult?: SeedCreationEventResult, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
submitServerSignerSignatureEventResult: (serverSignerId: string, signatureCreationEventResult?: SignatureCreationEventResult, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
};
export declare const ServerSignersApiFp: (configuration?: Configuration) => {
createServerSigner(createServerSignerRequest?: CreateServerSignerRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ServerSigner>>;
getServerSigner(serverSignerId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ServerSigner>>;
listServerSignerEvents(serverSignerId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ServerSignerEventList>>;
listServerSigners(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ServerSigner>>;
submitServerSignerSeedEventResult(serverSignerId: string, seedCreationEventResult?: SeedCreationEventResult, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SeedCreationEventResult>>;
submitServerSignerSignatureEventResult(serverSignerId: string, signatureCreationEventResult?: SignatureCreationEventResult, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SignatureCreationEventResult>>;
};
export declare const ServerSignersApiFactory: (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) => {
createServerSigner(createServerSignerRequest?: CreateServerSignerRequest, options?: any): AxiosPromise<ServerSigner>;
getServerSigner(serverSignerId: string, options?: any): AxiosPromise<ServerSigner>;
listServerSignerEvents(serverSignerId: string, limit?: number, page?: string, options?: any): AxiosPromise<ServerSignerEventList>;
listServerSigners(options?: any): AxiosPromise<ServerSigner>;
submitServerSignerSeedEventResult(serverSignerId: string, seedCreationEventResult?: SeedCreationEventResult, options?: any): AxiosPromise<SeedCreationEventResult>;
submitServerSignerSignatureEventResult(serverSignerId: string, signatureCreationEventResult?: SignatureCreationEventResult, options?: any): AxiosPromise<SignatureCreationEventResult>;
};
export interface ServerSignersApiInterface {
createServerSigner(createServerSignerRequest?: CreateServerSignerRequest, options?: RawAxiosRequestConfig): AxiosPromise<ServerSigner>;
getServerSigner(serverSignerId: string, options?: RawAxiosRequestConfig): AxiosPromise<ServerSigner>;
listServerSignerEvents(serverSignerId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig): AxiosPromise<ServerSignerEventList>;
listServerSigners(options?: RawAxiosRequestConfig): AxiosPromise<ServerSigner>;
submitServerSignerSeedEventResult(serverSignerId: string, seedCreationEventResult?: SeedCreationEventResult, options?: RawAxiosRequestConfig): AxiosPromise<SeedCreationEventResult>;
submitServerSignerSignatureEventResult(serverSignerId: string, signatureCreationEventResult?: SignatureCreationEventResult, options?: RawAxiosRequestConfig): AxiosPromise<SignatureCreationEventResult>;
}
export declare class ServerSignersApi extends BaseAPI implements ServerSignersApiInterface {
createServerSigner(createServerSignerRequest?: CreateServerSignerRequest, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<ServerSigner, any>>;
getServerSigner(serverSignerId: string, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<ServerSigner, any>>;
listServerSignerEvents(serverSignerId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<ServerSignerEventList, any>>;
listServerSigners(options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<ServerSigner, any>>;
submitServerSignerSeedEventResult(serverSignerId: string, seedCreationEventResult?: SeedCreationEventResult, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<SeedCreationEventResult, any>>;
submitServerSignerSignatureEventResult(serverSignerId: string, signatureCreationEventResult?: SignatureCreationEventResult, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<SignatureCreationEventResult, any>>;
}
export declare const TradesApiAxiosParamCreator: (configuration?: Configuration) => {
broadcastTrade: (walletId: string, addressId: string, tradeId: string, broadcastTradeRequest: BroadcastTradeRequest, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
createTrade: (walletId: string, addressId: string, createTradeRequest: CreateTradeRequest, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
getTrade: (walletId: string, addressId: string, tradeId: string, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
listTrades: (walletId: string, addressId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;
};
export declare const TradesApiFp: (configuration?: Configuration) => {
broadcastTrade(walletId: string, addressId: string, tradeId: string, broadcastTradeRequest: BroadcastTradeRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Trade>>;
createTrade(walletId: string, addressId: string, createTradeRequest: CreateTradeRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Trade>>;
getTrade(walletId: string, addressId: string, tradeId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Trade>>;
listTrades(walletId: string, addressId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<TradeList>>;
};
export declare const TradesApiFactory: (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) => {
broadcastTrade(walletId: string, addressId: string, tradeId: string, broadcastTradeRequest: BroadcastTradeRequest, options?: any): AxiosPromise<Trade>;
createTrade(walletId: string, addressId: string, createTradeRequest: CreateTradeRequest, options?: any): AxiosPromise<Trade>;
getTrade(walletId: string, addressId: string, tradeId: string, options?: any): AxiosPromise<Trade>;
listTrades(walletId: string, addressId: string, limit?: number, page?: string, options?: any): AxiosPromise<TradeList>;
};
export interface TradesApiInterface {
broadcastTrade(walletId: string, addressId: string, tradeId: string, broadcastTradeRequest: BroadcastTradeRequest, options?: RawAxiosRequestConfig): AxiosPromise<Trade>;
createTrade(walletId: string, addressId: string, createTradeRequest: CreateTradeRequest, options?: RawAxiosRequestConfig): AxiosPromise<Trade>;
getTrade(walletId: string, addressId: string, tradeId: string, options?: RawAxiosRequestConfig): AxiosPromise<Trade>;
listTrades(walletId: string, addressId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig): AxiosPromise<TradeList>;
}
export declare class TradesApi extends BaseAPI implements TradesApiInterface {
broadcastTrade(walletId: string, addressId: string, tradeId: string, broadcastTradeRequest: BroadcastTradeRequest, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<Trade, any>>;
createTrade(walletId: string, addressId: string, createTradeRequest: CreateTradeRequest, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<Trade, any>>;
getTrade(walletId: string, addressId: string, tradeId: string, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<Trade, any>>;
listTrades(walletId: string, addressId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<TradeList, any>>;
}
export declare const TransfersApiAxiosParamCreator: (configuration?: Configuration) => {

@@ -154,3 +333,9 @@ broadcastTransfer: (walletId: string, addressId: string, transferId: string, broadcastTransferRequest: BroadcastTransferRequest, options?: RawAxiosRequestConfig) => Promise<RequestArgs>;

};
export declare class TransfersApi extends BaseAPI {
export interface TransfersApiInterface {
broadcastTransfer(walletId: string, addressId: string, transferId: string, broadcastTransferRequest: BroadcastTransferRequest, options?: RawAxiosRequestConfig): AxiosPromise<Transfer>;
createTransfer(walletId: string, addressId: string, createTransferRequest: CreateTransferRequest, options?: RawAxiosRequestConfig): AxiosPromise<Transfer>;
getTransfer(walletId: string, addressId: string, transferId: string, options?: RawAxiosRequestConfig): AxiosPromise<Transfer>;
listTransfers(walletId: string, addressId: string, limit?: number, page?: string, options?: RawAxiosRequestConfig): AxiosPromise<TransferList>;
}
export declare class TransfersApi extends BaseAPI implements TransfersApiInterface {
broadcastTransfer(walletId: string, addressId: string, transferId: string, broadcastTransferRequest: BroadcastTransferRequest, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<Transfer, any>>;

@@ -170,3 +355,6 @@ createTransfer(walletId: string, addressId: string, createTransferRequest: CreateTransferRequest, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<Transfer, any>>;

};
export declare class UsersApi extends BaseAPI {
export interface UsersApiInterface {
getCurrentUser(options?: RawAxiosRequestConfig): AxiosPromise<User>;
}
export declare class UsersApi extends BaseAPI implements UsersApiInterface {
getCurrentUser(options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<User, any>>;

@@ -195,3 +383,10 @@ }

};
export declare class WalletsApi extends BaseAPI {
export interface WalletsApiInterface {
createWallet(createWalletRequest?: CreateWalletRequest, options?: RawAxiosRequestConfig): AxiosPromise<Wallet>;
getWallet(walletId: string, options?: RawAxiosRequestConfig): AxiosPromise<Wallet>;
getWalletBalance(walletId: string, assetId: string, options?: RawAxiosRequestConfig): AxiosPromise<Balance>;
listWalletBalances(walletId: string, options?: RawAxiosRequestConfig): AxiosPromise<AddressBalanceList>;
listWallets(limit?: number, page?: string, options?: RawAxiosRequestConfig): AxiosPromise<WalletList>;
}
export declare class WalletsApi extends BaseAPI implements WalletsApiInterface {
createWallet(createWalletRequest?: CreateWalletRequest, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<Wallet, any>>;

@@ -198,0 +393,0 @@ getWallet(walletId: string, options?: RawAxiosRequestConfig): Promise<import("axios").AxiosResponse<Wallet, any>>;

5

dist/client/common.js

@@ -23,6 +23,3 @@ "use strict";

if (configuration && (configuration.username || configuration.password)) {
object["auth"] = {
username: configuration.username,
password: configuration.password,
};
object["auth"] = { username: configuration.username, password: configuration.password };
}

@@ -29,0 +26,0 @@ };

@@ -54,3 +54,13 @@ "use strict";

async getBalance(assetId) {
const response = await coinbase_1.Coinbase.apiClients.address.getAddressBalance(this.model.wallet_id, this.model.address_id, assetId);
const normalizedAssetId = (() => {
switch (assetId) {
case coinbase_1.Coinbase.assets.Gwei:
return coinbase_1.Coinbase.assets.Eth;
case coinbase_1.Coinbase.assets.Wei:
return coinbase_1.Coinbase.assets.Eth;
default:
return assetId;
}
})();
const response = await coinbase_1.Coinbase.apiClients.address.getAddressBalance(this.model.wallet_id, this.model.address_id, normalizedAssetId);
if (!response.data) {

@@ -65,3 +75,3 @@ return new decimal_js_1.Decimal(0);

async createTransfer(amount, assetId, destination, intervalSeconds = 0.2, timeoutSeconds = 10) {
if (!this.key) {
if (!coinbase_1.Coinbase.useServerSigner && !this.key) {
throw new errors_1.InternalError("Cannot transfer from address without private key loaded");

@@ -96,2 +106,3 @@ }

case coinbase_1.Coinbase.assets.Gwei:
return coinbase_1.Coinbase.assets.Eth;
case coinbase_1.Coinbase.assets.Wei:

@@ -110,16 +121,19 @@ return coinbase_1.Coinbase.assets.Eth;

let response = await coinbase_1.Coinbase.apiClients.transfer.createTransfer(this.getWalletId(), this.getId(), createTransferRequest);
const transfer = transfer_1.Transfer.fromModel(response.data);
const transaction = transfer.getTransaction();
let signedPayload = await this.key.signTransaction(transaction);
signedPayload = signedPayload.slice(2);
const broadcastTransferRequest = {
signed_payload: signedPayload,
};
response = await coinbase_1.Coinbase.apiClients.transfer.broadcastTransfer(this.getWalletId(), this.getId(), transfer.getId(), broadcastTransferRequest);
const updatedTransfer = transfer_1.Transfer.fromModel(response.data);
let transfer = transfer_1.Transfer.fromModel(response.data);
if (!coinbase_1.Coinbase.useServerSigner) {
const transaction = transfer.getTransaction();
let signedPayload = await this.key.signTransaction(transaction);
signedPayload = signedPayload.slice(2);
const broadcastTransferRequest = {
signed_payload: signedPayload,
};
response = await coinbase_1.Coinbase.apiClients.transfer.broadcastTransfer(this.getWalletId(), this.getId(), transfer.getId(), broadcastTransferRequest);
transfer = transfer_1.Transfer.fromModel(response.data);
}
const startTime = Date.now();
while (Date.now() - startTime < timeoutSeconds * 1000) {
const status = await updatedTransfer.getStatus();
await transfer.reload();
const status = transfer.getStatus();
if (status === types_1.TransferStatus.COMPLETE || status === types_1.TransferStatus.FAILED) {
return updatedTransfer;
return transfer;
}

@@ -126,0 +140,0 @@ await (0, utils_1.delay)(intervalSeconds);

@@ -45,3 +45,3 @@ "use strict";

sub: this.apiKey,
iss: "coinbase-cloud",
iss: "cdp",
aud: ["cdp_service"],

@@ -48,0 +48,0 @@ nbf: Math.floor(Date.now() / 1000),

@@ -1,2 +0,2 @@

import { ApiClients } from "./types";
import { ApiClients, CoinbaseConfigureFromJsonOptions, CoinbaseOptions } from "./types";
import { User } from "./user";

@@ -15,7 +15,7 @@ export declare class Coinbase {

static apiClients: ApiClients;
static backupFilePath: string;
static apiKeyPrivateKey: string;
constructor(apiKeyName: string, privateKey: string, debugging?: boolean, basePath?: string, backupFilePath?: string);
static configureFromJson(filePath?: string, debugging?: boolean, basePath?: string, backupFilePath?: string): Coinbase;
static useServerSigner: boolean;
constructor({ apiKeyName, privateKey, useServerSigner, debugging, basePath, }: CoinbaseOptions);
static configureFromJson({ filePath, useServerSigner, debugging, basePath, }: CoinbaseConfigureFromJsonOptions): Coinbase;
getDefaultUser(): Promise<User>;
}

@@ -33,3 +33,2 @@ "use strict";

const client_1 = require("../client");
const ethers_1 = require("ethers");
const base_1 = require("./../client/base");

@@ -41,4 +40,5 @@ const configuration_1 = require("./../client/configuration");

const utils_1 = require("./utils");
const os = __importStar(require("os"));
class Coinbase {
constructor(apiKeyName, privateKey, debugging = false, basePath = base_1.BASE_PATH, backupFilePath) {
constructor({ apiKeyName = "", privateKey = "", useServerSigner = false, debugging = false, basePath = base_1.BASE_PATH, }) {
if (apiKeyName === "") {

@@ -56,11 +56,11 @@ throw new errors_1.InternalError("Invalid configuration: apiKeyName is empty");

(0, utils_1.registerAxiosInterceptors)(axiosInstance, config => coinbaseAuthenticator.authenticateRequest(config, debugging), response => (0, utils_1.logApiResponse)(response, debugging));
Coinbase.apiClients.user = (0, client_1.UsersApiFactory)(config, base_1.BASE_PATH, axiosInstance);
Coinbase.apiClients.wallet = (0, client_1.WalletsApiFactory)(config, base_1.BASE_PATH, axiosInstance);
Coinbase.apiClients.address = (0, client_1.AddressesApiFactory)(config, base_1.BASE_PATH, axiosInstance);
Coinbase.apiClients.transfer = (0, client_1.TransfersApiFactory)(config, base_1.BASE_PATH, axiosInstance);
Coinbase.apiClients.baseSepoliaProvider = new ethers_1.ethers.JsonRpcProvider("https://sepolia.base.org");
Coinbase.backupFilePath = backupFilePath ? backupFilePath : Coinbase.backupFilePath;
Coinbase.apiClients.user = (0, client_1.UsersApiFactory)(config, basePath, axiosInstance);
Coinbase.apiClients.wallet = (0, client_1.WalletsApiFactory)(config, basePath, axiosInstance);
Coinbase.apiClients.address = (0, client_1.AddressesApiFactory)(config, basePath, axiosInstance);
Coinbase.apiClients.transfer = (0, client_1.TransfersApiFactory)(config, basePath, axiosInstance);
Coinbase.apiKeyPrivateKey = privateKey;
Coinbase.useServerSigner = useServerSigner;
}
static configureFromJson(filePath = "coinbase_cloud_api_key.json", debugging = false, basePath = base_1.BASE_PATH, backupFilePath) {
static configureFromJson({ filePath = "coinbase_cloud_api_key.json", useServerSigner = false, debugging = false, basePath = base_1.BASE_PATH, }) {
filePath = filePath.startsWith("~") ? filePath.replace("~", os.homedir()) : filePath;
if (!fs.existsSync(filePath)) {

@@ -75,3 +75,9 @@ throw new errors_1.InvalidConfiguration(`Invalid configuration: file not found at ${filePath}`);

}
return new Coinbase(config.name, config.privateKey, debugging, basePath, backupFilePath);
return new Coinbase({
apiKeyName: config.name,
privateKey: config.privateKey,
useServerSigner: useServerSigner,
debugging: debugging,
basePath: basePath,
});
}

@@ -104,2 +110,1 @@ catch (e) {

Coinbase.apiClients = {};
Coinbase.backupFilePath = "seed.json";

@@ -40,2 +40,3 @@ "use strict";

const transfer_1 = require("../transfer");
const types_1 = require("../types");
describe("Address", () => {

@@ -100,3 +101,3 @@ const transactionHash = (0, utils_1.generateRandomHash)();

expect(ethBalance).toEqual(new decimal_js_1.default("1000000000"));
expect(coinbase_1.Coinbase.apiClients.address.getAddressBalance).toHaveBeenCalledWith(address.getWalletId(), address.getId(), assetId);
expect(coinbase_1.Coinbase.apiClients.address.getAddressBalance).toHaveBeenCalledWith(address.getWalletId(), address.getId(), coinbase_1.Coinbase.assets.Eth);
expect(coinbase_1.Coinbase.apiClients.address.getAddressBalance).toHaveBeenCalledTimes(1);

@@ -109,3 +110,3 @@ });

expect(ethBalance).toEqual(new decimal_js_1.default("1000000000000000000"));
expect(coinbase_1.Coinbase.apiClients.address.getAddressBalance).toHaveBeenCalledWith(address.getWalletId(), address.getId(), assetId);
expect(coinbase_1.Coinbase.apiClients.address.getAddressBalance).toHaveBeenCalledWith(address.getWalletId(), address.getId(), coinbase_1.Coinbase.assets.Eth);
expect(coinbase_1.Coinbase.apiClients.address.getAddressBalance).toHaveBeenCalledTimes(1);

@@ -156,6 +157,2 @@ });

let walletId, id;
const mockProvider = new ethers_1.ethers.JsonRpcProvider("https://sepolia.base.org");
mockProvider.getTransaction = jest.fn();
mockProvider.getTransactionReceipt = jest.fn();
coinbase_1.Coinbase.apiClients.baseSepoliaProvider = mockProvider;
beforeEach(() => {

@@ -187,11 +184,10 @@ weiAmount = new decimal_js_1.default("500000000000000000");

});
mockProvider.getTransaction.mockResolvedValueOnce({
blockHash: "0xdeadbeef",
coinbase_1.Coinbase.apiClients.transfer.getTransfer = (0, utils_1.mockReturnValue)({
...utils_1.VALID_TRANSFER_MODEL,
status: types_1.TransferStatus.COMPLETE,
});
mockProvider.getTransactionReceipt.mockResolvedValueOnce({
status: 1,
});
const transfer = await address.createTransfer(weiAmount, coinbase_1.Coinbase.assets.Wei, destination, intervalSeconds, timeoutSeconds);
await address.createTransfer(weiAmount, coinbase_1.Coinbase.assets.Wei, destination, intervalSeconds, timeoutSeconds);
expect(coinbase_1.Coinbase.apiClients.transfer.createTransfer).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.transfer.broadcastTransfer).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.transfer.getTransfer).toHaveBeenCalledTimes(1);
});

@@ -217,2 +213,6 @@ it("should throw an APIError if the createTransfer API call fails", async () => {

});
coinbase_1.Coinbase.apiClients.transfer.getTransfer = (0, utils_1.mockReturnValue)({
...utils_1.VALID_TRANSFER_MODEL,
status: types_1.TransferStatus.BROADCAST,
});
intervalSeconds = 0.000002;

@@ -226,2 +226,13 @@ timeoutSeconds = 0.000002;

});
it("should successfully create and complete a transfer when using server signer", async () => {
coinbase_1.Coinbase.useServerSigner = true;
coinbase_1.Coinbase.apiClients.transfer.createTransfer = (0, utils_1.mockReturnValue)(utils_1.VALID_TRANSFER_MODEL);
coinbase_1.Coinbase.apiClients.transfer.getTransfer = (0, utils_1.mockReturnValue)({
...utils_1.VALID_TRANSFER_MODEL,
status: types_1.TransferStatus.COMPLETE,
});
await address.createTransfer(weiAmount, coinbase_1.Coinbase.assets.Wei, destination, intervalSeconds, timeoutSeconds);
expect(coinbase_1.Coinbase.apiClients.transfer.createTransfer).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.transfer.getTransfer).toHaveBeenCalledTimes(1);
});
afterEach(() => {

@@ -228,0 +239,0 @@ jest.restoreAllMocks();

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const os = __importStar(require("os"));
const fs = __importStar(require("fs"));
const crypto_1 = require("crypto");

@@ -8,24 +36,39 @@ const api_error_1 = require("../api_error");

const ethers_1 = require("ethers");
const path_1 = __importDefault(require("path"));
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_1.Coinbase("", "test")).toThrow("Invalid configuration: apiKeyName is empty");
expect(() => new coinbase_1.Coinbase("test", "")).toThrow("Invalid configuration: privateKey is empty");
expect(() => new coinbase_1.Coinbase({ privateKey: "test" })).toThrow("Invalid configuration: apiKeyName is empty");
expect(() => new coinbase_1.Coinbase({ apiKeyName: "test" })).toThrow("Invalid configuration: privateKey is empty");
});
it("should throw an error if the file does not exist", () => {
expect(() => coinbase_1.Coinbase.configureFromJson(`${PATH_PREFIX}/does-not-exist.json`)).toThrow("Invalid configuration: file not found at ./src/coinbase/tests/config/does-not-exist.json");
expect(() => coinbase_1.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_1.Coinbase.configureFromJson(`${PATH_PREFIX}/coinbase_cloud_api_key.json`);
const cbInstance = coinbase_1.Coinbase.configureFromJson({
filePath: `${PATH_PREFIX}/coinbase_cloud_api_key.json`,
});
expect(cbInstance).toBeInstanceOf(coinbase_1.Coinbase);
});
it("should throw an error if there is an issue reading the file or parsing the JSON data", () => {
expect(() => coinbase_1.Coinbase.configureFromJson(`${PATH_PREFIX}/invalid.json`)).toThrow("Invalid configuration: missing configuration values");
expect(() => coinbase_1.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_1.Coinbase.configureFromJson(`${PATH_PREFIX}/not_parseable.json`)).toThrow("Not able to parse the configuration file");
expect(() => coinbase_1.Coinbase.configureFromJson({ filePath: `${PATH_PREFIX}/not_parseable.json` })).toThrow("Not able to parse the configuration file");
});
it("should expand the tilde to the home directory", () => {
const configuration = fs.readFileSync(`${PATH_PREFIX}/coinbase_cloud_api_key.json`, "utf8");
const homeDir = os.homedir();
const relativePath = "~/test_config.json";
const expandedPath = path_1.default.join(homeDir, "test_config.json");
fs.writeFileSync(expandedPath, configuration, "utf8");
const cbInstance = coinbase_1.Coinbase.configureFromJson({ filePath: relativePath });
expect(cbInstance).toBeInstanceOf(coinbase_1.Coinbase);
fs.unlinkSync(expandedPath);
});
describe("should able to interact with the API", () => {
let user, walletId, publicKey, addressId, transactionHash;
const cbInstance = coinbase_1.Coinbase.configureFromJson(`${PATH_PREFIX}/coinbase_cloud_api_key.json`, true);
const cbInstance = coinbase_1.Coinbase.configureFromJson({
filePath: `${PATH_PREFIX}/coinbase_cloud_api_key.json`,
});
beforeAll(async () => {

@@ -69,3 +112,5 @@ coinbase_1.Coinbase.apiClients = {

it("should raise an error if the user is not found", async () => {
const cbInstance = coinbase_1.Coinbase.configureFromJson(`${PATH_PREFIX}/coinbase_cloud_api_key.json`);
const cbInstance = coinbase_1.Coinbase.configureFromJson({
filePath: `${PATH_PREFIX}/coinbase_cloud_api_key.json`,
});
coinbase_1.Coinbase.apiClients.user.getCurrentUser = (0, utils_1.mockReturnRejectedValue)(new api_error_1.APIError("User not found"));

@@ -72,0 +117,0 @@ await expect(cbInstance.getDefaultUser()).rejects.toThrow(api_error_1.APIError);

@@ -16,6 +16,2 @@ "use strict";

const transactionHash = "0x6c087c1676e8269dd81e0777244584d0cbfd39b6997b3477242a008fa9349e11";
const mockProvider = new ethers_1.ethers.JsonRpcProvider("https://sepolia.base.org");
mockProvider.getTransaction = jest.fn();
mockProvider.getTransactionReceipt = jest.fn();
coinbase_1.Coinbase.apiClients.baseSepoliaProvider = mockProvider;
describe("Transfer Class", () => {

@@ -25,2 +21,3 @@ let transferModel;

beforeEach(() => {
coinbase_1.Coinbase.apiClients.transfer = utils_1.transfersApiMock;
transferModel = utils_1.VALID_TRANSFER_MODEL;

@@ -118,46 +115,58 @@ transfer = transfer_1.Transfer.fromModel(transferModel);

it("should return PENDING when the transaction has not been created", async () => {
const status = await transfer.getStatus();
const status = transfer.getStatus();
expect(status).toEqual(types_1.TransferStatus.PENDING);
});
it("should return PENDING when the transaction has been created but not broadcast", async () => {
transferModel.transaction_hash = transactionHash;
transfer = transfer_1.Transfer.fromModel(transferModel);
mockProvider.getTransaction.mockResolvedValueOnce(null);
const status = await transfer.getStatus();
const status = transfer.getStatus();
expect(status).toEqual(types_1.TransferStatus.PENDING);
});
it("should return BROADCAST when the transaction has been broadcast but not included in a block", async () => {
transferModel.transaction_hash = transactionHash;
transferModel.status = types_1.TransferStatus.BROADCAST;
transfer = transfer_1.Transfer.fromModel(transferModel);
mockProvider.getTransaction.mockResolvedValueOnce({
blockHash: null,
});
const status = await transfer.getStatus();
const status = transfer.getStatus();
expect(status).toEqual(types_1.TransferStatus.BROADCAST);
});
it("should return COMPLETE when the transaction has confirmed", async () => {
transferModel.transaction_hash = transactionHash;
transferModel.status = types_1.TransferStatus.COMPLETE;
transfer = transfer_1.Transfer.fromModel(transferModel);
mockProvider.getTransaction.mockResolvedValueOnce({
blockHash: "0xdeadbeef",
});
mockProvider.getTransactionReceipt.mockResolvedValueOnce({
status: 1,
});
const status = await transfer.getStatus();
const status = transfer.getStatus();
expect(status).toEqual(types_1.TransferStatus.COMPLETE);
});
it("should return FAILED when the transaction has failed", async () => {
transferModel.transaction_hash = transactionHash;
transferModel.status = types_1.TransferStatus.FAILED;
transfer = transfer_1.Transfer.fromModel(transferModel);
mockProvider.getTransaction.mockResolvedValueOnce({
blockHash: "0xdeadbeef",
const status = transfer.getStatus();
expect(status).toEqual(types_1.TransferStatus.FAILED);
});
});
describe("reload", () => {
it("should return PENDING when the trnasaction has not been created", async () => {
coinbase_1.Coinbase.apiClients.transfer.getTransfer = (0, utils_1.mockReturnValue)({
...utils_1.VALID_TRANSFER_MODEL,
status: types_1.TransferStatus.PENDING,
});
mockProvider.getTransactionReceipt.mockResolvedValueOnce({
status: 0,
await transfer.reload();
expect(transfer.getStatus()).toEqual(types_1.TransferStatus.PENDING);
expect(coinbase_1.Coinbase.apiClients.transfer.getTransfer).toHaveBeenCalledTimes(1);
});
it("should return COMPLETE when the trnasaction is complete", async () => {
coinbase_1.Coinbase.apiClients.transfer.getTransfer = (0, utils_1.mockReturnValue)({
...utils_1.VALID_TRANSFER_MODEL,
status: types_1.TransferStatus.COMPLETE,
});
const status = await transfer.getStatus();
expect(status).toEqual(types_1.TransferStatus.FAILED);
await transfer.reload();
expect(transfer.getStatus()).toEqual(types_1.TransferStatus.COMPLETE);
expect(coinbase_1.Coinbase.apiClients.transfer.getTransfer).toHaveBeenCalledTimes(1);
});
it("should return FAILED when the trnasaction has failed", async () => {
coinbase_1.Coinbase.apiClients.transfer.getTransfer = (0, utils_1.mockReturnValue)({
...utils_1.VALID_TRANSFER_MODEL,
status: types_1.TransferStatus.FAILED,
});
await transfer.reload();
expect(transfer.getStatus()).toEqual(types_1.TransferStatus.FAILED);
expect(coinbase_1.Coinbase.apiClients.transfer.getTransfer).toHaveBeenCalledTimes(1);
});
});
});

@@ -30,3 +30,2 @@ "use strict";

const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs"));
const errors_1 = require("../errors");

@@ -104,189 +103,2 @@ const coinbase_1 = require("./../coinbase");

});
describe(".saveWallet", () => {
let seed;
let walletId;
let mockSeedWallet;
let savedWallet;
beforeAll(async () => {
walletId = crypto.randomUUID();
seed = "86fc9fba421dcc6ad42747f14132c3cd975bd9fb1454df84ce5ea554f2542fbe";
const { address1, wallet1PrivateKey } = (0, utils_1.generateWalletFromSeed)(seed);
mockAddressModel = {
address_id: address1,
wallet_id: walletId,
public_key: wallet1PrivateKey,
network_id: coinbase_1.Coinbase.networkList.BaseSepolia,
};
mockWalletModel = {
id: walletId,
network_id: coinbase_1.Coinbase.networkList.BaseSepolia,
default_address: mockAddressModel,
};
user = new user_1.User(mockUserModel);
coinbase_1.Coinbase.apiClients.address = utils_1.addressesApiMock;
coinbase_1.Coinbase.apiClients.address.getAddress = (0, utils_1.mockReturnValue)(mockAddressModel);
coinbase_1.Coinbase.backupFilePath = crypto.randomUUID() + ".json";
coinbase_1.Coinbase.apiKeyPrivateKey = crypto.generateKeyPairSync("ec", {
namedCurve: "prime256v1",
privateKeyEncoding: { type: "pkcs8", format: "pem" },
publicKeyEncoding: { type: "spki", format: "pem" },
}).privateKey;
mockSeedWallet = await wallet_1.Wallet.init(mockWalletModel, seed, [mockAddressModel]);
});
afterEach(async () => {
fs.unlinkSync(coinbase_1.Coinbase.backupFilePath);
});
it("should save the Wallet data when encryption is false", async () => {
savedWallet = user.saveWallet(mockSeedWallet);
expect(savedWallet).toBe(mockSeedWallet);
const storedSeedData = fs.readFileSync(coinbase_1.Coinbase.backupFilePath);
const walletSeedData = JSON.parse(storedSeedData.toString());
expect(walletSeedData[walletId].encrypted).toBe(false);
expect(walletSeedData[walletId].iv).toBe("");
expect(walletSeedData[walletId].authTag).toBe("");
expect(walletSeedData[walletId].seed).toBe(seed);
});
it("should save the Wallet data when encryption is true", async () => {
savedWallet = user.saveWallet(mockSeedWallet, true);
expect(savedWallet).toBe(mockSeedWallet);
const storedSeedData = fs.readFileSync(coinbase_1.Coinbase.backupFilePath);
const walletSeedData = JSON.parse(storedSeedData.toString());
expect(walletSeedData[walletId].encrypted).toBe(true);
expect(walletSeedData[walletId].iv).toBeTruthy();
expect(walletSeedData[walletId].authTag).toBeTruthy();
expect(walletSeedData[walletId].seed).not.toBe(seed);
});
it("should throw an error when the existing file is malformed", async () => {
fs.writeFileSync(coinbase_1.Coinbase.backupFilePath, JSON.stringify({ malformed: "test" }, null, 2), "utf8");
expect(() => user.saveWallet(mockSeedWallet)).toThrow(errors_1.ArgumentError);
});
});
describe(".loadWallets", () => {
let mockUserModel;
let user;
let walletId;
let addressModel;
let walletModelWithDefaultAddress;
let addressListModel;
let initialSeedData;
let malformedSeedData;
let seedDataWithoutSeed;
let seedDataWithoutIv;
let seedDataWithoutAuthTag;
beforeAll(() => {
walletId = crypto.randomUUID();
addressModel = (0, utils_1.newAddressModel)(walletId);
addressModel.address_id = "0xB1666C6cDDB29468f721f3A4881a6e95CC963849";
walletModelWithDefaultAddress = {
id: walletId,
network_id: coinbase_1.Coinbase.networkList.BaseSepolia,
default_address: addressModel,
};
addressListModel = {
data: [addressModel],
has_more: false,
next_page: "",
total_count: 1,
};
coinbase_1.Coinbase.apiClients.wallet = utils_1.walletsApiMock;
coinbase_1.Coinbase.apiClients.address = utils_1.addressesApiMock;
coinbase_1.Coinbase.backupFilePath = `${crypto.randomUUID()}.json`;
coinbase_1.Coinbase.apiKeyPrivateKey = crypto.generateKeyPairSync("ec", {
namedCurve: "prime256v1",
privateKeyEncoding: { type: "pkcs8", format: "pem" },
publicKeyEncoding: { type: "spki", format: "pem" },
}).privateKey;
mockUserModel = {
id: "12345",
};
initialSeedData = {
[walletId]: {
seed: "86fc9fba421dcc6ad42747f14132c3cd975bd9fb1454df84ce5ea554f2542fbe",
encrypted: false,
iv: "",
authTag: "",
},
};
malformedSeedData = {
[walletId]: "test",
};
seedDataWithoutSeed = {
[walletId]: {
seed: "",
encrypted: false,
},
};
seedDataWithoutIv = {
[walletId]: {
seed: "86fc9fba421dcc6ad42747f14132c3cd975bd9fb1454df84ce5ea554f2542fbe",
encrypted: true,
iv: "",
auth_tag: "0x111",
},
};
seedDataWithoutAuthTag = {
[walletId]: {
seed: "86fc9fba421dcc6ad42747f14132c3cd975bd9fb1454df84ce5ea554f2542fbe",
encrypted: true,
iv: "0x111",
auth_tag: "",
},
};
});
beforeEach(() => {
user = new user_1.User(mockUserModel);
fs.writeFileSync(coinbase_1.Coinbase.backupFilePath, JSON.stringify(initialSeedData, null, 2));
});
afterEach(() => {
if (fs.existsSync(coinbase_1.Coinbase.backupFilePath)) {
fs.unlinkSync(coinbase_1.Coinbase.backupFilePath);
}
});
it("loads the Wallet from backup", async () => {
const seed = "86fc9fba421dcc6ad42747f14132c3cd975bd9fb1454df84ce5ea554f2542fbe";
const { address1, address2 } = (0, utils_1.generateWalletFromSeed)(seed);
const addressModel1 = (0, utils_1.newAddressModel)(walletId, address1);
const addressModel2 = (0, utils_1.newAddressModel)(walletId, address2);
walletModelWithDefaultAddress = {
id: walletId,
network_id: coinbase_1.Coinbase.networkList.BaseSepolia,
default_address: addressModel1,
};
addressListModel = {
data: [addressModel1, addressModel2],
has_more: false,
next_page: "",
total_count: 2,
};
coinbase_1.Coinbase.apiClients.wallet = utils_1.walletsApiMock;
coinbase_1.Coinbase.apiClients.wallet.getWallet = (0, utils_1.mockReturnValue)(walletModelWithDefaultAddress);
coinbase_1.Coinbase.apiClients.address = utils_1.addressesApiMock;
coinbase_1.Coinbase.apiClients.address.listAddresses = (0, utils_1.mockReturnValue)(addressListModel);
const wallets = await user.loadWallets();
const wallet = wallets[walletId];
expect(wallet).not.toBeNull();
expect(wallet.getId()).toBe(walletId);
expect(wallet.getDefaultAddress()?.getId()).toBe(addressModel1.address_id);
});
it("throws an error when the backup file is absent", async () => {
fs.unlinkSync(coinbase_1.Coinbase.backupFilePath);
await expect(user.loadWallets()).rejects.toThrow(new errors_1.ArgumentError("Backup file not found"));
});
it("throws an error when the backup file is corrupted", async () => {
fs.writeFileSync(coinbase_1.Coinbase.backupFilePath, JSON.stringify(malformedSeedData, null, 2));
await expect(user.loadWallets()).rejects.toThrow(new errors_1.ArgumentError("Malformed backup data"));
});
it("throws an error when backup does not contain seed", async () => {
fs.writeFileSync(coinbase_1.Coinbase.backupFilePath, JSON.stringify(seedDataWithoutSeed, null, 2));
await expect(user.loadWallets()).rejects.toThrow(new errors_1.ArgumentError("Malformed backup data"));
});
it("throws an error when backup does not contain iv", async () => {
fs.writeFileSync(coinbase_1.Coinbase.backupFilePath, JSON.stringify(seedDataWithoutIv, null, 2));
await expect(user.loadWallets()).rejects.toThrow(new errors_1.ArgumentError("Malformed backup data"));
});
it("throws an error when backup does not contain auth_tag", async () => {
fs.writeFileSync(coinbase_1.Coinbase.backupFilePath, JSON.stringify(seedDataWithoutAuthTag, null, 2));
await expect(user.loadWallets()).rejects.toThrow(new errors_1.ArgumentError("Malformed backup data"));
});
});
describe(".listWallets", () => {

@@ -349,4 +161,4 @@ let user;

});
const wallets = await user.listWallets();
expect(wallets.length).toBe(0);
const result = await user.listWallets();
expect(result.wallets.length).toBe(0);
expect(coinbase_1.Coinbase.apiClients.wallet.listWallets).toHaveBeenCalledTimes(1);

@@ -361,11 +173,12 @@ expect(coinbase_1.Coinbase.apiClients.address.listAddresses).toHaveBeenCalledTimes(0);

has_more: false,
next_page: "",
next_page: "nextPageToken",
total_count: 1,
});
coinbase_1.Coinbase.apiClients.address.listAddresses = (0, utils_1.mockReturnValue)(addressListModel);
const wallets = await user.listWallets();
expect(wallets[0]).toBeInstanceOf(wallet_1.Wallet);
expect(wallets.length).toBe(1);
expect(wallets[0].getId()).toBe(walletId);
expect(wallets[0].listAddresses().length).toBe(2);
const result = await user.listWallets();
expect(result.wallets[0]).toBeInstanceOf(wallet_1.Wallet);
expect(result.wallets.length).toBe(1);
expect(result.wallets[0].getId()).toBe(walletId);
expect(result.wallets[0].listAddresses().length).toBe(2);
expect(result.nextPageToken).toBe("nextPageToken");
expect(coinbase_1.Coinbase.apiClients.wallet.listWallets).toHaveBeenCalledTimes(1);

@@ -385,4 +198,5 @@ expect(coinbase_1.Coinbase.apiClients.address.listAddresses).toHaveBeenCalledTimes(1);

});
coinbase_1.Coinbase.apiClients.address.listAddresses = (0, utils_1.mockReturnValue)(addressListModel);
const [unhydratedWallet] = await user.listWallets();
coinbase_1.Coinbase.apiClients.address.listAddresses = (0, utils_1.mockReturnValue)(mockAddressList);
const result = await user.listWallets();
const unhydratedWallet = result.wallets[0];
expect(unhydratedWallet.canSign()).toBe(false);

@@ -403,4 +217,5 @@ await unhydratedWallet.setSeed(seed);

});
coinbase_1.Coinbase.apiClients.address.listAddresses = (0, utils_1.mockReturnValue)(addressListModel);
const [unhydratedWallet] = await user.listWallets();
coinbase_1.Coinbase.apiClients.address.listAddresses = (0, utils_1.mockReturnValue)(mockAddressList);
const result = await user.listWallets();
const unhydratedWallet = result.wallets[0];
expect(() => unhydratedWallet.export()).toThrow(new errors_1.InternalError("Cannot export Wallet without loaded seed"));

@@ -440,3 +255,4 @@ await expect(unhydratedWallet.createAddress()).rejects.toThrow(errors_1.InternalError);

});
const [wallet] = await user.listWallets();
const result = await user.listWallets();
const wallet = result.wallets[0];
expect(wallet.getId()).toBe(walletId);

@@ -456,2 +272,65 @@ expect(wallet.canSign()).toBe(false);

});
describe(".getWallet", () => {
let user;
let walletId;
let walletModelWithDefaultAddress;
let addressListModel;
beforeEach(() => {
jest.clearAllMocks();
walletId = crypto.randomUUID();
const seed = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
const { address1 } = (0, utils_1.generateWalletFromSeed)(seed);
mockAddressModel = (0, utils_1.newAddressModel)(walletId, address1);
const addressModel1 = (0, utils_1.newAddressModel)(walletId);
const addressModel2 = (0, utils_1.newAddressModel)(walletId);
walletModelWithDefaultAddress = {
id: walletId,
network_id: coinbase_1.Coinbase.networkList.BaseSepolia,
default_address: addressModel1,
};
addressListModel = {
data: [addressModel1, addressModel2],
has_more: false,
next_page: "",
total_count: 1,
};
coinbase_1.Coinbase.apiClients.wallet = utils_1.walletsApiMock;
coinbase_1.Coinbase.apiClients.address = utils_1.addressesApiMock;
const mockUserModel = {
id: "12345",
};
user = new user_1.User(mockUserModel);
});
it("should raise an error when the Wallet API call fails", async () => {
coinbase_1.Coinbase.apiClients.wallet.getWallet = (0, utils_1.mockReturnRejectedValue)(new Error("API Error"));
await expect(user.getWallet(walletId)).rejects.toThrow(new Error("API Error"));
expect(coinbase_1.Coinbase.apiClients.wallet.getWallet).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.wallet.getWallet).toHaveBeenLastCalledWith(walletId);
});
it("should raise an error when the Address API call fails", async () => {
coinbase_1.Coinbase.apiClients.wallet.getWallet = (0, utils_1.mockReturnValue)(walletModelWithDefaultAddress);
coinbase_1.Coinbase.apiClients.address.listAddresses = (0, utils_1.mockReturnRejectedValue)(new Error("API Error"));
await expect(user.getWallet(walletId)).rejects.toThrow(new Error("API Error"));
expect(coinbase_1.Coinbase.apiClients.wallet.getWallet).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.wallet.getWallet).toHaveBeenLastCalledWith(walletId);
expect(coinbase_1.Coinbase.apiClients.address.listAddresses).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.address.listAddresses).toHaveBeenLastCalledWith(walletId);
});
it("should return the Wallet", async () => {
const seed = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
const { address1 } = (0, utils_1.generateWalletFromSeed)(seed);
mockAddressModel = (0, utils_1.newAddressModel)(walletId, address1);
coinbase_1.Coinbase.apiClients.wallet.getWallet = (0, utils_1.mockReturnValue)(walletModelWithDefaultAddress);
coinbase_1.Coinbase.apiClients.address.listAddresses = (0, utils_1.mockReturnValue)(addressListModel);
const result = await user.getWallet(walletId);
expect(result).toBeInstanceOf(wallet_1.Wallet);
expect(result.getId()).toBe(walletId);
expect(result.listAddresses().length).toBe(2);
expect(result.canSign()).toBe(false);
expect(coinbase_1.Coinbase.apiClients.wallet.getWallet).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.address.listAddresses).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.address.listAddresses).toHaveBeenCalledWith(walletId);
expect(coinbase_1.Coinbase.apiClients.wallet.getWallet).toHaveBeenCalledWith(walletId);
});
});
});
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -6,2 +29,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const crypto_1 = __importDefault(require("crypto"));

@@ -16,2 +40,3 @@ const decimal_js_1 = __importDefault(require("decimal.js"));

const wallet_1 = require("../wallet");
const types_1 = require("../types");
const utils_1 = require("./utils");

@@ -45,9 +70,8 @@ describe("Wallet Class", () => {

});
beforeEach(async () => {
coinbase_1.Coinbase.useServerSigner = false;
});
describe(".createTransfer", () => {
let weiAmount, destination, intervalSeconds, timeoutSeconds;
let balanceModel;
const mockProvider = new ethers_1.ethers.JsonRpcProvider("https://sepolia.base.org");
mockProvider.getTransaction = jest.fn();
mockProvider.getTransactionReceipt = jest.fn();
coinbase_1.Coinbase.apiClients.baseSepoliaProvider = mockProvider;
beforeEach(() => {

@@ -78,11 +102,10 @@ const key = ethers_1.ethers.Wallet.createRandom();

});
mockProvider.getTransaction.mockResolvedValueOnce({
blockHash: "0xdeadbeef",
coinbase_1.Coinbase.apiClients.transfer.getTransfer = (0, utils_1.mockReturnValue)({
...utils_1.VALID_TRANSFER_MODEL,
status: types_1.TransferStatus.COMPLETE,
});
mockProvider.getTransactionReceipt.mockResolvedValueOnce({
status: 1,
});
await wallet.createTransfer(weiAmount, coinbase_1.Coinbase.assets.Wei, destination, intervalSeconds, timeoutSeconds);
expect(coinbase_1.Coinbase.apiClients.transfer.createTransfer).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.transfer.broadcastTransfer).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.transfer.getTransfer).toHaveBeenCalledTimes(1);
});

@@ -104,2 +127,6 @@ it("should throw an APIError if the createTransfer API call fails", async () => {

});
coinbase_1.Coinbase.apiClients.transfer.getTransfer = (0, utils_1.mockReturnValue)({
...utils_1.VALID_TRANSFER_MODEL,
status: types_1.TransferStatus.BROADCAST,
});
intervalSeconds = 0.000002;

@@ -113,2 +140,13 @@ timeoutSeconds = 0.000002;

});
it("should successfully create and complete a transfer when using server signer", async () => {
coinbase_1.Coinbase.useServerSigner = true;
coinbase_1.Coinbase.apiClients.transfer.createTransfer = (0, utils_1.mockReturnValue)(utils_1.VALID_TRANSFER_MODEL);
coinbase_1.Coinbase.apiClients.transfer.getTransfer = (0, utils_1.mockReturnValue)({
...utils_1.VALID_TRANSFER_MODEL,
status: types_1.TransferStatus.COMPLETE,
});
await wallet.createTransfer(weiAmount, coinbase_1.Coinbase.assets.Wei, destination, intervalSeconds, timeoutSeconds);
expect(coinbase_1.Coinbase.apiClients.transfer.createTransfer).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.transfer.getTransfer).toHaveBeenCalledTimes(1);
});
afterEach(() => {

@@ -156,2 +194,38 @@ jest.restoreAllMocks();

});
describe("when using a server signer", () => {
let walletId = crypto_1.default.randomUUID();
let wallet;
beforeEach(async () => {
jest.clearAllMocks();
coinbase_1.Coinbase.useServerSigner = true;
});
it("should return a Wallet instance", async () => {
coinbase_1.Coinbase.apiClients.wallet.createWallet = (0, utils_1.mockReturnValue)({
...utils_1.VALID_WALLET_MODEL,
server_signer_status: types_1.ServerSignerStatus.PENDING,
});
coinbase_1.Coinbase.apiClients.wallet.getWallet = (0, utils_1.mockReturnValue)({
...utils_1.VALID_WALLET_MODEL,
server_signer_status: types_1.ServerSignerStatus.ACTIVE,
});
coinbase_1.Coinbase.apiClients.address.createAddress = (0, utils_1.mockReturnValue)((0, utils_1.newAddressModel)(walletId));
wallet = await wallet_1.Wallet.create();
expect(wallet).toBeInstanceOf(wallet_1.Wallet);
expect(wallet.getServerSignerStatus()).toBe(types_1.ServerSignerStatus.ACTIVE);
expect(coinbase_1.Coinbase.apiClients.wallet.createWallet).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.wallet.getWallet).toHaveBeenCalledTimes(2);
expect(coinbase_1.Coinbase.apiClients.address.createAddress).toHaveBeenCalledTimes(1);
});
it("should throw an Error if the Wallet times out waiting on a not active server signer", async () => {
const intervalSeconds = 0.000002;
const timeoutSeconds = 0.000002;
coinbase_1.Coinbase.apiClients.wallet.getWallet = (0, utils_1.mockReturnValue)({
...utils_1.VALID_WALLET_MODEL,
server_signer_status: types_1.ServerSignerStatus.PENDING,
});
await expect(wallet_1.Wallet.create(intervalSeconds, timeoutSeconds)).rejects.toThrow("Wallet creation timed out. Check status of your Server-Signer");
expect(coinbase_1.Coinbase.apiClients.wallet.createWallet).toHaveBeenCalledTimes(1);
expect(coinbase_1.Coinbase.apiClients.wallet.getWallet).toHaveBeenCalled();
});
});
});

@@ -346,2 +420,117 @@ describe(".init", () => {

});
describe(".saveSeed", () => {
const seed = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
let apiPrivateKey;
const filePath = "seeds.json";
let seedWallet;
beforeEach(async () => {
apiPrivateKey = coinbase_1.Coinbase.apiKeyPrivateKey;
coinbase_1.Coinbase.apiKeyPrivateKey = crypto_1.default.generateKeyPairSync("ec", {
namedCurve: "prime256v1",
privateKeyEncoding: { type: "pkcs8", format: "pem" },
publicKeyEncoding: { type: "spki", format: "pem" },
}).privateKey;
fs.writeFileSync(filePath, JSON.stringify({}), "utf8");
seedWallet = await wallet_1.Wallet.init(walletModel, seed);
});
afterEach(async () => {
fs.unlinkSync(filePath);
coinbase_1.Coinbase.apiKeyPrivateKey = apiPrivateKey;
});
it("should save the seed when encryption is false", async () => {
seedWallet.saveSeed(filePath, false);
const storedSeedData = fs.readFileSync(filePath);
const walletSeedData = JSON.parse(storedSeedData.toString());
expect(walletSeedData[walletId].encrypted).toBe(false);
expect(walletSeedData[walletId].iv).toBe("");
expect(walletSeedData[walletId].authTag).toBe("");
expect(walletSeedData[walletId].seed).toBe(seed);
});
it("should save the seed when encryption is true", async () => {
seedWallet.saveSeed(filePath, true);
const storedSeedData = fs.readFileSync(filePath);
const walletSeedData = JSON.parse(storedSeedData.toString());
expect(walletSeedData[walletId].encrypted).toBe(true);
expect(walletSeedData[walletId].iv).not.toBe("");
expect(walletSeedData[walletId].authTag).not.toBe("");
expect(walletSeedData[walletId].seed).not.toBe(seed);
});
it("should throw an error when the wallet is seedless", async () => {
const seedlessWallet = await wallet_1.Wallet.init(walletModel, "", [(0, utils_1.newAddressModel)(walletId)]);
expect(() => seedlessWallet.saveSeed(filePath, false)).toThrow(errors_1.InternalError);
});
});
describe(".loadSeed", () => {
const seed = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
let apiPrivateKey;
const filePath = "seeds.json";
let seedWallet;
let seedlessWallet;
const addressModel = (0, utils_1.newAddressModel)(walletId, "0x919538116b4F25f1CE01429fd9Ed7964556bf565");
beforeEach(async () => {
apiPrivateKey = coinbase_1.Coinbase.apiKeyPrivateKey;
coinbase_1.Coinbase.apiKeyPrivateKey = crypto_1.default.generateKeyPairSync("ec", {
namedCurve: "prime256v1",
privateKeyEncoding: { type: "pkcs8", format: "pem" },
publicKeyEncoding: { type: "spki", format: "pem" },
}).privateKey;
const initialSeedData = {
[walletId]: {
encrypted: false,
iv: "",
authTag: "",
seed,
},
};
fs.writeFileSync(filePath, JSON.stringify(initialSeedData), "utf8");
seedWallet = await wallet_1.Wallet.init(walletModel, seed, [addressModel]);
seedlessWallet = await wallet_1.Wallet.init(walletModel, "", [addressModel]);
});
afterEach(async () => {
fs.unlinkSync(filePath);
coinbase_1.Coinbase.apiKeyPrivateKey = apiPrivateKey;
});
it("loads the seed from the file", async () => {
seedlessWallet.loadSeed(filePath);
expect(seedlessWallet.canSign()).toBe(true);
});
it("loads the encrypted seed from the file", async () => {
seedWallet.saveSeed(filePath, true);
seedlessWallet.loadSeed(filePath);
expect(seedlessWallet.canSign()).toBe(true);
});
it("loads the encrypted seed from the file with multiple seeds", async () => {
seedWallet.saveSeed(filePath, true);
const otherModel = {
id: crypto_1.default.randomUUID(),
network_id: coinbase_1.Coinbase.networkList.BaseSepolia,
};
const otherWallet = await wallet_1.Wallet.init(otherModel);
otherWallet.saveSeed(filePath, true);
seedlessWallet.loadSeed(filePath);
expect(seedlessWallet.canSign()).toBe(true);
});
it("raises an error if the wallet is already hydrated", async () => {
expect(() => seedWallet.loadSeed(filePath)).toThrow(errors_1.InternalError);
});
it("raises an error when file contains different wallet data", async () => {
const otherSeedData = {
[crypto_1.default.randomUUID()]: {
encrypted: false,
iv: "",
authTag: "",
seed,
},
};
fs.writeFileSync(filePath, JSON.stringify(otherSeedData), "utf8");
expect(() => seedlessWallet.loadSeed(filePath)).toThrow(errors_1.ArgumentError);
});
it("raises an error when the file is absent", async () => {
expect(() => seedlessWallet.loadSeed("non-file.json")).toThrow(errors_1.ArgumentError);
});
it("raises an error when the file is corrupted", async () => {
fs.writeFileSync(filePath, "corrupted data", "utf8");
expect(() => seedlessWallet.loadSeed(filePath)).toThrow(errors_1.ArgumentError);
});
});
});

@@ -22,5 +22,6 @@ import { Decimal } from "decimal.js";

setSignedTransaction(transaction: ethers.Transaction): void;
getStatus(): Promise<TransferStatus>;
getStatus(): TransferStatus | undefined;
getTransactionLink(): string;
reload(): Promise<void>;
toString(): Promise<string>;
}

@@ -87,13 +87,15 @@ "use strict";

}
async getStatus() {
const transactionHash = this.getTransactionHash();
if (!transactionHash)
return types_1.TransferStatus.PENDING;
const onchainTransaction = await coinbase_1.Coinbase.apiClients.baseSepoliaProvider.getTransaction(transactionHash);
if (!onchainTransaction)
return types_1.TransferStatus.PENDING;
if (!onchainTransaction.blockHash)
return types_1.TransferStatus.BROADCAST;
const transactionReceipt = await coinbase_1.Coinbase.apiClients.baseSepoliaProvider.getTransactionReceipt(transactionHash);
return transactionReceipt?.status ? types_1.TransferStatus.COMPLETE : types_1.TransferStatus.FAILED;
getStatus() {
switch (this.model.status) {
case types_1.TransferStatus.PENDING:
return types_1.TransferStatus.PENDING;
case types_1.TransferStatus.BROADCAST:
return types_1.TransferStatus.BROADCAST;
case types_1.TransferStatus.COMPLETE:
return types_1.TransferStatus.COMPLETE;
case types_1.TransferStatus.FAILED:
return types_1.TransferStatus.FAILED;
default:
return undefined;
}
}

@@ -103,10 +105,13 @@ getTransactionLink() {

}
async reload() {
const result = await coinbase_1.Coinbase.apiClients.transfer.getTransfer(this.getWalletId(), this.getFromAddressId(), this.getId());
this.model = result?.data;
}
async toString() {
const status = await this.getStatus();
return (`Transfer{transferId: '${this.getId()}', networkId: '${this.getNetworkId()}', ` +
`fromAddressId: '${this.getFromAddressId()}', destinationAddressId: '${this.getDestinationAddressId()}', ` +
`assetId: '${this.getAssetId()}', amount: '${this.getAmount()}', transactionHash: '${this.getTransactionHash()}', ` +
`transactionLink: '${this.getTransactionLink()}', status: '${status}'}`);
`transactionLink: '${this.getTransactionLink()}', status: '${this.getStatus()}'}`);
}
}
exports.Transfer = Transfer;
import { Decimal } from "decimal.js";
import { AxiosPromise, AxiosRequestConfig, RawAxiosRequestConfig } from "axios";
import { ethers } from "ethers";
import { Address as AddressModel, AddressList, AddressBalanceList, Balance, CreateAddressRequest, CreateWalletRequest, BroadcastTransferRequest, CreateTransferRequest, TransferList, User as UserModel, Wallet as WalletModel, Transfer as TransferModel, WalletList } from "./../client/api";

@@ -41,9 +40,8 @@ import { Address } from "./address";

transfer?: TransferAPIClient;
baseSepoliaProvider?: ethers.Provider;
};
export declare enum TransferStatus {
PENDING = "PENDING",
BROADCAST = "BROADCAST",
COMPLETE = "COMPLETE",
FAILED = "FAILED"
PENDING = "pending",
BROADCAST = "broadcast",
COMPLETE = "complete",
FAILED = "failed"
}

@@ -62,1 +60,18 @@ export type WalletData = {

export type Destination = string | Address | Wallet;
export declare enum ServerSignerStatus {
PENDING = "pending_seed_creation",
ACTIVE = "active_seed"
}
export type CoinbaseOptions = {
apiKeyName?: string;
privateKey?: string;
useServerSigner?: boolean;
debugging?: boolean;
basePath?: string;
};
export type CoinbaseConfigureFromJsonOptions = {
filePath: string;
useServerSigner?: boolean;
debugging?: boolean;
basePath?: string;
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransferStatus = void 0;
exports.ServerSignerStatus = exports.TransferStatus = void 0;
var TransferStatus;
(function (TransferStatus) {
TransferStatus["PENDING"] = "PENDING";
TransferStatus["BROADCAST"] = "BROADCAST";
TransferStatus["COMPLETE"] = "COMPLETE";
TransferStatus["FAILED"] = "FAILED";
TransferStatus["PENDING"] = "pending";
TransferStatus["BROADCAST"] = "broadcast";
TransferStatus["COMPLETE"] = "complete";
TransferStatus["FAILED"] = "failed";
})(TransferStatus || (exports.TransferStatus = TransferStatus = {}));
var ServerSignerStatus;
(function (ServerSignerStatus) {
ServerSignerStatus["PENDING"] = "pending_seed_creation";
ServerSignerStatus["ACTIVE"] = "active_seed";
})(ServerSignerStatus || (exports.ServerSignerStatus = ServerSignerStatus = {}));

@@ -9,11 +9,9 @@ import { WalletData } from "./types";

getId(): string;
saveWallet(wallet: Wallet, encrypt?: boolean): Wallet;
listWallets(pageSize?: number, nextPageToken?: string): Promise<Wallet[]>;
loadWallets(): Promise<{
[key: string]: Wallet;
listWallets(pageSize?: number, nextPageToken?: string): Promise<{
wallets: Wallet[];
nextPageToken: string;
}>;
getWallet(wallet_id: string): Promise<Wallet>;
importWallet(data: WalletData): Promise<Wallet>;
private getExistingSeeds;
private storeEncryptionKey;
toString(): string;
}
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.User = void 0;
const fs = __importStar(require("fs"));
const crypto = __importStar(require("crypto"));
const wallet_1 = require("./wallet");
const coinbase_1 = require("./coinbase");
const errors_1 = require("./errors");
class User {

@@ -42,26 +16,2 @@ constructor(user) {

}
saveWallet(wallet, encrypt = false) {
const existingSeedsInStore = this.getExistingSeeds();
const data = wallet.export();
let seedToStore = data.seed;
let authTag = "";
let iv = "";
if (encrypt) {
const ivBytes = crypto.randomBytes(12);
const sharedSecret = this.storeEncryptionKey();
const cipher = crypto.createCipheriv("aes-256-gcm", crypto.createHash("sha256").update(sharedSecret).digest(), ivBytes);
const encryptedData = Buffer.concat([cipher.update(data.seed, "utf8"), cipher.final()]);
authTag = cipher.getAuthTag().toString("hex");
seedToStore = encryptedData.toString("hex");
iv = ivBytes.toString("hex");
}
existingSeedsInStore[data.walletId] = {
seed: seedToStore,
encrypted: encrypt,
authTag: authTag,
iv: iv,
};
fs.writeFileSync(coinbase_1.Coinbase.backupFilePath, JSON.stringify(existingSeedsInStore, null, 2), "utf8");
return wallet;
}
async listWallets(pageSize = 10, nextPageToken) {

@@ -78,34 +28,13 @@ const addressModelMap = {};

}
return Promise.all(walletsModels.map(async (wallet) => {
return await wallet_1.Wallet.init(wallet, "", addressModelMap[wallet.id]);
const wallets = await Promise.all(walletsModels.map(async (wallet) => {
const walletId = wallet.id;
const addressModels = addressModelMap[walletId];
return await wallet_1.Wallet.init(wallet, "", addressModels);
}));
return { wallets: wallets, nextPageToken: walletList.data.next_page };
}
async loadWallets() {
const existingSeedsInStore = this.getExistingSeeds();
if (Object.keys(existingSeedsInStore).length === 0) {
throw new errors_1.ArgumentError("Backup file not found");
}
const wallets = {};
for (const [walletId, seedData] of Object.entries(existingSeedsInStore)) {
let seed = seedData.seed;
if (!seed) {
throw new Error("Malformed backup data");
}
if (seedData.encrypted) {
const sharedSecret = this.storeEncryptionKey();
if (!seedData.iv || !seedData.authTag) {
throw new Error("Malformed encrypted seed data");
}
const decipher = crypto.createDecipheriv("aes-256-gcm", crypto.createHash("sha256").update(sharedSecret).digest(), Buffer.from(seedData.iv, "hex"));
decipher.setAuthTag(Buffer.from(seedData.authTag, "hex"));
const decryptedData = Buffer.concat([
decipher.update(Buffer.from(seed, "hex")),
decipher.final(),
]);
seed = decryptedData.toString("utf8");
}
const data = { walletId, seed };
wallets[walletId] = await this.importWallet(data);
}
return wallets;
async getWallet(wallet_id) {
const walletModel = await coinbase_1.Coinbase.apiClients.wallet.getWallet(wallet_id);
const addressList = await coinbase_1.Coinbase.apiClients.address.listAddresses(wallet_id);
return wallet_1.Wallet.init(walletModel.data, "", addressList.data.data);
}

@@ -117,34 +46,2 @@ async importWallet(data) {

}
getExistingSeeds() {
try {
const data = fs.readFileSync(coinbase_1.Coinbase.backupFilePath, "utf8");
if (!data) {
return {};
}
const seedData = JSON.parse(data);
if (!Object.entries(seedData).every(([key, value]) => typeof key === "string" &&
typeof value.authTag === "string" &&
typeof value.encrypted === "boolean" &&
typeof value.iv === "string" &&
typeof value.seed === "string")) {
throw new errors_1.ArgumentError("Malformed backup data");
}
return seedData;
}
catch (error) {
if (error.code === "ENOENT") {
return {};
}
throw new errors_1.ArgumentError("Malformed backup data");
}
}
storeEncryptionKey() {
const privateKey = crypto.createPrivateKey(coinbase_1.Coinbase.apiKeyPrivateKey);
const publicKey = crypto.createPublicKey(coinbase_1.Coinbase.apiKeyPrivateKey);
const sharedSecret = crypto.diffieHellman({
privateKey,
publicKey,
});
return sharedSecret;
}
toString() {

@@ -151,0 +48,0 @@ return `User{ userId: ${this.model.id} }`;

import { Address as AddressModel, Wallet as WalletModel } from "../client";
import { Address } from "./address";
import { Transfer } from "./transfer";
import { Amount, Destination, WalletData } from "./types";
import { Amount, Destination, WalletData, ServerSignerStatus } from "./types";
import { FaucetTransaction } from "./faucet_transaction";

@@ -18,4 +18,5 @@ import { BalanceMap } from "./balance_map";

private constructor();
static create(): Promise<Wallet>;
static init(model: WalletModel, seed: string | undefined, addressModels?: AddressModel[]): Promise<Wallet>;
static create(intervalSeconds?: number, timeoutSeconds?: number): Promise<Wallet>;
private waitForSigner;
static init(model: WalletModel, seed?: string | undefined, addressModels?: AddressModel[]): Promise<Wallet>;
export(): WalletData;

@@ -30,3 +31,3 @@ private deriveKey;

private cacheAddress;
setSeed(seed: string): Promise<void>;
setSeed(seed: string): void;
getAddress(addressId: string): Address | undefined;

@@ -37,3 +38,6 @@ listAddresses(): Address[];

getNetworkId(): string;
getServerSignerStatus(): ServerSignerStatus | undefined;
getId(): string | undefined;
saveSeed(filePath: string, encrypt?: boolean): string;
loadSeed(filePath: string): string;
getDefaultAddress(): Address | undefined;

@@ -46,2 +50,4 @@ canSign(): boolean;

private static getSeedAndMasterKey;
private getExistingSeeds;
private getEncryptionKey;
}

@@ -33,2 +33,3 @@ "use strict";

const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs"));
const ethers_1 = require("ethers");

@@ -39,2 +40,3 @@ const secp256k1 = __importStar(require("secp256k1"));

const errors_1 = require("./errors");
const types_1 = require("./types");
const utils_1 = require("./utils");

@@ -55,9 +57,13 @@ const balance_map_1 = require("./balance_map");

}
static async create() {
const walletData = await coinbase_1.Coinbase.apiClients.wallet.createWallet({
static async create(intervalSeconds = 0.2, timeoutSeconds = 20) {
const result = await coinbase_1.Coinbase.apiClients.wallet.createWallet({
wallet: {
network_id: coinbase_1.Coinbase.networkList.BaseSepolia,
use_server_signer: coinbase_1.Coinbase.useServerSigner,
},
});
const wallet = await Wallet.init(walletData.data, undefined, []);
const wallet = await Wallet.init(result.data, undefined, []);
if (coinbase_1.Coinbase.useServerSigner) {
await wallet.waitForSigner(wallet.getId(), intervalSeconds, timeoutSeconds);
}
await wallet.createAddress();

@@ -67,4 +73,18 @@ await wallet.reload();

}
async waitForSigner(walletId, intervalSeconds = 0.2, timeoutSeconds = 20) {
const startTime = Date.now();
while (Date.now() - startTime < timeoutSeconds * 1000) {
const response = await coinbase_1.Coinbase.apiClients.wallet.getWallet(walletId);
if (response?.data.server_signer_status === types_1.ServerSignerStatus.ACTIVE) {
return;
}
await (0, utils_1.delay)(intervalSeconds);
}
throw new Error("Wallet creation timed out. Check status of your Server-Signer");
}
static async init(model, seed, addressModels = []) {
this.validateSeedAndAddressModels(seed, addressModels);
if (coinbase_1.Coinbase.useServerSigner) {
return new Wallet(model, undefined, undefined, addressModels);
}
const seedAndMaster = this.getSeedAndMasterKey(seed);

@@ -89,10 +109,13 @@ const wallet = new Wallet(model, seedAndMaster.master, seedAndMaster.seed, addressModels);

async createAddress() {
const hdKey = this.deriveKey();
const attestation = this.createAttestation(hdKey);
const publicKey = (0, utils_1.convertStringToHex)(hdKey.publicKey);
const key = new ethers_1.ethers.Wallet((0, utils_1.convertStringToHex)(hdKey.privateKey));
const payload = {
public_key: publicKey,
attestation: attestation,
};
let payload, key;
if (!coinbase_1.Coinbase.useServerSigner) {
const hdKey = this.deriveKey();
const attestation = this.createAttestation(hdKey);
const publicKey = (0, utils_1.convertStringToHex)(hdKey.publicKey);
key = new ethers_1.ethers.Wallet((0, utils_1.convertStringToHex)(hdKey.privateKey));
payload = {
public_key: publicKey,
attestation: attestation,
};
}
const response = await coinbase_1.Coinbase.apiClients.address.createAddress(this.model.id, payload);

@@ -153,6 +176,18 @@ this.cacheAddress(response.data, key);

}
async setSeed(seed) {
if (this.master === undefined) {
setSeed(seed) {
if (this.master === undefined && (this.seed === undefined || this.seed === "")) {
this.master = bip32_1.HDKey.fromMasterSeed(Buffer.from(seed, "hex"));
this.addresses = [];
this.addressModels.map((addressModel) => {
const derivedKey = this.deriveKey();
const etherKey = new ethers_1.ethers.Wallet((0, utils_1.convertStringToHex)(derivedKey.privateKey));
if (etherKey.address != addressModel.address_id) {
throw new errors_1.InternalError(`Seed does not match wallet; cannot find address ${etherKey.address}`);
}
this.cacheAddress(addressModel, etherKey);
});
}
else {
throw new errors_1.InternalError("Cannot set seed on Wallet with existing seed");
}
}

@@ -182,5 +217,71 @@ getAddress(addressId) {

}
getServerSignerStatus() {
switch (this.model.server_signer_status) {
case types_1.ServerSignerStatus.PENDING:
return types_1.ServerSignerStatus.PENDING;
case types_1.ServerSignerStatus.ACTIVE:
return types_1.ServerSignerStatus.ACTIVE;
default:
return undefined;
}
}
getId() {
return this.model.id;
}
saveSeed(filePath, encrypt = false) {
if (!this.master) {
throw new errors_1.InternalError("Cannot save Wallet without loaded seed");
}
const existingSeedsInStore = this.getExistingSeeds(filePath);
const data = this.export();
let seedToStore = data.seed;
let authTag = "";
let iv = "";
if (encrypt) {
const ivBytes = crypto.randomBytes(12);
const sharedSecret = this.getEncryptionKey();
const cipher = crypto.createCipheriv("aes-256-gcm", crypto.createHash("sha256").update(sharedSecret).digest(), ivBytes);
const encryptedData = Buffer.concat([cipher.update(data.seed, "utf8"), cipher.final()]);
authTag = cipher.getAuthTag().toString("hex");
seedToStore = encryptedData.toString("hex");
iv = ivBytes.toString("hex");
}
existingSeedsInStore[data.walletId] = {
seed: seedToStore,
encrypted: encrypt,
authTag: authTag,
iv: iv,
};
fs.writeFileSync(filePath, JSON.stringify(existingSeedsInStore, null, 2), "utf8");
return `Successfully saved seed for ${data.walletId} to ${filePath}.`;
}
loadSeed(filePath) {
const existingSeedsInStore = this.getExistingSeeds(filePath);
if (Object.keys(existingSeedsInStore).length === 0) {
throw new errors_1.ArgumentError(`File ${filePath} does not contain any seed data`);
}
if (existingSeedsInStore[this.getId()] === undefined) {
throw new errors_1.ArgumentError(`File ${filePath} does not contain seed data for wallet ${this.getId()}`);
}
const seedData = existingSeedsInStore[this.getId()];
let seed = seedData.seed;
if (!seed) {
throw new errors_1.ArgumentError("Seed data is malformed");
}
if (seedData.encrypted) {
const sharedSecret = this.getEncryptionKey();
if (!seedData.iv || !seedData.authTag) {
throw new errors_1.ArgumentError("Encrypted seed data is malformed");
}
const decipher = crypto.createDecipheriv("aes-256-gcm", crypto.createHash("sha256").update(sharedSecret).digest(), Buffer.from(seedData.iv, "hex"));
decipher.setAuthTag(Buffer.from(seedData.authTag, "hex"));
const decryptedData = Buffer.concat([
decipher.update(Buffer.from(seed, "hex")),
decipher.final(),
]);
seed = decryptedData.toString("utf8");
}
this.setSeed(seed);
return `Successfully loaded seed for wallet ${this.getId()} from ${filePath}.`;
}
getDefaultAddress() {

@@ -243,4 +344,36 @@ return this.addresses.find(address => address.getId() === this.model.default_address?.address_id);

}
getExistingSeeds(filePath) {
try {
const data = fs.readFileSync(filePath, "utf8");
if (!data) {
return {};
}
const seedData = JSON.parse(data);
if (!Object.entries(seedData).every(([key, value]) => typeof key === "string" &&
typeof value.authTag === "string" &&
typeof value.encrypted === "boolean" &&
typeof value.iv === "string" &&
typeof value.seed === "string")) {
throw new errors_1.ArgumentError("Malformed backup data");
}
return seedData;
}
catch (error) {
if (error.code === "ENOENT") {
return {};
}
throw new errors_1.ArgumentError("Malformed backup data");
}
}
getEncryptionKey() {
const privateKey = crypto.createPrivateKey(coinbase_1.Coinbase.apiKeyPrivateKey);
const publicKey = crypto.createPublicKey(coinbase_1.Coinbase.apiKeyPrivateKey);
const encryptionKey = crypto.diffieHellman({
privateKey,
publicKey,
});
return encryptionKey;
}
}
exports.Wallet = Wallet;
Wallet.MAX_ADDRESSES = 20;

@@ -7,3 +7,3 @@ {

"repository": "https://github.com/coinbase/coinbase-sdk-nodejs",
"version": "0.0.4",
"version": "0.0.6",
"main": "dist/index.js",

@@ -17,3 +17,5 @@ "types": "dist/index.d.ts",

"check": "tsc --noEmit",
"test": "npx jest --no-cache",
"test": "npx jest --no-cache --testMatch=**/*_test.ts",
"test:dry-run": "npm install && npm ci && npm publish --dry-run",
"test:e2e": "npx jest --no-cache --testMatch=**/e2e.ts",
"clean": "rm -rf dist/*",

@@ -33,2 +35,3 @@ "build": "tsc",

"decimal.js": "^10.4.3",
"dotenv": "^16.4.5",
"ethers": "^6.12.1",

@@ -75,5 +78,4 @@ "node-jose": "^2.2.0",

"verbose": true,
"testRegex": ".test.ts$",
"maxWorkers": 1
}
}

@@ -18,7 +18,32 @@ # Coinbase Node.js SDK

## Requirements
The Coinbase server-side SDK requires Node.js version 18 or higher and npm version 9.7.2 or higher. To view your currently installed versions of Node.js, run the following from the command-line:
```bash
node -v
npm -v
```
We recommend installing and managing Node.js and npm versions with `nvm`. See [Installing and Updating](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating) in the `nvm` README for instructions on how to install `nvm`.
Once `nvm` has been installed, you can install and use the latest versions of Node.js and npm by running the following commands:
```bash
nvm install node # "node" is an alias for the latest version
nvm use node
```
## Installation
### In Your Node.js Project
Optional: Initialize the npm
This command initializes a new npm project with default settings and configures it to use ES modules by setting the type field to "module" in the package.json file.
```bash
npm init -y; npm pkg set type="module"
```
#### You can import the SDK as follows
```bash
npm install @coinbase/coinbase-sdk

@@ -33,17 +58,19 @@ ```

### In the ts-node REPL
## Usage
After running `npx ts-node` to start the REPL, you can import the SDK as follows:
### Initialization
```typescript
import { Coinbase } from "@coinbase/coinbase-sdk";
```
#### You can import the SDK as follows:
### Requirements
CommonJs:
- Node.js 18 or higher
```javascript
const { Coinbase } = require("@coinbase/coinbase-sdk");
```
## Usage
ES modules:
### Initialization
```typescript
import { Coinbase } from "@coinbase/coinbase-sdk";
```

@@ -55,11 +82,16 @@ To start, [create a CDP API Key](https://portal.cdp.coinbase.com/access/api). Then, initialize the Platform SDK by passing your API Key name and API Key's private key via the `Coinbase` constructor:

const apiKeyPrivateKey = "Copy your API Key's private key here.";
const privateKey = "Copy your API Key's private key here.";
const coinbase = new Coinbase(apiKeyName, apiKeyPrivateKey);
const coinbase = new Coinbase({ apiKeyName: apiKeyName, privateKey: privateKey });
```
If you are using a CDP Server-Signer to manage your private keys, enable it with the constuctor option:
```typescript
const coinbase = new Coinbase({ apiKeyName: apiKeyName, privateKey: apiKeyPrivateKey, useServerSigner: true })
```
Another way to initialize the SDK is by sourcing the API key from the json file that contains your API key, downloaded from CDP portal.
```typescript
const coinbase = Coinbase.configureFromJson("path/to/your/api-key.json");
const coinbase = Coinbase.configureFromJson({ filePath: "path/to/your/api-key.json" });
```

@@ -69,4 +101,19 @@

CommonJs:
```javascript
const { Coinbase } = require("@coinbase/coinbase-sdk");
const coinbase = Coinbase.configureFromJson("path/to/your/api-key.json");
coinbase.getDefaultUser().then(user => {
console.log(user);
});
```
Or using ES modules and async/await:
```typescript
import { Coinbase } from "@coinbase/coinbase-sdk";
const coinbase = Coinbase.configureFromJson("path/to/your/api-key.json");
const user = await coinbase.getDefaultUser();
console.log(user);
```

@@ -125,12 +172,12 @@

For convenience during testing, we provide a `saveWallet` method that stores the Wallet data in your local file system. This is an insecure method of storing wallet seeds and should only be used for development purposes.
For convenience during testing, we provide a `saveSeed` method that stores the wallet's seed in your local file system. This is an insecure method of storing wallet seeds and should only be used for development purposes.
```typescript
user.saveWallet(wallet);
wallet.saveSeed(wallet);
```
To encrypt the saved data, set encrypt to true. Note that your CDP API key also serves as the encryption key for the data persisted locally. To re-instantiate wallets with encrypted data, ensure that your SDK is configured with the same API key when invoking `saveWallet` and `loadWallets`.
To encrypt the saved data, set encrypt to true. Note that your CDP API key also serves as the encryption key for the data persisted locally. To re-instantiate wallets with encrypted data, ensure that your SDK is configured with the same API key when invoking `saveSeed` and `loadSeed`.
```typescript
user.saveWallet(wallet, true);
wallet.saveSeed(wallet, true);
```

@@ -145,8 +192,8 @@

To import Wallets that were persisted to your local file system using `saveWallet`, use the below code.
To import Wallets that were persisted to your local file system using `saveSeed`, use the below code.
```typescript
// The Wallet can be re-instantiated using the exported data.
const wallets = await user.loadWallets();
const reinitWallet = wallets[wallet.getId()];
const w = await user.getWallet(w.getId());
w.loadSeed(filePath);
```

@@ -201,9 +248,6 @@

```
To run e2e tests, run:
### REPL
The repository is equipped with a REPL to allow developers to play with the SDK. To start it, run:
```bash
npx ts-node
npm run test:dry-run && NAME="placeholder" PRIVATE_KEY="placeholder" WALLET_DATA="placeholder" && npm run test:e2e
```

@@ -210,0 +254,0 @@

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc