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

Adding interfaces to relevant classes #277

Closed
wants to merge 12 commits into from
40 changes: 39 additions & 1 deletion src/coinbase/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,48 @@ import { StakingReward } from "./staking_reward";
import { StakingBalance } from "./staking_balance";
import { Transaction } from "./transaction";

export interface IAddress {
getNetworkId(): string;
getId(): string;
listBalances(): Promise<BalanceMap>;
getBalance(assetId: string): Promise<Decimal>;
listHistoricalBalances(
options: ListHistoricalBalancesOptions,
): Promise<ListHistoricalBalancesResult>;
listTransactions(options: ListTransactionsOptions): Promise<ListTransactionsResult>;
stakingRewards(
assetId: string,
startTime: string,
endTime: string,
format: StakingRewardFormat,
): Promise<StakingReward[]>;
historicalStakingBalances(
assetId: string,
startTime: string,
endTime: string,
): Promise<StakingBalance[]>;
stakeableBalance(
assetId: string,
mode: StakeOptionsMode,
options: { [key: string]: string },
): Promise<Decimal>;
unstakeableBalance(
assetId: string,
mode: StakeOptionsMode,
options: { [key: string]: string },
): Promise<Decimal>;
claimableBalance(
assetId: string,
mode: StakeOptionsMode,
options: { [key: string]: string },
): Promise<Decimal>;
faucet(assetId?: string): Promise<FaucetTransaction>;
}

