Skip to content

Commit

Permalink
Merge pull request #44 from MVPWorkshop/fix/gettokenlist
Browse files Browse the repository at this point in the history
Fix/gettokenlist
  • Loading branch information
kaliman93 authored Feb 21, 2023
2 parents 549b3c3 + 2fb1e08 commit be1eecb
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 138 deletions.
40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ const result = await theaSDK.recover.recoverNFT(tokenId, tokenAmount);
}
```

- Query recover fungible - Returns list of token amounts needed to recover amount of specified NFT (by tokenID).

```js
// call to unwrap method of Registry contract
const result = await theaSDK.recover.queryRecoverFungibles(tokenId, tokenAmount);

// Sample output
{
"cbt": "100000000", // current base token amount
"sdg": '100000000', // sdg tokent amount
"vintage": '100000000', // vintage token amount
"rating": '100000000' // rating token amount
}
```

## Roll Tokens module

- Roll base tokens - Rolls `tokenAmount` of Base Token based on their `vintage`. This means that old Base Tokens (defined by sent `vintage`) and vintage tokens are burned and the same amount(`tokenAmount`) of new Base Tokens (defined by `vintage + 1`) is beeing minted. These Base Tokens addresses are defined in BaseTokenManager contract under `baseTokens` mapping with `vintage` as key.
Expand Down Expand Up @@ -212,19 +227,18 @@ theaSDK.setCurrentNBTContractAddress("0x123...");
```js
const priceInWEI = await theaSDK.nftTokenList.getTokenList();

