Skip to content

Commit

Permalink
[PSDK-117] Implement Transfer Class
Browse files Browse the repository at this point in the history
  • Loading branch information
John-peterson-coinbase committed May 14, 2024
1 parent 2007a99 commit ee98266
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/coinbase/coinbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import { InvalidAPIKeyFormat, InternalError, InvalidConfiguration } from "./erro
export class Coinbase {
apiClients: ApiClients = {};

/**
* Represents the number of Wei per Ether.
*/
static readonly WEI_PER_ETHER: bigint = BigInt("1000000000000000000");

/**
* Initializes the Coinbase SDK.
* @constructor
Expand Down
19 changes: 19 additions & 0 deletions src/coinbase/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,22 @@ export class InvalidConfiguration extends Error {
}
}
}

/**
* InvalidUnsignedPayload error is thrown when the unsigned payload is invalid.
*/
export class InvalidUnsignedPayload extends Error {
static DEFAULT_MESSAGE = "Invalid unsigned payload";

/**
* Initializes a new InvalidUnsignedPayload instance.
* @param message - The error message.
*/
constructor(message: string = InvalidUnsignedPayload.DEFAULT_MESSAGE) {
super(message);
this.name = "InvalidUnsignedPayload";
if (Error.captureStackTrace) {
Error.captureStackTrace(this, InvalidUnsignedPayload);
}
}
}
160 changes: 160 additions & 0 deletions src/coinbase/transfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { ApiClients } from "./types";
import { Coinbase } from "./coinbase";
import { Transfer as TransferModel } from "../client/api";
import { ethers } from "ethers";
import { InvalidUnsignedPayload } from "./errors";

/**
* A representation of a Transfer, which moves an amount of an Asset from
* a user-controlled Wallet to another Address. The fee is assumed to be paid
* in the native Asset of the Network.
*/
export class Transfer {
private model: TransferModel;
private client: ApiClients;
private transaction?: ethers.Transaction;

/**
* Initializes a new Transfer instance.
* @param {TransferModel} transferModel - The Transfer model.
* @param {ApiClients} client - The API clients.
*/
constructor(transferModel: TransferModel, client: ApiClients) {
this.model = transferModel;
this.client = client;
}

/**
* Returns the ID of the Transfer.
* @returns {string} The Transfer ID.
*/
public getId(): string {
return this.model.transfer_id;
}

/**
* Returns the Network ID of the Transfer.
* @returns {string} The Network ID.
*/
public getNetworkId(): string {
return this.model.network_id;
}

/**
* Returns the Wallet ID of the Transfer.
* @returns {string} The Wallet ID.
*/
public getWalletId(): string {
return this.model.wallet_id;
}

/**
* Returns the From Address ID of the Transfer.
* @returns {string} The From Address ID.
*/
public getFromAddressId(): string {
return this.model.address_id;
}

/**
* Returns the Destination Address ID of the Transfer.
* @returns {string} The Destination Address ID.
*/
public getDestinationAddressId(): string {
return this.model.destination;
}

/**
* Returns the Asset ID of the Transfer.
* @returns {string} The Asset ID.
*/
public getAssetId(): string {
return this.model.asset_id;
}

/**
* Returns the Amount of the Transfer.
* @returns {string} The Amount of the Asset.
*/
public getAmount(): bigint {
const amount = BigInt(this.model.amount);

if (this.getAssetId() === "ETH") {
return amount / Coinbase.WEI_PER_ETHER;
}
return BigInt(this.model.amount);
}

/**
* Returns the Unsigned Payload of the Transfer.
* @returns {string} The Unsigned Payload as a Hex string.
*/
public getUnsignedPayload(): string {
return this.model.unsigned_payload;
}

/**
* Returns the Signed Payload of the Transfer.
* @returns {string | undefined} The Signed Payload as a Hex string, or undefined if not yet available.
*/
public getSignedPayload(): string | undefined {
return this.model.signed_payload;
}

/**
* Returns the Transaction Hash of the Transfer.
* @returns {string | undefined} The Transaction Hash as a Hex string, or undefined if not yet available
*/
public getTransactionhash(): string | undefined {
return this.model.transaction_hash;
}

/**
* Returns the Transaction of the Transfer.
* @returns {ethers.Transaction} The ethers.js Transaction object.
* @throws (InvalidUnsignedPayload) If the unsigned payload is invalid.
*/
public getTransaction(): ethers.Transaction {
if (this.transaction) return this.transaction;

const transaction = new ethers.Transaction();

const rawPayload = this.getUnsignedPayload()
.match(/../g)
?.map(byte => parseInt(byte, 16));
if (!rawPayload) {
throw new InvalidUnsignedPayload("Unable to parse unsigned payload");
}

const rawPayloadBytes = new Uint8Array(rawPayload);

const decoder = new TextDecoder();

let parsedPayload;
try {
parsedPayload = JSON.parse(decoder.decode(rawPayloadBytes));
} catch (error) {
throw new InvalidUnsignedPayload("Unable to decode unsigned payload JSON");
}

transaction.chainId = BigInt(parsedPayload["chainId"]);
transaction.nonce = BigInt(parsedPayload["nonce"]);
transaction.maxPriorityFeePerGas = BigInt(parsedPayload["maxPriorityFeePerGas"]);
transaction.maxFeePerGas = BigInt(parsedPayload["maxFeePerGas"]);
transaction.gasLimit = BigInt(parsedPayload["gas"]);
transaction.to = parsedPayload["to"];
transaction.value = BigInt(parsedPayload["value"]);
transaction.data = parsedPayload["input"];

this.transaction = transaction;
return transaction;
}

/**
* Sets the signed Transaction of the Transfer.
* @param {ethers.Transaction} transaction - The signed Transaction.
*/
public setSignedTransaction(transaction: ethers.Transaction): void {
this.transaction = transaction;
}
}
2 changes: 1 addition & 1 deletion src/coinbase/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class User {

/**
* Initializes a new User instance.
* @param {UserModel} user - The user model.
* @param {UserModel} user - The User model.
* @param {ApiClients} client - The API clients.
*/
constructor(user: UserModel, client: ApiClients) {
Expand Down

0 comments on commit ee98266

Please sign in to comment.