/**
* A representation of a blockchain address, which is a user-controlled account on a network.
*/
export class Address {
export class Address implements IAddress {
private static MAX_HISTORICAL_BALANCE = 1000;

protected networkId: string;
Expand Down
27 changes: 24 additions & 3 deletions src/coinbase/address/external_address.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
import { Address } from "../address";
import { Address, IAddress } from "../address";
import { Amount, StakeOptionsMode } from "../types";
import { Coinbase } from "../coinbase";
import Decimal from "decimal.js";
import { Asset } from "../asset";
import { StakingOperation } from "../staking_operation";
import { IStakingOperation, StakingOperation } from "../staking_operation";

export interface IExternalAddress extends IAddress {
buildStakeOperation(
amount: Amount,
assetId: string,
mode: StakeOptionsMode,
options: { [key: string]: string },
): Promise<IStakingOperation>;
buildUnstakeOperation(
amount: Amount,
assetId: string,
mode: StakeOptionsMode,
options: { [key: string]: string },
): Promise<IStakingOperation>;
buildClaimStakeOperation(
amount: Amount,
assetId: string,
mode: StakeOptionsMode,
options: { [key: string]: string },
): Promise<IStakingOperation>;
}

/**
* A representation of a blockchain Address, which is a user-controlled account on a Network. Addresses are used to
* send and receive Assets. An ExternalAddress is an Address that is not controlled by the developer, but is instead
* controlled by the user.
*/
export class ExternalAddress extends Address {
export class ExternalAddress extends Address implements IExternalAddress {
/**
* Builds a stake operation for the supplied asset. The stake operation
* may take a few minutes to complete in the case when infrastructure is spun up.
Expand Down
76 changes: 61 additions & 15 deletions src/coinbase/address/wallet_address.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Decimal } from "decimal.js";
import { ethers } from "ethers";
import { Address as AddressModel, SmartContractType } from "../../client";
import { Address } from "../address";
import { Address, IAddress } from "../address";
import { Asset } from "../asset";
import { Coinbase } from "../coinbase";
import { ArgumentError } from "../errors";
import { Trade } from "../trade";
import { Transfer } from "../transfer";
import { ContractInvocation } from "../contract_invocation";
import { ITrade, Trade } from "../trade";
import { ITransfer, Transfer } from "../transfer";
import { ContractInvocation, IContractInvocation } from "../contract_invocation";
import {
Amount,
CreateTransferOptions,
Expand All @@ -21,14 +21,56 @@ import {
} from "../types";
import { delay } from "../utils";
import { Wallet as WalletClass } from "../wallet";
import { StakingOperation } from "../staking_operation";
import { PayloadSignature } from "../payload_signature";
import { SmartContract } from "../smart_contract";
import { IStakingOperation, StakingOperation } from "../staking_operation";
import { IPayloadSignature, PayloadSignature } from "../payload_signature";
import { ISmartContract, SmartContract } from "../smart_contract";

export interface IWalletAddress extends IAddress {
getWalletId(): string;
setKey(key: ethers.Wallet): void;
export(): string;
canSign(): boolean;
listTrades(): Promise<ITrade[]>;
listTransfers(): Promise<ITransfer[]>;
createTransfer(options: CreateTransferOptions): Promise<ITransfer>;
createTrade(options: CreateTradeOptions): Promise<ITrade>;
invokeContract(options: CreateContractInvocationOptions): Promise<IContractInvocation>;
deployToken(options: CreateERC20Options): Promise<ISmartContract>;
deployNFT(options: CreateERC721Options): Promise<ISmartContract>;
deployMultiToken(options: CreateERC1155Options): Promise<ISmartContract>;
createStake(
amount: Amount,
assetId: string,
mode: StakeOptionsMode,
options: { [key: string]: string },
timeoutSeconds: number,
intervalSeconds: number,
): Promise<IStakingOperation>;
createUnstake(
amount: Amount,
assetId: string,
mode: StakeOptionsMode,
options: { [key: string]: string },
timeoutSeconds: number,
intervalSeconds: number,
): Promise<IStakingOperation>;
createClaimStake(
amount: Amount,
assetId: string,
mode: StakeOptionsMode,
options: { [key: string]: string },
timeoutSeconds: number,
intervalSeconds: number,
): Promise<IStakingOperation>;
createPayloadSignature(payload: string): Promise<IPayloadSignature>;
getPayloadSignature(signatureId: string): Promise<IPayloadSignature>;
listPayloadSignatures(): Promise<IPayloadSignature[]>;
}

/**
* A representation of a blockchain address, which is a wallet-controlled account on a network.
*/
export class WalletAddress extends Address {
export class WalletAddress extends Address implements IWalletAddress {
private model: AddressModel;
private key?: ethers.Wallet;

Expand Down Expand Up @@ -139,8 +181,8 @@ export class WalletAddress extends Address {
*
* @returns The list of transfers.
*/
public async listTransfers(): Promise<Transfer[]> {
const transfers: Transfer[] = [];
public async listTransfers(): Promise<ITransfer[]> {
const transfers: ITransfer[] = [];
const queue: string[] = [""];

while (queue.length > 0) {
Expand Down Expand Up @@ -186,7 +228,7 @@ export class WalletAddress extends Address {
assetId,
destination,
gasless = false,
}: CreateTransferOptions): Promise<Transfer> {
}: CreateTransferOptions): Promise<ITransfer> {
if (!Coinbase.useServerSigner && !this.key) {
throw new Error("Cannot transfer from address without private key loaded");
}
Expand Down Expand Up @@ -727,13 +769,17 @@ export class WalletAddress extends Address {
if (typeof destination !== "string" && destination.getNetworkId() !== this.getNetworkId()) {
throw new ArgumentError("Transfer must be on the same Network");
}
if (destination instanceof WalletClass) {
return [(await destination.getDefaultAddress()).getId(), destination.getNetworkId()];
if (typeof destination === "string") {
return [destination, this.getNetworkId()];
}
if ("getDefaultAddress" in destination) {
const defaultAddress = await destination.getDefaultAddress();
return [defaultAddress.getId(), destination.getNetworkId()];
}
if (destination instanceof Address) {
if ("getId" in destination) {
return [destination.getId(), destination.getNetworkId()];
}
return [destination, this.getNetworkId()];
throw new ArgumentError("Invalid destination type");
}

/**
Expand Down
29 changes: 20 additions & 9 deletions src/coinbase/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@ import { Coinbase } from "./coinbase";
import { GWEI_DECIMALS } from "./constants";
import { ArgumentError } from "./errors";

export interface IAsset {
networkId: string;
assetId: string;
contractAddress: string;
decimals: number;
primaryDenomination(): string;
toAtomicAmount(wholeAmount: Decimal): bigint;
fromAtomicAmount(atomicAmount: Decimal): Decimal;
getAssetId(): string;
}

/** A representation of an Asset. */
export class Asset {
export class Asset implements IAsset {
public readonly networkId: string;
public readonly assetId: string;
public readonly contractAddress: string;
Expand Down Expand Up @@ -128,20 +139,20 @@ export class Asset {
}

/**
* Returns a string representation of the Asset.
* Returns the Asset ID.
*
* @returns a string representation of the Asset
* @returns The Asset ID.
*/
toString(): string {
return `Asset{ networkId: ${this.networkId}, assetId: ${this.assetId}, contractAddress: ${this.contractAddress}, decimals: ${this.decimals} }`;
getAssetId(): string {
return this.assetId;
}

/**
* Returns the Asset ID.
* Returns a string representation of the Asset.
*
* @returns The Asset ID.
* @returns a string representation of the Asset
*/
getAssetId(): string {
return this.assetId;
toString(): string {
return `Asset{ networkId: ${this.networkId}, assetId: ${this.assetId}, contractAddress: ${this.contractAddress}, decimals: ${this.decimals} }`;
}
}
10 changes: 9 additions & 1 deletion src/coinbase/authenticator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ import { version } from "../../package.json";
const pemHeader = "-----BEGIN EC PRIVATE KEY-----";
const pemFooter = "-----END EC PRIVATE KEY-----";

export interface IAuthenticator {
authenticateRequest(
config: InternalAxiosRequestConfig,
debugging?: boolean,
): Promise<InternalAxiosRequestConfig>;
buildJWT(url: string, method?: string): Promise<string>;
}

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

Expand Down
8 changes: 7 additions & 1 deletion src/coinbase/balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ import Decimal from "decimal.js";
import { Balance as BalanceModel } from "../client";
import { Asset } from "./asset";

export interface IBalance {
amount: Decimal;
assetId: string;
asset?: Asset;
}

/** A representation of a balance. */
export class Balance {
export class Balance implements IBalance {
public readonly amount: Decimal;
public readonly assetId: string;
public readonly asset?: Asset;
Expand Down
6 changes: 3 additions & 3 deletions src/coinbase/balance_map.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Balance } from "./balance";
import { Balance, IBalance } from "./balance";
import { Balance as BalanceModel } from "../client";
import { Decimal } from "decimal.js";

Expand All @@ -24,9 +24,9 @@ export class BalanceMap extends Map<string, Decimal> {
/**
* Adds a balance to the map.
*
* @param {Balance} balance - The balance to add to the map.
* @param {IBalance} balance - The balance to add to the map.
*/
public add(balance: Balance): void {
public add(balance: IBalance): void {
if (!(balance instanceof Balance)) {
throw new Error("balance must be a Balance");
}
Expand Down
19 changes: 18 additions & 1 deletion src/coinbase/contract_event.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import { ContractEvent as ContractEventModel } from "../client";

export interface IContractEvent {
networkId(): string;
protocolName(): string;
contractName(): string;
eventName(): string;
sig(): string;
fourBytes(): string;
contractAddress(): string;
blockTime(): Date;
blockHeight(): number;
txHash(): string;
txIndex(): number;
eventIndex(): number;
data(): string;
}

/**
* A representation of a single contract event.
*/
export class ContractEvent {
export class ContractEvent implements IContractEvent {
private model: ContractEventModel;

/**
Expand Down
31 changes: 29 additions & 2 deletions src/coinbase/contract_invocation.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
import { Decimal } from "decimal.js";
import { TransactionStatus } from "./types";
import { Transaction } from "./transaction";
import { ITransaction, Transaction } from "./transaction";
import { Coinbase } from "./coinbase";
import { ContractInvocation as ContractInvocationModel } from "../client/api";
import { ethers } from "ethers";
import { delay } from "./utils";
import { TimeoutError } from "./errors";

export interface IContractInvocation {
getId(): string;
getNetworkId(): string;
getWalletId(): string;
getFromAddressId(): string;
getContractAddressId(): string;
getMethod(): string;
getArgs(): object;
getAbi(): object | undefined;
getAmount(): Decimal;
getTransactionHash(): string | undefined;
getRawTransaction(): ethers.Transaction;
sign(key: ethers.Wallet): Promise<string>;
getStatus(): TransactionStatus | undefined;
getTransaction(): ITransaction;
getTransactionLink(): string;
broadcast(): Promise<IContractInvocation>;
wait({
intervalSeconds,
timeoutSeconds,
}: {
intervalSeconds: number;
timeoutSeconds: number;
}): Promise<ContractInvocation>;
reload(): Promise<void>;
}

/**
* A representation of a ContractInvocation, which calls a smart contract method
* onchain. The fee is assumed to be paid in the native Asset of the Network.
*/
export class ContractInvocation {
export class ContractInvocation implements IContractInvocation {
private model: ContractInvocationModel;

/**
Expand Down
Loading
Loading