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

Add currency abstraction #2707

Merged
merged 15 commits into from
May 25, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions packages/grid_client/scripts/tft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { CurrencyModel, HourlyTFTModel } from "../src";
import { getClient } from "./client_loader";
import { log } from "./utils";

async function convertTFTtoUSD(client, amount) {
const res = await client.tft.convertTFTtoUSD(amount);
log("================= Convert TFT =================");
log(res);
log("================= Convert TFT =================");
}

async function convertUSDtoTFT(client, amount) {
const res = await client.tft.convertUSDtoTFT(amount);
log("================= Convert USD =================");
log(res);
log("================= Convert USD =================");
}

async function monthlyTFT(client, hourlyTFT) {
const res = await client.tft.monthlyTFT(hourlyTFT);
log("================= Monthly TFT =================");
log(res);
log("================= Monthly TFT =================");
}

async function yearlyTFT(client, hourlyTFT) {
const res = await client.tft.yearlyTFT(hourlyTFT);
log("================= Yearly TFT =================");
log(res);
log("================= Yearly TFT =================");
}
async function main() {
const grid = await getClient();

const amount: CurrencyModel = {
amount: 5,
};

const hourlyTFT: HourlyTFTModel = {
amount: 5,
};

await convertTFTtoUSD(grid, amount);
await convertUSDtoTFT(grid, amount);
await monthlyTFT(grid, hourlyTFT);
await yearlyTFT(grid, hourlyTFT);

await grid.disconnect();
}

main();
1 change: 1 addition & 0 deletions packages/grid_client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class GridClient {
stellar: modules.stellar;
blockchain: modules.blockchain;
calculator: modules.calculator;
tft: modules.tft;
utility: modules.utility;
farmerbot: modules.farmerbot;
farms: modules.farms;
Expand Down
12 changes: 6 additions & 6 deletions packages/grid_client/src/helpers/validator.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ValidationError } from "@threefold/types";
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";
import { validateSync } from "class-validator";

