diff --git a/src/init/thea.ts b/src/init/thea.ts index be73134..5919129 100644 --- a/src/init/thea.ts +++ b/src/init/thea.ts @@ -1,7 +1,15 @@ import { Signer } from "@ethersproject/abstract-signer"; import { Provider, Web3Provider } from "@ethersproject/providers"; import { Wallet } from "@ethersproject/wallet"; -import { Convert, GetCharacteristicsBytes, Recover, Tokenization, Unwrap } from "../modules"; +import { + Convert, + GetCharacteristicsBytes, + GetTokenList, + QueryPriceListing, + Recover, + Tokenization, + Unwrap +} from "../modules"; import { TheaNetwork, ProviderOrSigner } from "../types"; import { TheaError } from "../utils"; @@ -19,12 +27,16 @@ export class TheaSDK { readonly tokenization: Tokenization; readonly convert: Convert; readonly recover: Recover; + readonly nftTokenList: GetTokenList; + readonly nftQueryPriceListing: QueryPriceListing; private constructor(readonly providerOrSigner: ProviderOrSigner, readonly network: TheaNetwork) { this.unwrap = new Unwrap(this.providerOrSigner, network); this.convert = new Convert(this.providerOrSigner, network); const registry = new GetCharacteristicsBytes(this.providerOrSigner, network); this.recover = new Recover(this.providerOrSigner, network, registry); + this.nftTokenList = new GetTokenList(network); + this.nftQueryPriceListing = new QueryPriceListing(network); } /** diff --git a/src/modules/NFTtrading/getTokenList.ts b/src/modules/NFTtrading/getTokenList.ts new file mode 100644 index 0000000..355dbfb --- /dev/null +++ b/src/modules/NFTtrading/getTokenList.ts @@ -0,0 +1,27 @@ +import { TheaNetwork, TokenListResponsePayload } from "src/types"; +import { consts } from "src/utils/consts"; +import { HttpClient } from "../shared"; + +export class GetTokenList { + readonly httpClient: HttpClient; + readonly network: TheaNetwork; + + constructor(network: TheaNetwork) { + this.network = network; + this.httpClient = new HttpClient("https://api.rarible.org/v0.1"); + } + async getTokenList() { + const response = await this.httpClient.get("/items/byCollection", { + collection: consts[`${this.network}`].networkName + ":" + consts[`${this.network}`].theaERC1155Contract + }); + // eslint-disable-next-line no-var + var tokenIdsList: string[] = []; + if (response.items.length > 0) { + response.items.forEach((item) => { + tokenIdsList.push(item.tokenId); + }); + } + + return tokenIdsList; + } +} diff --git a/src/modules/NFTtrading/queryPriceListing copy.ts b/src/modules/NFTtrading/queryPriceListing copy.ts new file mode 100644 index 0000000..550e97e --- /dev/null +++ b/src/modules/NFTtrading/queryPriceListing copy.ts @@ -0,0 +1,74 @@ +import { + OrderSide, + SearchOrdersParams, + // PostOrderRequestPayload, + // PostOrderResponsePayload, + SearchOrdersResponsePayload, + // SignedERC1155OrderStruct, + // SignedERC1155OrderStructSerialized, + TheaNetwork +} from "src/types"; +import { consts } from "src/utils/consts"; +import { HttpClient } from "../shared/httpClient"; + +export class QueryPriceListing { + readonly orderBook: HttpClient; + readonly network: TheaNetwork; + + constructor(network: TheaNetwork) { + this.network = network; + this.orderBook = new HttpClient("https://api.trader.xyz/orderbook"); + } + // private async postOrderToOrderbook( + // signedOrder: SignedERC1155OrderStruct, + // chainId: string | number, + // metadata: Record = {} + // ) { + // const payload: PostOrderRequestPayload = { + // order: this.serializeNftOrder(signedOrder), + // chainId: chainId.toString(10), + // metadata + // }; + // try { + // const response = await this.orderBook.post("/orders", JSON.stringify(payload)); + // return response; + // } catch (error) { + // throw new TheaError(error.message); + // } + // } + async queryPriceListing(tokenId: string, side: OrderSide) { + const response = await this.orderBook.get("/orders", { + nftToken: consts[`${this.network}`].theaERC1155Contract, + nftTokenId: tokenId, + sellOrBuyNft: side, + status: "open", + erc20Token: consts[`${this.network}`].stableCoinContract + } as Partial); + const priceList: number[] = []; + response.orders.forEach((element) => { + priceList.push(parseInt(element.nftTokenAmount) / (parseInt(element.erc20TokenAmount) / 10 ** 18)); + }); + return priceList; + } + // private serializeNftOrder = (signedOrder: SignedERC1155OrderStruct): SignedERC1155OrderStructSerialized => { + // if ("erc1155Token" in signedOrder) { + // return { + // ...signedOrder, + // direction: parseInt(signedOrder.direction.toString()), + // expiry: signedOrder.expiry.toString(), + // nonce: signedOrder.nonce.toString(), + // erc20TokenAmount: signedOrder.erc20TokenAmount.toString(), + // fees: signedOrder.fees.map((fee) => ({ + // ...fee, + // amount: fee.amount.toString(), + // feeData: fee.feeData.toString() + // })), + // erc1155TokenAmount: signedOrder.erc1155TokenAmount.toString(), + // erc1155TokenId: signedOrder.erc1155TokenId.toString() + // }; + // } else { + // console.log("unknown order format type erc1155", signedOrder); + // throw new TheaError({ message: "Unknown asset type", type: "NFT_ORDER_SERILIZATION_ERROR" }); + // } + // }; +} diff --git a/src/modules/NFTtrading/queryPriceListing.ts b/src/modules/NFTtrading/queryPriceListing.ts new file mode 100644 index 0000000..5be2b7d --- /dev/null +++ b/src/modules/NFTtrading/queryPriceListing.ts @@ -0,0 +1,28 @@ +import { OrderSide, SearchOrdersParams, SearchOrdersResponsePayload, TheaNetwork } from "src/types"; +import { consts } from "src/utils/consts"; +import { HttpClient } from "../shared/httpClient"; + +export class QueryPriceListing { + readonly orderBook: HttpClient; + readonly network: TheaNetwork; + + constructor(network: TheaNetwork) { + this.network = network; + this.orderBook = new HttpClient("https://api.trader.xyz/orderbook"); + } + + async queryPriceListing(tokenId: string, side: OrderSide) { + const response = await this.orderBook.get("/orders", { + nftToken: consts[`${this.network}`].theaERC1155Contract, + nftTokenId: tokenId, + sellOrBuyNft: side, + status: "open", + erc20Token: consts[`${this.network}`].stableCoinContract + } as Partial); + const priceList: number[] = []; + response.orders.forEach((element) => { + priceList.push(parseInt(element.nftTokenAmount) / (parseInt(element.erc20TokenAmount) / 10 ** 18)); + }); + return priceList; + } +} diff --git a/src/modules/index.ts b/src/modules/index.ts index 57202f3..1448f9a 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -4,3 +4,5 @@ export * from "./recover"; export * from "./shared"; export * from "./tokenization"; export * from "./getCharacteristicsBytes"; +export * from "./NFTtrading/getTokenList"; +export * from "./NFTtrading/queryPriceListing"; diff --git a/src/modules/shared/httpClient.ts b/src/modules/shared/httpClient.ts index 8f02641..c9fbded 100644 --- a/src/modules/shared/httpClient.ts +++ b/src/modules/shared/httpClient.ts @@ -1,12 +1,11 @@ import axios, { AxiosInstance } from "axios"; -import { TheaNetwork } from "../../types"; -import { consts, TheaAPICallError } from "../../utils"; +import { TheaAPICallError } from "../../utils"; export class HttpClient { readonly client: AxiosInstance; - constructor(network: TheaNetwork) { + constructor(url: string) { this.client = axios.create({ - baseURL: consts[`${network}`].theaApiBaseUrl, + baseURL: url, headers: { "Content-Type": "application/json" } @@ -26,7 +25,8 @@ export class HttpClient { } } - async get(path: string, params?: Record): Promise { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async get(path: string, params?: Record): Promise { try { const response = await this.client.get(path, { params }); return response.data; diff --git a/src/modules/tokenization.ts b/src/modules/tokenization.ts index a144423..74cf54b 100644 --- a/src/modules/tokenization.ts +++ b/src/modules/tokenization.ts @@ -1,11 +1,11 @@ import { ClientDetails, TheaNetwork, TokenizationRequest, TokenizationSource, TokenizationState } from "../types"; -import { TheaError, validateAddress } from "../utils"; +import { consts, TheaError, validateAddress } from "../utils"; import { HttpClient } from "./shared"; export class Tokenization { readonly httpClient: HttpClient; constructor(network: TheaNetwork) { - this.httpClient = new HttpClient(network); + this.httpClient = new HttpClient(consts[`${network}`].theaApiBaseUrl); } /** diff --git a/src/types/index.ts b/src/types/index.ts index 2730476..6b09733 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ import { Signer } from "@ethersproject/abstract-signer"; -import { BigNumber } from "@ethersproject/bignumber"; +import { BigNumber, BigNumberish } from "@ethersproject/bignumber"; import { Overrides } from "@ethersproject/contracts"; import { Provider } from "@ethersproject/providers"; @@ -131,6 +131,167 @@ export type BaseTokenAmounts = { rating: BigNumber; }; +export interface PostOrderRequestPayload { + order: SignedERC1155OrderStructSerialized; + chainId: string; + metadata?: Record; +} + +export type PropertyStructSerialized = { + propertyValidator: string; + propertyData: string | Array; +}; + +export type FeeStructSerialized = { + recipient: string; + amount: string; + feeData: string; +}; +export type TokenListResponsePayload = { + continuation: string; + items: TokenResponseFromRaribleAPI[]; +}; +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; +}; +export interface PostOrderResponsePayload { + erc20Token: string; + erc20TokenAmount: string; + nftToken: string; + nftTokenId: string; + nftTokenAmount: string; + nftType: string; + sellOrBuyNft: "buy" | "sell"; + chainId: string; + order: SignedERC1155OrderStructSerialized; + orderStatus: OrderStatus; + metadata: Record | null; +} +export type OrderStatus = { + status: null | string; + transactionHash: null | string; + blockNumber: null | string; +}; +export type OrderSide = "buy" | "sell"; +export type ERC1155OrderStruct = { + direction: BigNumberish; + maker: string; + taker: string; + expiry: BigNumberish; + nonce: BigNumberish; + erc20Token: string; + erc20TokenAmount: BigNumberish; + fees: FeeStruct[]; + erc1155Token: string; + erc1155TokenId: BigNumberish; + erc1155TokenProperties: PropertyStruct[]; + erc1155TokenAmount: BigNumberish; +}; +export type FeeStruct = { + recipient: string; + amount: BigNumberish; + feeData: string | Array; +}; +export type PropertyStruct = { + propertyValidator: string; + propertyData: string | Array; +}; +export interface SignedERC1155OrderStructSerialized extends ERC1155OrderStructSerialized { + signature: SignatureStructSerialized; +} + +export interface SignedERC1155OrderStruct extends ERC1155OrderStruct { + signature: SignatureStruct; +} + +export type SignatureStruct = { + signatureType: number; // 2 for EIP-712 + v: number; + r: string; + s: string; +}; +export type SignatureStructSerialized = { + signatureType: number; // 2 for EIP-712 + v: number; + r: string; + s: string; +}; + +export type ERC1155OrderStructSerialized = { + direction: number; + maker: string; + taker: string; + expiry: string; + nonce: string; + erc20Token: string; + erc20TokenAmount: string; + fees: FeeStructSerialized[]; + erc1155Token: string; + erc1155TokenId: string; + erc1155TokenProperties: PropertyStructSerialized[]; + erc1155TokenAmount: string; +}; +export interface SearchOrdersResponsePayload { + orders: Array; +} + +/** + * Available query parameters for searching the orderbook + */ +export interface SearchOrdersParams { + nftTokenId: string | string[]; + erc20Token: string | string[]; + nftToken: string | string[]; + nftType: "ERC721" | "ERC1155"; + chainId: string | number | string[] | number[]; + maker: string; + taker: string; + nonce: string | string[]; + offset: string | number; + limit: string | number; + sellOrBuyNft: "sell" | "buy"; + direction: "0" | "1"; + // Defaults to only 'open' orders + status: "open" | "filled" | "expired" | "cancelled" | "all"; + visibility: "public" | "private"; + valid: "valid" | "all"; +} export * from "./IRegistryContract"; export * from "./IBaseTokenManagerContract"; export * from "./IERC1155Contract"; diff --git a/src/utils/consts.ts b/src/utils/consts.ts index 6b2ba02..9248091 100644 --- a/src/utils/consts.ts +++ b/src/utils/consts.ts @@ -9,6 +9,7 @@ export enum Events { } export type EnvConfig = { + networkName: string; registryContract: string; theaERC1155Contract: string; vintageTokenContract: string; @@ -17,10 +18,12 @@ export type EnvConfig = { baseTokenManagerContract: string; baseTokenManagerDeployerContract: string; theaApiBaseUrl: string; + stableCoinContract: string; }; export const consts: { [key in TheaNetwork]: EnvConfig } = { [TheaNetwork.GANACHE]: { + networkName: "GANACHE", registryContract: "0x88449Dd0a1b75BC607A1E971b13930617D535EC1", theaERC1155Contract: "0x0CFB090683Ea58B740F583c348Ff8730A82f3F64", vintageTokenContract: "0x3621027715647B69D706636a8878E85d725A2aed", @@ -28,9 +31,11 @@ export const consts: { [key in TheaNetwork]: EnvConfig } = { ratingTokenContract: "0xc95347BD5212148A09c34a7d890D061D73f50bb8", baseTokenManagerContract: "0xE100c4ffFF7c00253BA4A2a695F5ac909d756D76", baseTokenManagerDeployerContract: "0x3ace09bba3b8507681146252d3dd33cd4e2d4f63", + stableCoinContract: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", //DAI theaApiBaseUrl: "http://127.0.0.1:8078/cli" }, [TheaNetwork.GOERLI]: { + networkName: "GOERLI", registryContract: "0x88449Dd0a1b75BC607A1E971b13930617D535EC1", theaERC1155Contract: "0x0CFB090683Ea58B740F583c348Ff8730A82f3F64", vintageTokenContract: "0x3621027715647B69D706636a8878E85d725A2aed", @@ -38,16 +43,19 @@ export const consts: { [key in TheaNetwork]: EnvConfig } = { ratingTokenContract: "0xc95347BD5212148A09c34a7d890D061D73f50bb8", baseTokenManagerContract: "0xE100c4ffFF7c00253BA4A2a695F5ac909d756D76", baseTokenManagerDeployerContract: "0x3ace09bba3b8507681146252d3dd33cd4e2d4f63", + stableCoinContract: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", //DAI theaApiBaseUrl: "http://127.0.0.1:8078/cli" }, [TheaNetwork.MAINNET]: { + networkName: "ETHEREUM", registryContract: "0x88449Dd0a1b75BC607A1E971b13930617D535EC1", - theaERC1155Contract: "0x0CFB090683Ea58B740F583c348Ff8730A82f3F64", + theaERC1155Contract: "0x2953399124f0cbb46d2cbacd8a89cf0599974963", vintageTokenContract: "0x3621027715647B69D706636a8878E85d725A2aed", sdgTokenContract: "0xB48C895039c9F81C87eb97Ed54B69a769b291f28", ratingTokenContract: "0xc95347BD5212148A09c34a7d890D061D73f50bb8", baseTokenManagerContract: "0xE100c4ffFF7c00253BA4A2a695F5ac909d756D76", baseTokenManagerDeployerContract: "0x3ace09bba3b8507681146252d3dd33cd4e2d4f63", + stableCoinContract: "0xa6fa4fb5f76172d178d61b04b0ecd319c5d1c0aa", //DAI theaApiBaseUrl: "http://127.0.0.1:8078/cli" } }; diff --git a/src/utils/theaError.ts b/src/utils/theaError.ts index f598c86..df299e5 100644 --- a/src/utils/theaError.ts +++ b/src/utils/theaError.ts @@ -15,6 +15,7 @@ export type ErrorType = | "API_CALL_ERROR" | "INVALID_TOKENIZATION_ID_FORMAT" | "INVALID_BATCH_ID_FORMAT" + | "NFT_ORDER_SERILIZATION_ERROR" | "INVALID_EMAIL_FORMAT"; export type ErrorProps = { diff --git a/test/mocks.ts b/test/mocks.ts index 7264020..ff4d8f2 100644 --- a/test/mocks.ts +++ b/test/mocks.ts @@ -1,4 +1,10 @@ -import { TokenizationSource, TokenizationState, TokenizationStatus } from "../src"; +import { + SearchOrdersResponsePayload, + TokenizationSource, + TokenizationState, + TokenizationStatus, + TokenListResponsePayload +} from "../src"; export const PRIVATE_KEY = "5b5354654516fb598d5c51594e0b50c8f1e1fac0b27424b6251b3e6fd96f4207"; export const WALLET_ADDRESS = "0xE63CCe5bEBF27CFa751de8A1550692d3B12b7B7a"; @@ -46,3 +52,88 @@ 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: [] + }, + deleted: true, + originOrders: [], + ammOrders: { ids: [] }, + auctions: [], + totalStock: "0", + sellers: 0, + suspicious: false + } + ] +}; + +export const priceListingMock: SearchOrdersResponsePayload = { + orders: [ + { + erc20Token: "0x5d29011d843b0b1760c43e10d66f302174bccd1a", + erc20TokenAmount: "10000000000000000000", + nftToken: "0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb", + nftTokenId: "69", + nftTokenAmount: "100", + nftType: "ERC721", + sellOrBuyNft: "buy", + chainId: "1337", + order: { + direction: 1, + erc20Token: "0x5d29011d843b0b1760c43e10d66f302174bccd1a", + erc20TokenAmount: "10000000000000000000", + erc1155Token: "0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb", + erc1155TokenId: "69", + erc1155TokenAmount: "100", + erc1155TokenProperties: [], + expiry: "2524604400", + fees: [], + maker: "0x9342a65736a2e9c6a84a2adaba55ad1dc1f3a418", + nonce: "100131415900000000000000000000000000000096685863241593142117280893798097702934", + signature: { + r: "0x39728a3bef397db69c6c6e1409ae6756c567a989894ad0787f9561113c9a80e9", + s: "0x5f8a25be83efa2326e6405c68e8bdf5c0e83894dbef7e31de39d8c073302a1f6", + v: 28, + signatureType: 2 + }, + taker: "0x0000000000000000000000000000000000000000" + }, + orderStatus: { + status: null, + transactionHash: null, + blockNumber: null + }, + metadata: {} + } + ] +}; diff --git a/test/modules/NFTtrading/getTokenList.spec.ts b/test/modules/NFTtrading/getTokenList.spec.ts new file mode 100644 index 0000000..af2c163 --- /dev/null +++ b/test/modules/NFTtrading/getTokenList.spec.ts @@ -0,0 +1,27 @@ +import { TheaNetwork, GetTokenList, consts } from "../../../src"; +import { itemsByCollection } from "../../mocks"; + +jest.mock("../../../src/modules/shared/httpClient", () => { + return { + HttpClient: jest.fn().mockImplementation(() => { + return { + get: jest.fn().mockReturnValue(itemsByCollection) + }; + }) + }; +}); + +describe("Tokenization", () => { + const getTokenList: GetTokenList = new GetTokenList(TheaNetwork.GANACHE); + const httpGetSpy = jest.spyOn(getTokenList.httpClient, "get"); + + describe("getTokenList", () => { + it("should return a token 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 + }); + }); + }); +}); diff --git a/test/modules/NFTtrading/queryPriceListing.spec.ts b/test/modules/NFTtrading/queryPriceListing.spec.ts new file mode 100644 index 0000000..86d69f6 --- /dev/null +++ b/test/modules/NFTtrading/queryPriceListing.spec.ts @@ -0,0 +1,33 @@ +import { TheaNetwork, QueryPriceListing, consts, SearchOrdersParams } from "../../../src"; +import { priceListingMock } from "../../mocks"; + +jest.mock("../../../src/modules/shared/httpClient", () => { + return { + HttpClient: jest.fn().mockImplementation(() => { + return { + get: jest.fn().mockReturnValue(priceListingMock) + }; + }) + }; +}); + +describe("Tokenization", () => { + const queryPriceListing: QueryPriceListing = new QueryPriceListing(TheaNetwork.GANACHE); + const httpGetSpy = jest.spyOn(queryPriceListing.orderBook, "get"); + + describe("getTokenList", () => { + it("should return a price listing for specific token ID", async () => { + const tokenId = priceListingMock.orders[0].nftTokenId; + const side = "buy"; + const result = await queryPriceListing.queryPriceListing(tokenId, side); + expect(result).toEqual([10]); + expect(httpGetSpy).toBeCalledWith("/orders", { + nftToken: consts[TheaNetwork.GANACHE].theaERC1155Contract, + nftTokenId: tokenId, + sellOrBuyNft: side, + status: "open", + erc20Token: consts[TheaNetwork.GANACHE].stableCoinContract + } as Partial); + }); + }); +}); diff --git a/test/modules/shared/httpClient.spec.ts b/test/modules/shared/httpClient.spec.ts index c905a18..828876d 100644 --- a/test/modules/shared/httpClient.spec.ts +++ b/test/modules/shared/httpClient.spec.ts @@ -17,14 +17,15 @@ describe("httpClient", () => { const httpResponse = new DummyResponseClass("Welcome to Thea API"); beforeEach(() => { - httpClient = new HttpClient(TheaNetwork.GANACHE); + httpClient = new HttpClient(consts[TheaNetwork.GANACHE].theaApiBaseUrl); }); it("should create http client on class initialization", () => { - httpClient = new HttpClient(TheaNetwork.GANACHE); + const baseURL = "http://test.com"; + httpClient = new HttpClient(baseURL); const axiosCreateSpy = jest.spyOn(axios, "create"); expect(axiosCreateSpy).toBeCalledWith({ - baseURL: consts[TheaNetwork.GANACHE].theaApiBaseUrl, + baseURL: baseURL, headers: { "Content-Type": "application/json" }