// Sample output
[
{
"projectID": 1748,
"vintages": [
{
"vintage": 2018,
"tokenID": "1",
...
}
]
}
]
// Sample output
// (Object which keys are projectIDs and values are arrays of tokens that belong to that project)
{
"1784": // proojectId
[ // list of tokens in that project
{
"vintage": 2018,
"tokenID": "1",
...
}
]
}
```

- Query Orders Info - Returns a list of orders for a given tokenID and owner
Expand Down
50 changes: 42 additions & 8 deletions src/modules/NFTtrading/getTokenList.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,54 @@
import { TheaNetwork, TokenListResponsePayload, TokenResponseFromRaribleAPI } from "src/types";
import { consts, TOKEN_LIST_FETCHING_URL } from "src/utils/consts";
import { GraphqlQuery, QueryError, QueryErrorResponse, QueryResponse, TheaNetwork, TokenInfo } from "src/types";
import { TheaSubgraphError } from "src/utils";
import { consts } from "src/utils/consts";
import { HttpClient } from "../shared";

export const tokenInfo: GraphqlQuery = {
query: `{
tokens(orderBy: projectId) {
id
tokenURI
projectId
vintage
activeAmount
mintedAmount
retiredAmount
unwrappedAmount
}
}`
};
export class GetTokenList {
readonly httpClient: HttpClient;
readonly network: TheaNetwork;

constructor(network: TheaNetwork) {
this.network = network;
this.httpClient = new HttpClient(TOKEN_LIST_FETCHING_URL);
this.httpClient = new HttpClient(consts[`${network}`].subGraphUrl);
}
async getTokenList() {
const response = await this.httpClient.get<TokenListResponsePayload>("/items/byCollection", {
collection: consts[`${this.network}`].networkName + ":" + consts[`${this.network}`].theaERC1155Contract
});
const tokenIdsList = response.items.map((item: TokenResponseFromRaribleAPI) => item.tokenId);
return tokenIdsList;
const response = await this.httpClient.post<
GraphqlQuery,
QueryResponse<{ tokens: TokenInfo[] }> | QueryErrorResponse
>("", tokenInfo);

const tokens = this.handleResponse<{ tokens: TokenInfo[] }, TokenInfo[]>(response, "tokens");
return tokens.reduce((acc, token: TokenInfo) => {
const projectId = token.projectId;
if (acc[`${projectId}`]) {
acc[`${projectId}`].push(token);
} else {
acc[`${projectId}`] = [token];
}
return acc;
}, {} as Record<string, TokenInfo[]>);
}
private handleResponse<T, Response>(
response: QueryResponse<T> | QueryErrorResponse,
responseProperty: keyof T
): Response {
if ("errors" in response) throw new TheaSubgraphError("Subgraph call error", response.errors as QueryError[]);

// eslint-disable-next-line security/detect-object-injection
return response.data[responseProperty] as Response;
}
}
15 changes: 10 additions & 5 deletions src/modules/recover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,18 @@ export class Recover extends ContractWrapper<IBaseTokenManagerContract> {
async queryRecoverFungibles(tokenId: BigNumberish, amount: BigNumberish): Promise<BaseTokenAmounts> {
const baseTokenCharactaristics = await this.contract.baseCharacteristics();
const btAmount = await this.calculateBaseTokensAmounts(tokenId, amount, baseTokenCharactaristics);
return btAmount;
return {
cbt: btAmount.cbt.toString(),
sdg: btAmount.sdg.toString(),
vintage: btAmount.vintage.toString(),
rating: btAmount.rating.toString()
};
}

async checkBalancesForAllBaseTokens(btAmount: BaseTokenAmounts) {
await checkBalance(this.providerOrSigner as Signer, this.network, {
token: "ERC20",
amount: btAmount.btVintage,
amount: btAmount.cbt,
tokenName: "CurrentNBT"
});
await checkBalance(this.providerOrSigner as Signer, this.network, {
Expand All @@ -86,7 +91,7 @@ export class Recover extends ContractWrapper<IBaseTokenManagerContract> {
await approve(this.providerOrSigner as Signer, this.network, {
token: "ERC20",
spender,
amount: btAmount.btVintage,
amount: btAmount.cbt,
tokenName: "CurrentNBT"
});

Expand Down Expand Up @@ -117,15 +122,15 @@ export class Recover extends ContractWrapper<IBaseTokenManagerContract> {
id: BigNumberish,
amount: BigNumberish,
baseTokenCharactaristics: BaseTokenCharactaristics
): Promise<{ btVintage: BigNumber; sdg: BigNumber; vintage: BigNumber; rating: BigNumber }> {
): Promise<{ cbt: BigNumber; sdg: BigNumber; vintage: BigNumber; rating: BigNumber }> {
const unitAmount = BigNumber.from(amount).mul(RATE_VCC_TO_BT);
const { sdgValue, vintageValue, ratingValue } = await this.getFeatureValue(id);

const sdgAmount = BigNumber.from(unitAmount).mul(sdgValue.sub(baseTokenCharactaristics.sdgsCount));
const vintageAmount = BigNumber.from(unitAmount).mul(vintageValue.sub(baseTokenCharactaristics.vintage));
const ratingAmount = BigNumber.from(unitAmount).mul(ratingValue.sub(baseTokenCharactaristics.rating));

return { btVintage: unitAmount, sdg: sdgAmount, vintage: vintageAmount, rating: ratingAmount };
return { cbt: unitAmount, sdg: sdgAmount, vintage: vintageAmount, rating: ratingAmount };
}

async getFeatureValue(
Expand Down
59 changes: 8 additions & 51 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,54 +154,10 @@ export type BaseTokenCharactaristics = {
};

export type BaseTokenAmounts = {
btVintage: BigNumber;
sdg: BigNumber;
vintage: BigNumber;
rating: BigNumber;
};

export type TokenListResponsePayload = {
continuation: string;
items: TokenResponseFromRaribleAPI[];
};
export type TokenResponseFromRaribleAPI = {
id: string;
blockchain: string;
collection: string;
contract: string;
tokenId: string;
creators: [{ account: string; value: number }];
lazySupply: string;
pending: [];
mintedAt: string;
lastUpdatedAt: string;
supply: string;
meta: {
name: string;
description: string;
tags: [];
genres: [];
attributes: [{ key: string; value: string }];
content: [
{
"@type": "IMAGE";
url: string;
representation: string;
mimeType: string;
size: number;
width: number;
height: number;
}
];
restrictions: [];
};
deleted: true;
originOrders: [];
ammOrders: { ids: [] };
auctions: [];
totalStock: string;
sellers: number;
suspicious: boolean;
cbt: BigNumberish;
sdg: BigNumberish;
vintage: BigNumberish;
rating: BigNumberish;
};

export type Co2DataSet = {
Expand Down Expand Up @@ -249,8 +205,7 @@ export type TokenizationHistory = {
projectId: string;
vintage: string;
};

export type TokenizationStats = {
export type TokenInfo = {
id: string;
unwrappedAmount: string;
vintage: string;
Expand All @@ -259,7 +214,9 @@ export type TokenizationStats = {
projectId: string;
retiredAmount: string;
tokenURI: string;
} | null;
};

export type TokenizationStats = TokenInfo | null;

export type OffsetHistory = {
id: string;
Expand Down
1 change: 0 additions & 1 deletion src/utils/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { TheaNetwork } from "src/types";

export const RATE_VCC_TO_BT = 10;
export const STABLE_TOKEN_DECIMALS_MULTIPLIER = 10 ** 18;
export const TOKEN_LIST_FETCHING_URL = "https://api.rarible.org/v0.1";
export const ORDERBOOK_URL = "https://api.trader.xyz/orderbook";
export const DEFAULT_SLIPPAGE_TOLERANCE = 0.5;
export const INFINITE_EXPIRATION_TIMESTAMP_SEC = BigNumber.from(2524604400);
Expand Down
80 changes: 36 additions & 44 deletions test/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import {
TokenizationSource,
TokenizationState,
TokenizationStats,
TokenizationStatus,
TokenListResponsePayload
TokenizationStatus
} from "../src";

export const PRIVATE_KEY = "5b5354654516fb598d5c51594e0b50c8f1e1fac0b27424b6251b3e6fd96f4207";
Expand Down Expand Up @@ -58,50 +57,43 @@ export const tokenizationState: TokenizationState = {
error: null,
errorMessage: null
};
export const itemsByCollection: TokenListResponsePayload = {
continuation: "1669946173376_ETHEREUM:0xd07dc4262bcdbf85190c01c996b4c06a461d2430:654210",
items: [
{
id: "ETHEREUM:0xd07dc4262bcdbf85190c01c996b4c06a461d2430:463956",
blockchain: "ETHEREUM",
collection: "ETHEREUM:0xd07dc4262bcdbf85190c01c996b4c06a461d2430",
contract: "ETHEREUM:0xd07dc4262bcdbf85190c01c996b4c06a461d2430",
tokenId: "463956",
creators: [{ account: "ETHEREUM:0x6f87176de6060e004f0181bd913cd22b9428fd94", value: 10000 }],
lazySupply: "0",
pending: [],
mintedAt: "2021-04-10T15:17:56Z",
lastUpdatedAt: "2023-01-15T16:02:53.453Z",
supply: "0",
meta: {
name: "Freestyle",
description: "Freestyle - Digital Graffiti",
tags: [],
genres: [],
attributes: [{ key: "Laruad", value: "Laruad" }],
content: [
{
"@type": "IMAGE",
url: "https://ipfs.io/ipfs/QmdThej2WVTPdKWapfgmQYWZZuFRRz4fqK2BFHHRhT5Qvh/image.jpeg",
representation: "ORIGINAL",
mimeType: "image/jpeg",
size: 157565,
width: 650,
height: 460
}
],
restrictions: []
export const subgraphResponse = {
data: {
tokens: [
{
id: "1",
tokenURI: "1.json",
projectId: "1748",
vintage: "2019",
activeAmount: "100000",
mintedAmount: "100000",
retiredAmount: "0",
unwrappedAmount: "0"
},
deleted: true,
originOrders: [],
ammOrders: { ids: [] },
auctions: [],
totalStock: "0",
sellers: 0,
suspicious: false
}
]
{
id: "2",
tokenURI: "1.json",
projectId: "1749",
vintage: "2019",
activeAmount: "100000",
mintedAmount: "100000",
retiredAmount: "0",
unwrappedAmount: "0"
},
{
id: "3",
tokenURI: "1.json",
projectId: "1749",
vintage: "2019",
activeAmount: "100000",
mintedAmount: "100000",
retiredAmount: "0",
unwrappedAmount: "0"
}
]
}
};

export const postOrderResponseMock = {
erc20Token: "0xd393b1e02da9831ff419e22ea105aae4c47e1253",
erc20TokenAmount: "12000000000000000000",
Expand Down
27 changes: 20 additions & 7 deletions test/modules/NFTtrading/getTokenList.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,40 @@
import { TheaNetwork, GetTokenList, consts } from "../../../src";
import { itemsByCollection } from "../../mocks";
import { TheaNetwork, GetTokenList, tokenInfo, TheaSubgraphError, QueryError } from "../../../src";
import { subgraphResponse } from "../../mocks";

jest.mock("../../../src/modules/shared/httpClient", () => {
return {
HttpClient: jest.fn().mockImplementation(() => {
return {
get: jest.fn().mockReturnValue(itemsByCollection)
post: jest.fn().mockReturnValue(subgraphResponse)
};
})
};
});

describe("GetTokenList", () => {
const getTokenList: GetTokenList = new GetTokenList(TheaNetwork.GANACHE);
const httpGetSpy = jest.spyOn(getTokenList.httpClient, "get");
const httpPostSpy = jest.spyOn(getTokenList.httpClient, "post");

it("should throw error with list of query errors", async () => {
const expectedResult = { errors: [{ error: "indexing_error" }] };
jest.spyOn(getTokenList.httpClient, "post").mockResolvedValueOnce(expectedResult);

await expect(getTokenList.getTokenList()).rejects.toThrow(
new TheaSubgraphError("Subgraph call error", [{ error: "indexing_error" }] as QueryError[])
);
});

describe("getTokenList", () => {
it("should return a token ID list", async () => {
const result = await getTokenList.getTokenList();
expect(result).toEqual([itemsByCollection.items[0].tokenId]);
expect(httpGetSpy).toBeCalledWith("/items/byCollection", {
collection: consts[TheaNetwork.GANACHE].networkName + ":" + consts[TheaNetwork.GANACHE].theaERC1155Contract
expect(result).toEqual({
[`${subgraphResponse.data.tokens[0].projectId}`]: [subgraphResponse.data.tokens[0]],
[`${subgraphResponse.data.tokens[1].projectId}`]: [
subgraphResponse.data.tokens[1],
subgraphResponse.data.tokens[2]
]
});
expect(httpPostSpy).toBeCalledWith("", tokenInfo);
});
});
});
Loading

0 comments on commit be1eecb

Please sign in to comment.