async function validateObject(obj) {
const errors = await validate(obj);
function validateObject(obj) {
const errors = validateSync(obj);
// errors is an array of validation errors
if (errors.length > 0) {
console.log("Validation failed. errors:", errors);
Expand All @@ -13,13 +13,13 @@ async function validateObject(obj) {
// used as decorator
function validateInput(target, propertyKey: string, descriptor: PropertyDescriptor) {
const method = descriptor.value;
descriptor.value = async function (...args) {
descriptor.value = function (...args) {
const types = Reflect.getMetadata("design:paramtypes", target, propertyKey);
for (let i = 0; i < args.length; i++) {
const input = plainToInstance(types[i], args[i], { excludeExtraneousValues: true });
await validateObject(input);
validateObject(input);
}
return await method.apply(this, args);
return method.apply(this, args);
};
}

Expand Down
1 change: 1 addition & 0 deletions packages/grid_client/src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export * from "./farmerbot";
export * from "./farms";
export * from "./networks";
export * from "./bridge";
export * from "./tft";
export * from "./base";
10 changes: 10 additions & 0 deletions packages/grid_client/src/modules/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,14 @@ class GetActiveContractsModel {
@Expose() @IsInt() @IsNotEmpty() @Min(1) nodeId: number;
}

class CurrencyModel {
@Expose() @IsNumber() @IsNotEmpty() @Min(0) amount: number;
}

class HourlyTFTModel {
@Expose() @IsNumber() @IsNotEmpty() @Min(0) amount: number;
}

interface GPUCardInfo {
id: string;
contract: number;
Expand Down Expand Up @@ -977,4 +985,6 @@ export {
NodeCPUTest,
NodeIPValidation,
NodeIPerf,
CurrencyModel,
HourlyTFTModel,
};
46 changes: 46 additions & 0 deletions packages/grid_client/src/modules/tft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Decimal from "decimal.js";

import { TFClient } from "..";
import { GridClientConfig } from "../config";
import { expose, validateInput } from "../helpers";
import { CurrencyModel, HourlyTFTModel } from "./models";

class TFTUSDConversionService {
private client: TFClient;
private decimals = 2;

constructor(public config: GridClientConfig) {
this.client = config.tfclient;
}

async price() {
return await this.client.tftPrice.get();
}

@expose
@validateInput
async convertUSDtoTFT(options: CurrencyModel): Promise<string> {
return new Decimal(options.amount / (await this.price())).toFixed(this.decimals);
}

@expose
@validateInput
async convertTFTtoUSD(options: CurrencyModel): Promise<string> {
return new Decimal(options.amount * (await this.price())).toFixed(this.decimals);
}
@expose
@validateInput
monthlyTFT(options: HourlyTFTModel): string {
return new Decimal(options.amount * 24 * 30).toFixed(this.decimals);
}

@expose
@validateInput
yearlyTFT(options: HourlyTFTModel): string {
const months = this.monthlyTFT(options);

return new Decimal(+months * 12).toFixed(this.decimals);
}
}

export { TFTUSDConversionService as tft };
88 changes: 88 additions & 0 deletions packages/grid_client/tests/modules/tft.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { ValidationError } from "@threefold/types";
import Decimal from "decimal.js";

import { type GridClient, tft as TFT } from "../../src";
import { getClient } from "../client_loader";

jest.setTimeout(300000);

const mock_price = jest.fn().mockResolvedValue(0.2);

let grid: GridClient;

beforeAll(async () => {
grid = await getClient();

grid.tft.price = mock_price;
});

afterAll(async () => {
jest.clearAllMocks();
await grid.disconnect();
});

describe("Testing TFT module", () => {
test("tft module to be instance of TFTUSDConversionService", async () => {
expect(await grid.tft).toBeInstanceOf(TFT);
});

test("should convert to the correct value based on the mocked price.", async () => {
const result = await grid.tft.convertTFTtoUSD({ amount: 5 });

expect(mock_price).toHaveBeenCalled();
expect(typeof result).toBe("string");
expect(result).toBe(new Decimal(5 * 0.2).toFixed(2));
});

test("convertTFTtoUSD function to throw if passed a negative value.", async () => {
const result = async () => await grid.tft.convertTFTtoUSD({ amount: -1 });

await expect(result).rejects.toThrow();
await expect(result).rejects.toBeInstanceOf(ValidationError);
});

test("convertUSDtoTFT function returns a valid value.", async () => {
const usd = 1;
const result = await grid.tft.convertUSDtoTFT({ amount: usd });

expect(typeof result).toBe("string");
expect(result).toEqual(new Decimal(1 / 0.2).toFixed(2));
});

test("convertUSDtoTFT function to throw if passed a negative value.", async () => {
const result = async () => await grid.tft.convertUSDtoTFT({ amount: -1 });

await expect(result).rejects.toThrow();
await expect(result).rejects.toBeInstanceOf(ValidationError);
});

test("monthlyTFT function returns a valid value.", async () => {
const tfts = 1;
const result = await grid.tft.monthlyTFT({ amount: tfts });
const expected_result = new Decimal(tfts * 24 * 30).toFixed(2);

expect(result).toBe(expected_result);
});

test("monthlyTFT function throws if passed anything other than a positive value.", async () => {
const result = async () => await grid.tft.monthlyTFT({ amount: -1 });

await expect(result).rejects.toThrow();
await expect(result).rejects.toBeInstanceOf(ValidationError);
});

test("yearlyTFT function returns a valid value.", async () => {
const tfts = 1;
const result = await grid.tft.yearlyTFT({ amount: tfts });
const expected_result = new Decimal(+(await grid.tft.monthlyTFT({ amount: tfts })) * 12).toFixed(2);

expect(result).toBe(expected_result);
});

test("yearlyTFT function throws if passed anything other than a positive value.", async () => {
const result = async () => grid.tft.yearlyTFT({ amount: -1 });

await expect(result).rejects.toThrow();
await expect(result).rejects.toBeInstanceOf(ValidationError);
});
});
Loading