From 7d4bf966c42c1e8218d0da771ec2897167fa5495 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Mon, 21 Feb 2022 13:30:07 +0200 Subject: [PATCH 01/15] Sketch interface INetworkProvider, proxy implementation. --- src/constants.ts | 1 + src/interface.networkProvider.ts | 118 ++++++++++++ src/networkProvider/proxyNetworkProvider.ts | 193 ++++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 src/interface.networkProvider.ts create mode 100644 src/networkProvider/proxyNetworkProvider.ts diff --git a/src/constants.ts b/src/constants.ts index 7d1d2900..b582f0e1 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -10,6 +10,7 @@ export const ESDTNFT_TRANSFER_FUNCTION_NAME = "ESDTNFTTransfer"; export const MULTI_ESDTNFT_TRANSFER_FUNCTION_NAME = "MultiESDTNFTTransfer"; export const ESDT_TRANSFER_VALUE = "0"; +// TODO: Rename fo "AxiosDefaultConfig" (less ambiguous). export const defaultConfig = { timeout: 1000, // See: https://github.com/axios/axios/issues/983 regarding transformResponse diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts new file mode 100644 index 00000000..51043cfb --- /dev/null +++ b/src/interface.networkProvider.ts @@ -0,0 +1,118 @@ +import { BigNumber } from "bignumber.js"; +import { AccountOnNetwork, TokenOfAccountOnNetwork } from "./account"; +import { Address } from "./address"; +import { NetworkConfig } from "./networkConfig"; +import { NetworkStake } from "./networkStake"; +import { NetworkStatus } from "./networkStatus"; +import { Query, QueryResponse } from "./smartcontracts"; +import { Stats } from "./stats"; +import { Token } from "./token"; +import { Transaction, TransactionHash, TransactionStatus } from "./transaction"; +import { TransactionOnNetwork } from "./transactionOnNetwork"; + +/** + * An interface that defines the endpoints of an HTTP API Provider. + */ +export interface INetworkProvider { + /** + * Fetches the Network configuration. + */ + getNetworkConfig(): Promise; + + /** + * Fetches the Network status. + */ + getNetworkStatus(): Promise; + + /** + * Fetches the state of an {@link Account}. + */ + getAccount(address: Address): Promise; + + /** + * Fetches all the tokens (fungible, SFT, NFT) of an account. It does NOT return the definition of tokens. + * It returns token balance and token instance properties. + * + * TODO: Rename to "GetTokensOfAccount" (not renamed yet in order to preserve interface compatibility). + */ + getAddressEsdtList(address: Address): Promise; + + /** + * Fetches information about a fungible token of an account. + * + * TODO: Return explicit type. + * TODO: Rename to "GetFungibleTokenOfAccount" (not renamed yet in order to preserve interface compatibility). + */ + getAddressEsdt(address: Address, tokenIdentifier: string): Promise; + + /** + * Fetches information about a specific token (instance) of an account (works for SFT, Meta ESDT, NFT). + * + * TODO: Return explicit type. + * TODO: Rename to "GetTokenInstanceOfAccount" (not renamed yet in order to preserve interface compatibility). + */ + getAddressNft(address: Address, tokenIdentifier: string, nonce: BigNumber.Value): Promise; + + /** + * Fetches the state of a {@link Transaction}. + */ + getTransaction(txHash: TransactionHash, hintSender?: Address): Promise; + + /** + * Queries the status of a {@link Transaction}. + */ + getTransactionStatus(txHash: TransactionHash): Promise; + + /** + * Broadcasts an already-signed {@link Transaction}. + */ + sendTransaction(tx: Transaction): Promise; + + /** + * Simulates the processing of an already-signed {@link Transaction}. + * + * TODO: Return explicit type. + */ + simulateTransaction(tx: Transaction): Promise; + + /** + * Queries a Smart Contract - runs a pure function defined by the contract and returns its results. + */ + queryContract(query: Query): Promise; + + /** + * Fetches the Network Stake. + * + * TODO: Rename to "GetNetworkStakeStatistics" (not renamed yet in order to preserve interface compatibility). + */ + getNetworkStake(): Promise; + + /** + * Fetches the Network Stats. + * + * TODO: Rename to "GetNetworkGeneralStatistics" (not renamed yet in order to preserve interface compatibility). + */ + getNetworkStats(): Promise; + + /** + * Fetches the definition of a fungible token. + * + * TODO: Rename to "GetDefinitionOfFungibleToken" or "GetFungibleTokenDefinition" (not renamed yet in order to preserve interface compatibility). + */ + getToken(tokenIdentifier: string): Promise; + + /** + * Fetches the definition of a SFT (including Meta ESDT) or NFT. + */ + getDefinitionOfTokenCollection(): Promise; + + /** + * Performs a generic GET action against the provider (useful for new HTTP endpoints, not yet supported by erdjs). + */ + doGetGeneric(resourceUrl: string): Promise; + + /** + * Performs a generic POST action against the provider (useful for new HTTP endpoints, not yet supported by erdjs). + */ + doPostGeneric(resourceUrl: string, payload: any): Promise; +} diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts new file mode 100644 index 00000000..643e0f9a --- /dev/null +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -0,0 +1,193 @@ +import axios, { AxiosRequestConfig } from "axios"; +import { BigNumber } from "bignumber.js"; +import { AccountOnNetwork, TokenOfAccountOnNetwork } from "../account"; +import { Address } from "../address"; +import { defaultConfig } from "../constants"; +import { ErrApiProviderGet, ErrContractQuery } from "../errors"; +import { INetworkProvider } from "../interface.networkProvider"; +import { Logger } from "../logger"; +import { NetworkConfig } from "../networkConfig"; +import { NetworkStake } from "../networkStake"; +import { NetworkStatus } from "../networkStatus"; +import { Query, QueryResponse } from "../smartcontracts"; +import { Stats } from "../stats"; +import { Token } from "../token"; +import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; +import { TransactionOnNetwork } from "../transactionOnNetwork"; + + +export class NetworkProxyProvider implements INetworkProvider { + private url: string; + private config: AxiosRequestConfig; + + /** + * Creates a new {@link INetworkProvider} backed by an Elrond Proxy. + * @param url the URL of the Elrond Proxy + * @param config axios request config options + */ + constructor(url: string, config?: AxiosRequestConfig) { + this.url = url; + this.config = { ...defaultConfig, ...config }; + } + + async getNetworkConfig(): Promise { + let response = await this.doGetGeneric("network/config"); + let networkConfig = NetworkConfig.fromHttpResponse(response.config); + return networkConfig; + } + + async getNetworkStatus(): Promise { + let response = await this.doGetGeneric("network/status/4294967295"); + let networkStatus = NetworkStatus.fromHttpResponse(response.status); + return networkStatus; + } + + async getAccount(address: Address): Promise { + let response = await this.doGetGeneric(`address/${address.bech32()}`); + let account = AccountOnNetwork.fromHttpResponse(response.account); + return account; + } + + async getAddressEsdtList(address: Address): Promise { + let url = `address/${address.bech32()}/esdt`; + let response = await this.doGetGeneric(url); + let tokens = Object.values(response.esdts).map(item => TokenOfAccountOnNetwork.fromHttpResponse(item)); + return tokens; + } + + async getAddressEsdt(address: Address, tokenIdentifier: string): Promise { + let response = await this.doGetGeneric(`address/${address.bech32()}/esdt/${tokenIdentifier}`); + let tokenData = response.tokenData; + return tokenData; + } + + async getAddressNft(address: Address, tokenIdentifier: string, nonce: BigNumber.Value): Promise { + let response = await this.doGetGeneric(`address/${address.bech32()}/nft/${tokenIdentifier}/nonce/${nonce}`); + let tokenData = response.tokenData; + return tokenData; + } + + async getTransaction(txHash: TransactionHash, hintSender?: Address): Promise { + let url = this.buildUrlWithQueryParameters(`transaction/${txHash.toString()}`, { + withSender: hintSender ? hintSender.bech32() : "", + withResults: "true" + }); + + let response = await this.doGetGeneric(url); + let transaction = TransactionOnNetwork.fromHttpResponse(txHash, response.transaction); + return transaction; + } + + async getTransactionStatus(txHash: TransactionHash): Promise { + let response = await this.doGetGeneric(`transaction/${txHash.toString()}/status`); + let status = new TransactionStatus(response.status); + return status; + } + + async sendTransaction(tx: Transaction): Promise { + let response = await this.doPostGeneric("transaction/send", tx.toSendable()); + let hash = new TransactionHash(response.txHash) + return hash; + } + + async simulateTransaction(tx: Transaction): Promise { + let response = await this.doPostGeneric("transaction/simulate", tx.toSendable(), (response) => response); + return response; + } + + async queryContract(query: Query): Promise { + try { + let data = query.toHttpRequest(); + let response = await this.doPostGeneric("vm-values/query", data); + let queryResponse = QueryResponse.fromHttpResponse(response.data || response.vmOutput) + return queryResponse; + } catch (err: any) { + throw ErrContractQuery.increaseSpecificity(err); + } + } + + getNetworkStake(): Promise { + // TODO: Implement wrt.: + // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/stake/stake.service.ts + throw new Error("Method not implemented."); + } + + getNetworkStats(): Promise { + // TODO: Implement wrt. (full implementation may not be possible): + // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/network/network.service.ts + throw new Error("Method not implemented."); + } + + getToken(_tokenIdentifier: string): Promise { + // TODO: Implement wrt.: + // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/esdt/esdt.service.ts#L221 + throw new Error("Method not implemented."); + } + + getDefinitionOfTokenCollection(): Promise { + // TODO: Implement wrt.: + // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/collections/collection.service.ts + // https://docs.elrond.com/developers/esdt-tokens/#get-esdt-token-properties + throw new Error("Method not implemented."); + } + + async doGetGeneric(resourceUrl: string): Promise { + let response = await this.doGet(resourceUrl); + return response; + } + + async doPostGeneric(resourceUrl: string, payload: any): Promise { + let response = await this.doPost(resourceUrl, payload); + return response; + } + + private async doGet(resourceUrl: string): Promise { + try { + let url = `${this.url}/${resourceUrl}`; + let response = await axios.get(url, this.config); + let payload = response.data.data; + return payload; + } catch (error) { + this.handleApiError(error, resourceUrl); + } + } + + private async doPost(resourceUrl: string, payload: any): Promise { + try { + let url = `${this.url}/${resourceUrl}`; + let response = await axios.post(url, payload, { + ...this.config, + headers: { + "Content-Type": "application/json", + }, + }); + let responsePayload = response.data.data; + return responsePayload; + } catch (error) { + this.handleApiError(error, resourceUrl); + } + } + + private buildUrlWithQueryParameters(endpoint: string, params: Record): string { + let searchParams = new URLSearchParams(); + + for (let [key, value] of Object.entries(params)) { + if (value) { + searchParams.append(key, value); + } + } + + return `${endpoint}?${searchParams.toString()}`; + } + + private handleApiError(error: any, resourceUrl: string) { + if (!error.response) { + Logger.warn(error); + throw new ErrApiProviderGet(resourceUrl, error.toString(), error); + } + + let errorData = error.response.data; + let originalErrorMessage = errorData.error || errorData.message || JSON.stringify(errorData); + throw new ErrApiProviderGet(resourceUrl, originalErrorMessage, error); + } +} From 73ccaff1b561df6c45542e4931c3ff201e23c6d2 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Mon, 21 Feb 2022 15:19:31 +0200 Subject: [PATCH 02/15] Sketch implementation. --- src/interface.networkProvider.ts | 40 +++-- src/networkProvider/apiNetworkProvider.ts | 184 ++++++++++++++++++++ src/networkProvider/proxyNetworkProvider.ts | 40 +++-- 3 files changed, 232 insertions(+), 32 deletions(-) create mode 100644 src/networkProvider/apiNetworkProvider.ts diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index 51043cfb..fde79e3c 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -4,6 +4,7 @@ import { Address } from "./address"; import { NetworkConfig } from "./networkConfig"; import { NetworkStake } from "./networkStake"; import { NetworkStatus } from "./networkStatus"; +import { NFTToken } from "./nftToken"; import { Query, QueryResponse } from "./smartcontracts"; import { Stats } from "./stats"; import { Token } from "./token"; @@ -24,6 +25,20 @@ export interface INetworkProvider { */ getNetworkStatus(): Promise; + /** + * Fetches the Network Stake. + * + * TODO: Rename to "GetNetworkStakeStatistics" (not renamed yet in order to preserve interface compatibility). + */ + getNetworkStake(): Promise; + + /** + * Fetches the Network Stats. + * + * TODO: Rename to "GetNetworkGeneralStatistics" (not renamed yet in order to preserve interface compatibility). + */ + getNetworkStats(): Promise; + /** * Fetches the state of an {@link Account}. */ @@ -51,7 +66,7 @@ export interface INetworkProvider { * TODO: Return explicit type. * TODO: Rename to "GetTokenInstanceOfAccount" (not renamed yet in order to preserve interface compatibility). */ - getAddressNft(address: Address, tokenIdentifier: string, nonce: BigNumber.Value): Promise; + getAddressNft(address: Address, collection: string, nonce: BigNumber.Value): Promise; /** * Fetches the state of a {@link Transaction}. @@ -81,30 +96,25 @@ export interface INetworkProvider { queryContract(query: Query): Promise; /** - * Fetches the Network Stake. + * Fetches the definition of a fungible token. * - * TODO: Rename to "GetNetworkStakeStatistics" (not renamed yet in order to preserve interface compatibility). + * TODO: Rename to "GetDefinitionOfFungibleToken" or "GetFungibleTokenDefinition" (not renamed yet in order to preserve interface compatibility). */ - getNetworkStake(): Promise; + getToken(tokenIdentifier: string): Promise; /** - * Fetches the Network Stats. + * Fetches the definition of a SFT (including Meta ESDT) or NFT. * - * TODO: Rename to "GetNetworkGeneralStatistics" (not renamed yet in order to preserve interface compatibility). + * TODO: Return explicit type. */ - getNetworkStats(): Promise; + getDefinitionOfTokenCollection(collection: string): Promise; /** - * Fetches the definition of a fungible token. + * Fetches an instance of a SFT (including Meta ESDT) or NFT. * - * TODO: Rename to "GetDefinitionOfFungibleToken" or "GetFungibleTokenDefinition" (not renamed yet in order to preserve interface compatibility). - */ - getToken(tokenIdentifier: string): Promise; - - /** - * Fetches the definition of a SFT (including Meta ESDT) or NFT. + * TODO: Rename to "GetTokenInstance" (not renamed yet in order to preserve interface compatibility). */ - getDefinitionOfTokenCollection(): Promise; + getNFTToken(tokenIdentifier: string): Promise; /** * Performs a generic GET action against the provider (useful for new HTTP endpoints, not yet supported by erdjs). diff --git a/src/networkProvider/apiNetworkProvider.ts b/src/networkProvider/apiNetworkProvider.ts new file mode 100644 index 00000000..cae27e70 --- /dev/null +++ b/src/networkProvider/apiNetworkProvider.ts @@ -0,0 +1,184 @@ +import axios, { AxiosRequestConfig } from "axios"; +import { BigNumber } from "bignumber.js"; +import { AccountOnNetwork, TokenOfAccountOnNetwork } from "../account"; +import { Address } from "../address"; +import { defaultConfig } from "../constants"; +import { ErrApiProviderGet, ErrContractQuery } from "../errors"; +import { INetworkProvider } from "../interface.networkProvider"; +import { Logger } from "../logger"; +import { NetworkConfig } from "../networkConfig"; +import { NetworkStake } from "../networkStake"; +import { NetworkStatus } from "../networkStatus"; +import { NFTToken } from "../nftToken"; +import { Query, QueryResponse } from "../smartcontracts"; +import { getHexMagnitudeOfBigInt } from "../smartcontracts/codec/utils"; +import { Stats } from "../stats"; +import { Token } from "../token"; +import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; +import { TransactionOnNetwork } from "../transactionOnNetwork"; +import { ProxyNetworkProvider } from "./proxyNetworkProvider"; + +/** + * This is a temporary change, this will be the only provider used, ProxyProvider will be deprecated + */ +export class ApiNetworkProvider implements INetworkProvider { + private url: string; + private config: AxiosRequestConfig; + private backingProxyNetworkProvider; + + /** + * Creates a new ApiProvider. + * @param url the URL of the Elrond Api + * @param config axios request config options + */ + constructor(url: string, config?: AxiosRequestConfig) { + this.url = url; + this.config = { ...defaultConfig, ...config }; + this.backingProxyNetworkProvider = new ProxyNetworkProvider(url, config); + } + + async getNetworkConfig(): Promise { + return await this.backingProxyNetworkProvider.getNetworkConfig(); + } + + async getNetworkStatus(): Promise { + return await this.backingProxyNetworkProvider.getNetworkStatus(); + } + + async getNetworkStake(): Promise { + let response = await this.doGetGeneric("stake"); + let networkStake = NetworkStake.fromHttpResponse(response) + return networkStake; + } + + async getNetworkStats(): Promise { + let response = await this.doGetGeneric("stats"); + let stats = Stats.fromHttpResponse(response) + return stats; + } + + async getAccount(address: Address): Promise { + let response = await this.doGetGeneric(`accounts/${address.bech32()}`); + let account = AccountOnNetwork.fromHttpResponse(response); + return account; + } + + async getAddressEsdtList(address: Address): Promise { + let url = `accounts/${address.bech32()}/tokens`; + let response: any[] = await this.doGetGeneric(url); + let tokens = response.map(item => TokenOfAccountOnNetwork.fromHttpResponse(item)); + return tokens; + } + + async getAddressEsdt(address: Address, tokenIdentifier: string): Promise { + let response = await this.doGetGeneric(`accounts/${address.bech32()}/tokens/${tokenIdentifier}`); + let tokenData = response.tokenData; + return tokenData; + } + + async getAddressNft(address: Address, collection: string, nonce: BigNumber.Value): Promise { + let nonceHex = getHexMagnitudeOfBigInt(new BigNumber(nonce)); + let response = await this.doGetGeneric(`accounts/${address.bech32()}/nfts/${collection}-${nonceHex}`); + let tokenData = response.tokenData; + return tokenData; + } + + async getTransaction(txHash: TransactionHash): Promise { + let response = await this.doGetGeneric(`transactions/${txHash.toString()}`); + let transaction = TransactionOnNetwork.fromHttpResponse(txHash, response.transaction); + return transaction; + } + + async getTransactionStatus(txHash: TransactionHash): Promise { + let response = await this.doGetGeneric(`transactions/${txHash.toString()}?fields=status`); + let status = new TransactionStatus(response.status); + return status; + } + + async sendTransaction(tx: Transaction): Promise { + let response = await this.doPostGeneric("transactions", tx.toSendable()); + // Also see: https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/transactions/entities/transaction.send.result.ts + let hash = new TransactionHash(response.txHash); + return hash; + } + + async simulateTransaction(tx: Transaction): Promise { + return await this.backingProxyNetworkProvider.simulateTransaction(tx); + } + + async queryContract(query: Query): Promise { + try { + let data = query.toHttpRequest(); + let response = await this.doPostGeneric("query", data); + let queryResponse = QueryResponse.fromHttpResponse(response) + return queryResponse; + } catch (err: any) { + throw ErrContractQuery.increaseSpecificity(err); + } + } + + async getToken(tokenIdentifier: string): Promise { + let response = await this.doGetGeneric(`tokens/${tokenIdentifier}`); + let token = Token.fromHttpResponse(response); + return token; + } + + async getNFTToken(tokenIdentifier: string): Promise { + let response = await this.doGetGeneric(`nfts/${tokenIdentifier}`); + let token = NFTToken.fromHttpResponse(response); + return token; + } + + async getDefinitionOfTokenCollection(collection: string): Promise { + let response = await this.doGetGeneric(`collections/${collection}`); + return response; + } + + async doGetGeneric(resourceUrl: string): Promise { + let response = await this.doGet(resourceUrl); + return response; + } + + async doPostGeneric(resourceUrl: string, payload: any): Promise { + let response = await this.doPost(resourceUrl, payload); + return response; + } + + private async doGet(resourceUrl: string): Promise { + try { + let url = `${this.url}/${resourceUrl}`; + let response = await axios.get(url, this.config); + + return response.data; + } catch (error) { + this.handleApiError(error, resourceUrl); + } + } + + private async doPost(resourceUrl: string, payload: any): Promise { + try { + let url = `${this.url}/${resourceUrl}`; + let response = await axios.post(url, payload, { + ...this.config, + headers: { + "Content-Type": "application/json", + }, + }); + let responsePayload = response.data; + return responsePayload; + } catch (error) { + this.handleApiError(error, resourceUrl); + } + } + + private handleApiError(error: any, resourceUrl: string) { + if (!error.response) { + Logger.warn(error); + throw new ErrApiProviderGet(resourceUrl, error.toString(), error); + } + + let errorData = error.response.data; + let originalErrorMessage = errorData.error || errorData.message || JSON.stringify(errorData); + throw new ErrApiProviderGet(resourceUrl, originalErrorMessage, error); + } +} diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index 643e0f9a..6f819b27 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -9,6 +9,7 @@ import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; import { NetworkStatus } from "../networkStatus"; +import { NFTToken } from "../nftToken"; import { Query, QueryResponse } from "../smartcontracts"; import { Stats } from "../stats"; import { Token } from "../token"; @@ -16,7 +17,7 @@ import { Transaction, TransactionHash, TransactionStatus } from "../transaction" import { TransactionOnNetwork } from "../transactionOnNetwork"; -export class NetworkProxyProvider implements INetworkProvider { +export class ProxyNetworkProvider implements INetworkProvider { private url: string; private config: AxiosRequestConfig; @@ -42,6 +43,18 @@ export class NetworkProxyProvider implements INetworkProvider { return networkStatus; } + async getNetworkStake(): Promise { + // TODO: Implement wrt.: + // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/stake/stake.service.ts + throw new Error("Method not implemented."); + } + + async getNetworkStats(): Promise { + // TODO: Implement wrt. (full implementation may not be possible): + // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/network/network.service.ts + throw new Error("Method not implemented."); + } + async getAccount(address: Address): Promise { let response = await this.doGetGeneric(`address/${address.bech32()}`); let account = AccountOnNetwork.fromHttpResponse(response.account); @@ -61,8 +74,8 @@ export class NetworkProxyProvider implements INetworkProvider { return tokenData; } - async getAddressNft(address: Address, tokenIdentifier: string, nonce: BigNumber.Value): Promise { - let response = await this.doGetGeneric(`address/${address.bech32()}/nft/${tokenIdentifier}/nonce/${nonce}`); + async getAddressNft(address: Address, collection: string, nonce: BigNumber.Value): Promise { + let response = await this.doGetGeneric(`address/${address.bech32()}/nft/${collection}/nonce/${nonce}`); let tokenData = response.tokenData; return tokenData; } @@ -86,12 +99,12 @@ export class NetworkProxyProvider implements INetworkProvider { async sendTransaction(tx: Transaction): Promise { let response = await this.doPostGeneric("transaction/send", tx.toSendable()); - let hash = new TransactionHash(response.txHash) + let hash = new TransactionHash(response.txHash); return hash; } async simulateTransaction(tx: Transaction): Promise { - let response = await this.doPostGeneric("transaction/simulate", tx.toSendable(), (response) => response); + let response = await this.doPostGeneric("transaction/simulate", tx.toSendable()); return response; } @@ -99,32 +112,25 @@ export class NetworkProxyProvider implements INetworkProvider { try { let data = query.toHttpRequest(); let response = await this.doPostGeneric("vm-values/query", data); - let queryResponse = QueryResponse.fromHttpResponse(response.data || response.vmOutput) + let queryResponse = QueryResponse.fromHttpResponse(response.data) return queryResponse; } catch (err: any) { throw ErrContractQuery.increaseSpecificity(err); } } - getNetworkStake(): Promise { + async getToken(_tokenIdentifier: string): Promise { // TODO: Implement wrt.: - // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/stake/stake.service.ts - throw new Error("Method not implemented."); - } - - getNetworkStats(): Promise { - // TODO: Implement wrt. (full implementation may not be possible): - // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/network/network.service.ts + // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/esdt/esdt.service.ts#L221 throw new Error("Method not implemented."); } - getToken(_tokenIdentifier: string): Promise { + async getNFTToken(_tokenIdentifier: string): Promise { // TODO: Implement wrt.: - // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/esdt/esdt.service.ts#L221 throw new Error("Method not implemented."); } - getDefinitionOfTokenCollection(): Promise { + async getDefinitionOfTokenCollection(_collection: string): Promise { // TODO: Implement wrt.: // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/collections/collection.service.ts // https://docs.elrond.com/developers/esdt-tokens/#get-esdt-token-properties From 641d2329bf7305417e99dbe270ca12733f46cd1c Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Mon, 21 Feb 2022 17:42:10 +0200 Subject: [PATCH 03/15] Fix AccountOnNetwork.fromHttpResponse(). --- src/account.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/account.ts b/src/account.ts index a5db19db..0d622921 100644 --- a/src/account.ts +++ b/src/account.ts @@ -115,8 +115,8 @@ export class AccountOnNetwork { result.address = new Address(payload["address"] || 0); result.nonce = new Nonce(payload["nonce"] || 0); result.balance = Balance.fromString(payload["balance"]); - result.code = payload["code"]; - result.userName = payload["username"]; + result.code = payload["code"] || ""; + result.userName = payload["username"] || ""; return result; } From 6bb5d8cea5a2d4747373c1c0e0a97f262f36bbea Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Mon, 21 Feb 2022 19:34:09 +0200 Subject: [PATCH 04/15] WIP. --- src/index.ts | 1 + src/interface.networkProvider.ts | 56 ++++++++++++--------- src/networkProvider/apiNetworkProvider.ts | 28 +++++++---- src/networkProvider/index.ts | 2 + src/networkProvider/proxyNetworkProvider.ts | 34 +++++++++---- src/networkProvider/tokens.ts | 54 ++++++++++++++++++++ 6 files changed, 133 insertions(+), 42 deletions(-) create mode 100644 src/networkProvider/index.ts create mode 100644 src/networkProvider/tokens.ts diff --git a/src/index.ts b/src/index.ts index 5e484ff1..b41dfbfb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,4 +30,5 @@ export * from "./nullSigner"; export * from "./smartcontracts"; +export * from "./networkProvider"; export * from "./dapp"; diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index fde79e3c..b36061f4 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -1,5 +1,6 @@ import { BigNumber } from "bignumber.js"; -import { AccountOnNetwork, TokenOfAccountOnNetwork } from "./account"; +import { Nonce } from "."; +import { AccountOnNetwork } from "./account"; import { Address } from "./address"; import { NetworkConfig } from "./networkConfig"; import { NetworkStake } from "./networkStake"; @@ -26,18 +27,14 @@ export interface INetworkProvider { getNetworkStatus(): Promise; /** - * Fetches the Network Stake. - * - * TODO: Rename to "GetNetworkStakeStatistics" (not renamed yet in order to preserve interface compatibility). + * Fetches stake statistics. */ - getNetworkStake(): Promise; + getNetworkStakeStatistics(): Promise; /** - * Fetches the Network Stats. - * - * TODO: Rename to "GetNetworkGeneralStatistics" (not renamed yet in order to preserve interface compatibility). + * Fetches general statistics. */ - getNetworkStats(): Promise; + getNetworkGeneralStatistics(): Promise; /** * Fetches the state of an {@link Account}. @@ -45,28 +42,24 @@ export interface INetworkProvider { getAccount(address: Address): Promise; /** - * Fetches all the tokens (fungible, SFT, NFT) of an account. It does NOT return the definition of tokens. - * It returns token balance and token instance properties. - * - * TODO: Rename to "GetTokensOfAccount" (not renamed yet in order to preserve interface compatibility). + * Fetches data about the fungible tokens held by an account. */ - getAddressEsdtList(address: Address): Promise; + getFungibleTokensOfAccount(address: Address): Promise; /** - * Fetches information about a fungible token of an account. - * - * TODO: Return explicit type. - * TODO: Rename to "GetFungibleTokenOfAccount" (not renamed yet in order to preserve interface compatibility). + * Fetches data about the non-fungible tokens held by account. */ - getAddressEsdt(address: Address, tokenIdentifier: string): Promise; + getNonFungibleTokensOfAccount(address: Address): Promise; /** - * Fetches information about a specific token (instance) of an account (works for SFT, Meta ESDT, NFT). - * - * TODO: Return explicit type. - * TODO: Rename to "GetTokenInstanceOfAccount" (not renamed yet in order to preserve interface compatibility). + * Fetches data about a specific fungible token held by an account. + */ + getFungibleTokenOfAccount(address: Address, tokenIdentifier: string): Promise; + + /** + * Fetches data about a specific non-fungible token (instance) held by an account. */ - getAddressNft(address: Address, collection: string, nonce: BigNumber.Value): Promise; + getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise; /** * Fetches the state of a {@link Transaction}. @@ -126,3 +119,18 @@ export interface INetworkProvider { */ doPostGeneric(resourceUrl: string, payload: any): Promise; } + + +export interface IFungibleTokenOfAccountOnNetwork { + tokenIdentifier: string; + balance: BigNumber; +} + +export interface INonFungibleTokenOfAccountOnNetwork { + tokenIdentifier: string; + attributes: Buffer; + balance: BigNumber; + nonce: Nonce; + creator: Address; + royalties: BigNumber; +} diff --git a/src/networkProvider/apiNetworkProvider.ts b/src/networkProvider/apiNetworkProvider.ts index cae27e70..fec48aa1 100644 --- a/src/networkProvider/apiNetworkProvider.ts +++ b/src/networkProvider/apiNetworkProvider.ts @@ -1,6 +1,6 @@ import axios, { AxiosRequestConfig } from "axios"; import { BigNumber } from "bignumber.js"; -import { AccountOnNetwork, TokenOfAccountOnNetwork } from "../account"; +import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrApiProviderGet, ErrContractQuery } from "../errors"; @@ -17,6 +17,7 @@ import { Token } from "../token"; import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; import { TransactionOnNetwork } from "../transactionOnNetwork"; import { ProxyNetworkProvider } from "./proxyNetworkProvider"; +import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; /** * This is a temporary change, this will be the only provider used, ProxyProvider will be deprecated @@ -45,13 +46,13 @@ export class ApiNetworkProvider implements INetworkProvider { return await this.backingProxyNetworkProvider.getNetworkStatus(); } - async getNetworkStake(): Promise { + async getNetworkStakeStatistics(): Promise { let response = await this.doGetGeneric("stake"); let networkStake = NetworkStake.fromHttpResponse(response) return networkStake; } - async getNetworkStats(): Promise { + async getNetworkGeneralStatistics(): Promise { let response = await this.doGetGeneric("stats"); let stats = Stats.fromHttpResponse(response) return stats; @@ -63,23 +64,32 @@ export class ApiNetworkProvider implements INetworkProvider { return account; } - async getAddressEsdtList(address: Address): Promise { + async getFungibleTokensOfAccount(address: Address): Promise { let url = `accounts/${address.bech32()}/tokens`; let response: any[] = await this.doGetGeneric(url); - let tokens = response.map(item => TokenOfAccountOnNetwork.fromHttpResponse(item)); + let tokens = response.map(item => FungibleTokenOfAccountOnNetwork.fromHttpResponse(item)); + tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); return tokens; } - async getAddressEsdt(address: Address, tokenIdentifier: string): Promise { + async getNonFungibleTokensOfAccount(address: Address): Promise { + let url = `accounts/${address.bech32()}/nfts`; + let response: any[] = await this.doGetGeneric(url); + let tokens = response.map(item => NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(item)); + tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); + return tokens; + } + + async getFungibleTokenOfAccount(address: Address, tokenIdentifier: string): Promise { let response = await this.doGetGeneric(`accounts/${address.bech32()}/tokens/${tokenIdentifier}`); - let tokenData = response.tokenData; + let tokenData = FungibleTokenOfAccountOnNetwork.fromHttpResponse(response.tokenData); return tokenData; } - async getAddressNft(address: Address, collection: string, nonce: BigNumber.Value): Promise { + async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise { let nonceHex = getHexMagnitudeOfBigInt(new BigNumber(nonce)); let response = await this.doGetGeneric(`accounts/${address.bech32()}/nfts/${collection}-${nonceHex}`); - let tokenData = response.tokenData; + let tokenData = NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(response.tokenData); return tokenData; } diff --git a/src/networkProvider/index.ts b/src/networkProvider/index.ts new file mode 100644 index 00000000..0969c4db --- /dev/null +++ b/src/networkProvider/index.ts @@ -0,0 +1,2 @@ +export * from "./apiNetworkProvider"; +export * from "./proxyNetworkProvider"; diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index 6f819b27..9f77bcc4 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -1,6 +1,6 @@ import axios, { AxiosRequestConfig } from "axios"; import { BigNumber } from "bignumber.js"; -import { AccountOnNetwork, TokenOfAccountOnNetwork } from "../account"; +import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrApiProviderGet, ErrContractQuery } from "../errors"; @@ -15,6 +15,7 @@ import { Stats } from "../stats"; import { Token } from "../token"; import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; import { TransactionOnNetwork } from "../transactionOnNetwork"; +import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; export class ProxyNetworkProvider implements INetworkProvider { @@ -43,13 +44,13 @@ export class ProxyNetworkProvider implements INetworkProvider { return networkStatus; } - async getNetworkStake(): Promise { + async getNetworkStakeStatistics(): Promise { // TODO: Implement wrt.: // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/stake/stake.service.ts throw new Error("Method not implemented."); } - async getNetworkStats(): Promise { + async getNetworkGeneralStatistics(): Promise { // TODO: Implement wrt. (full implementation may not be possible): // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/network/network.service.ts throw new Error("Method not implemented."); @@ -61,22 +62,37 @@ export class ProxyNetworkProvider implements INetworkProvider { return account; } - async getAddressEsdtList(address: Address): Promise { + async getFungibleTokensOfAccount(address: Address): Promise { let url = `address/${address.bech32()}/esdt`; let response = await this.doGetGeneric(url); - let tokens = Object.values(response.esdts).map(item => TokenOfAccountOnNetwork.fromHttpResponse(item)); + let responseItems: any[] = Object.values(response.esdts); + // Skip NFTs / SFTs. + let responseItemsFiltered = responseItems.filter(item => !item.attributes); + let tokens = responseItemsFiltered.map(item => FungibleTokenOfAccountOnNetwork.fromHttpResponse(item)); + tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); return tokens; } - async getAddressEsdt(address: Address, tokenIdentifier: string): Promise { + async getNonFungibleTokensOfAccount(address: Address): Promise { + let url = `address/${address.bech32()}/esdt`; + let response = await this.doGetGeneric(url); + let responseItems: any[] = Object.values(response.esdts); + // Skip fungible tokens. + let responseItemsFiltered = responseItems.filter(item => item.nonce >= 0); + let tokens = responseItemsFiltered.map(item => NonFungibleTokenOfAccountOnNetwork.fromProxyHttpResponse(item)); + tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); + return tokens; + } + + async getFungibleTokenOfAccount(address: Address, tokenIdentifier: string): Promise { let response = await this.doGetGeneric(`address/${address.bech32()}/esdt/${tokenIdentifier}`); - let tokenData = response.tokenData; + let tokenData = FungibleTokenOfAccountOnNetwork.fromHttpResponse(response.tokenData); return tokenData; } - async getAddressNft(address: Address, collection: string, nonce: BigNumber.Value): Promise { + async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise { let response = await this.doGetGeneric(`address/${address.bech32()}/nft/${collection}/nonce/${nonce}`); - let tokenData = response.tokenData; + let tokenData = NonFungibleTokenOfAccountOnNetwork.fromProxyHttpResponse(response.tokenData); return tokenData; } diff --git a/src/networkProvider/tokens.ts b/src/networkProvider/tokens.ts new file mode 100644 index 00000000..fdb780f9 --- /dev/null +++ b/src/networkProvider/tokens.ts @@ -0,0 +1,54 @@ +import { BigNumber } from "bignumber.js"; +import { Address } from "../address"; +import { IFungibleTokenOfAccountOnNetwork, INonFungibleTokenOfAccountOnNetwork } from "../interface.networkProvider"; +import { Nonce } from "../nonce"; + +export class FungibleTokenOfAccountOnNetwork implements IFungibleTokenOfAccountOnNetwork { + tokenIdentifier: string = ""; + balance: BigNumber = new BigNumber(0); + + static fromHttpResponse(payload: any): FungibleTokenOfAccountOnNetwork { + let result = new FungibleTokenOfAccountOnNetwork(); + + result.tokenIdentifier = payload.tokenIdentifier || payload.identifier || ""; + result.balance = new BigNumber(payload.balance || 0); + + return result; + } +} + +export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAccountOnNetwork { + tokenIdentifier: string = ""; + attributes: Buffer = Buffer.from([]); + balance: BigNumber = new BigNumber(0); + nonce: Nonce = new Nonce(0); + creator: Address = new Address(""); + royalties: BigNumber = new BigNumber(0); + + static fromProxyHttpResponse(payload: any): NonFungibleTokenOfAccountOnNetwork { + let result = new NonFungibleTokenOfAccountOnNetwork(); + + result.tokenIdentifier = payload.tokenIdentifier || payload.identifier || ""; + result.attributes = Buffer.from(payload.attributes || "", "base64"); + result.balance = new BigNumber(payload.balance || 0); + result.nonce = new Nonce(payload.nonce || 0); + result.creator = new Address(payload.creator || ""); + result.royalties = new BigNumber(payload.royalties || 0).div(100); + + return result; + } + + static fromApiHttpResponse(payload: any): NonFungibleTokenOfAccountOnNetwork { + let result = new NonFungibleTokenOfAccountOnNetwork(); + + result.tokenIdentifier = payload.tokenIdentifier || payload.identifier || ""; + result.attributes = Buffer.from(payload.attributes || "", "base64"); + // On API, missing balance means NFT. + result.balance = new BigNumber(payload.balance || 1); + result.nonce = new Nonce(payload.nonce || 0); + result.creator = new Address(payload.creator || ""); + result.royalties = new BigNumber(payload.royalties || 0); + + return result; + } +} From cdc55088e3c36a79708ebb7be697d99ac871f3d7 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Mon, 21 Feb 2022 23:02:02 +0200 Subject: [PATCH 05/15] WIP. Tests. --- src/interface.networkProvider.ts | 15 +- src/networkProvider/apiNetworkProvider.ts | 31 ++-- src/networkProvider/providers.dev.net.spec.ts | 149 ++++++++++++++++++ src/networkProvider/proxyNetworkProvider.ts | 17 +- 4 files changed, 191 insertions(+), 21 deletions(-) create mode 100644 src/networkProvider/providers.dev.net.spec.ts diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index b36061f4..3f4387fb 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -44,12 +44,12 @@ export interface INetworkProvider { /** * Fetches data about the fungible tokens held by an account. */ - getFungibleTokensOfAccount(address: Address): Promise; + getFungibleTokensOfAccount(address: Address, pagination?: Pagination): Promise; /** * Fetches data about the non-fungible tokens held by account. */ - getNonFungibleTokensOfAccount(address: Address): Promise; + getNonFungibleTokensOfAccount(address: Address, pagination?: Pagination): Promise; /** * Fetches data about a specific fungible token held by an account. @@ -59,7 +59,7 @@ export interface INetworkProvider { /** * Fetches data about a specific non-fungible token (instance) held by an account. */ - getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise; + getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise; /** * Fetches the state of a {@link Transaction}. @@ -134,3 +134,12 @@ export interface INonFungibleTokenOfAccountOnNetwork { creator: Address; royalties: BigNumber; } + +export class Pagination { + from: number = 0; + size: number = 100; + + static default(): Pagination { + return { from: 0, size: 100 }; + } +} diff --git a/src/networkProvider/apiNetworkProvider.ts b/src/networkProvider/apiNetworkProvider.ts index fec48aa1..c82b3a71 100644 --- a/src/networkProvider/apiNetworkProvider.ts +++ b/src/networkProvider/apiNetworkProvider.ts @@ -4,7 +4,7 @@ import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrApiProviderGet, ErrContractQuery } from "../errors"; -import { INetworkProvider } from "../interface.networkProvider"; +import { IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, Pagination } from "../interface.networkProvider"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; @@ -19,9 +19,6 @@ import { TransactionOnNetwork } from "../transactionOnNetwork"; import { ProxyNetworkProvider } from "./proxyNetworkProvider"; import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; -/** - * This is a temporary change, this will be the only provider used, ProxyProvider will be deprecated - */ export class ApiNetworkProvider implements INetworkProvider { private url: string; private config: AxiosRequestConfig; @@ -64,29 +61,37 @@ export class ApiNetworkProvider implements INetworkProvider { return account; } - async getFungibleTokensOfAccount(address: Address): Promise { - let url = `accounts/${address.bech32()}/tokens`; + async getFungibleTokensOfAccount(address: Address, pagination?: Pagination): Promise { + pagination = pagination || Pagination.default(); + + let url = `accounts/${address.bech32()}/tokens?${this.buildPaginationParams(pagination)}`; let response: any[] = await this.doGetGeneric(url); let tokens = response.map(item => FungibleTokenOfAccountOnNetwork.fromHttpResponse(item)); + + // TODO: Fix sorting tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); return tokens; } - async getNonFungibleTokensOfAccount(address: Address): Promise { - let url = `accounts/${address.bech32()}/nfts`; + async getNonFungibleTokensOfAccount(address: Address, pagination?: Pagination): Promise { + pagination = pagination || Pagination.default(); + + let url = `accounts/${address.bech32()}/nfts?${this.buildPaginationParams(pagination)}`; let response: any[] = await this.doGetGeneric(url); let tokens = response.map(item => NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(item)); + + // TODO: Fix sorting tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); return tokens; } - async getFungibleTokenOfAccount(address: Address, tokenIdentifier: string): Promise { + async getFungibleTokenOfAccount(address: Address, tokenIdentifier: string): Promise { let response = await this.doGetGeneric(`accounts/${address.bech32()}/tokens/${tokenIdentifier}`); - let tokenData = FungibleTokenOfAccountOnNetwork.fromHttpResponse(response.tokenData); + let tokenData = FungibleTokenOfAccountOnNetwork.fromHttpResponse(response); return tokenData; } - async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise { + async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise { let nonceHex = getHexMagnitudeOfBigInt(new BigNumber(nonce)); let response = await this.doGetGeneric(`accounts/${address.bech32()}/nfts/${collection}-${nonceHex}`); let tokenData = NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(response.tokenData); @@ -154,6 +159,10 @@ export class ApiNetworkProvider implements INetworkProvider { return response; } + private buildPaginationParams(pagination: Pagination) { + return `from=${pagination.from}&size=${pagination.size}`; + } + private async doGet(resourceUrl: string): Promise { try { let url = `${this.url}/${resourceUrl}`; diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts new file mode 100644 index 00000000..a0a9c7d2 --- /dev/null +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -0,0 +1,149 @@ +import { assert } from "chai"; +import { ApiNetworkProvider, ProxyNetworkProvider } from "."; +import { Hash } from "../hash"; +import { TransactionOnNetwork, TransactionOnNetworkType } from "../transactionOnNetwork"; +import { INetworkProvider } from "../interface.networkProvider"; +import { Address } from "../address"; +import { loadTestWallets, TestWallet } from "../testutils"; + +describe("test network providers on devnet: Proxy and API", function () { + let apiProvider: INetworkProvider = new ApiNetworkProvider("https://devnet-api.elrond.com", { timeout: 5000 }); + let proxyProvider: INetworkProvider = new ProxyNetworkProvider("https://devnet-gateway.elrond.com", { timeout: 5000 }); + + let alice: TestWallet; + let bob: TestWallet; + let carol: TestWallet; + let dan: TestWallet; + + before(async function () { + ({ alice, bob, carol, dan } = await loadTestWallets()); + }); + + it("should have same response for getNetworkConfig()", async function () { + let apiResponse = await apiProvider.getNetworkConfig(); + let proxyResponse = await proxyProvider.getNetworkConfig(); + assert.deepEqual(apiResponse, proxyResponse); + }); + + it("should have same response for getNetworkStatus()", async function () { + let apiResponse = await apiProvider.getNetworkStatus(); + let proxyResponse = await proxyProvider.getNetworkStatus(); + assert.deepEqual(apiResponse, proxyResponse); + }); + + // TODO: Enable test after implementing ProxyNetworkProvider.getNetworkStakeStatistics(). + it.skip("should have same response for getNetworkStakeStatistics()", async function () { + let apiResponse = await apiProvider.getNetworkStakeStatistics(); + let proxyResponse = await proxyProvider.getNetworkStakeStatistics(); + assert.deepEqual(apiResponse, proxyResponse); + }); + + // TODO: Enable test after implementing ProxyNetworkProvider.getNetworkGeneralStatistics(). + it.skip("should have same response for getNetworkGeneralStatistics()", async function () { + let apiResponse = await apiProvider.getNetworkGeneralStatistics(); + let proxyResponse = await proxyProvider.getNetworkGeneralStatistics(); + assert.deepEqual(apiResponse, proxyResponse); + }); + + it("should have same response for getAccount()", async function () { + for (const user of [alice, bob, carol, dan]) { + let apiResponse = await apiProvider.getAccount(user.address); + let proxyResponse = await proxyProvider.getAccount(user.address); + assert.deepEqual(apiResponse, proxyResponse); + } + }); + + it.only("should have same response for getFungibleTokensOfAccount(), getFungibleTokenOfAccount()", async function () { + this.timeout(10000); + + for (const user of [alice, bob, carol, dan]) { + let apiResponse = await apiProvider.getFungibleTokensOfAccount(user.address); + let proxyResponse = await proxyProvider.getFungibleTokensOfAccount(user.address); + assert.deepEqual(apiResponse.slice(0, 100), proxyResponse.slice(0, 100)); + + for (const item of apiResponse.slice(0, 5)) { + let apiResponse = await apiProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); + let proxyResponse = await proxyProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); + //assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.tokenIdentifier}`); + } + } + }); + + it("should have same response for getNonFungibleTokensOfAccount(), getNonFungibleTokenOfAccount", async function () { + this.timeout(10000); + + for (const user of [alice, bob, carol, dan]) { + let apiResponse = await apiProvider.getNonFungibleTokensOfAccount(user.address); + let proxyResponse = await proxyProvider.getNonFungibleTokensOfAccount(user.address); + assert.deepEqual(apiResponse.slice(0, 100), proxyResponse.slice(0, 100)); + + for (const item of apiResponse.slice(0, 5)) { + // let apiResponse = await apiProvider.getNonFungibleTokenOfAccount(user.address, item.tokenIdentifier); + // let proxyResponse = await proxyProvider.getNonFungibleTokenOfAccount(user.address, item.tokenIdentifier); + assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.tokenIdentifier}`); + } + } + }); + + // it("check get transaction", async function () { + // this.timeout(20000); + + // let sender = new Address("erd1testnlersh4z0wsv8kjx39me4rmnvjkwu8dsaea7ukdvvc9z396qykv7z7"); + // let proxyProvider = chooseProxyProvider("elrond-devnet"); + // let apiProvider = chooseApiProvider("elrond-devnet"); + + // let hashes = [ + // new TransactionHash("b41f5fc39e96b1f194d07761c6efd6cb92278b95f5012ab12cbc910058ca8b54"), + // new TransactionHash("7757397a59378e9d0f6d5f08cc934c260e33a50ae0d73fdf869f7c02b6b47b33"), + // new TransactionHash("b87238089e81527158a6daee520280324bc7e5322ba54d1b3c9a5678abe953ea"), + // new TransactionHash("b45dd5e598bc85ba71639f2cbce8c5dff2fbe93159e637852fddeb16c0e84a48"), + // new TransactionHash("83db780e98d4d3c917668c47b33ba51445591efacb0df2a922f88e7dfbb5fc7d"), + // new TransactionHash("c2eb62b28cc7320da2292d87944c5424a70e1f443323c138c1affada7f6e9705") + // ] + + // for (const hash of hashes) { + // let transactionOnProxy = await proxyProvider.getTransaction(hash, sender, true); + // let transactionOnAPI = await apiProvider.getTransaction(hash); + + // ignoreKnownDifferencesBetweenProviders(transactionOnProxy, transactionOnAPI); + // assert.deepEqual(transactionOnProxy, transactionOnAPI); + // } + // }); + + // // TODO: Strive to have as little differences as possible between Proxy and API. + // // ... On client-side (erdjs), try to handle the differences in ProxyProvider & ApiProvider, or in TransactionOnNetwork. + // // ... Merging the providers (in the future) should solve this as well. + // function ignoreKnownDifferencesBetweenProviders(transactionOnProxy: TransactionOnNetwork, transactionOnAPI: TransactionOnNetwork) { + // // Ignore status, since it differs between Proxy and API (for smart contract calls): + // transactionOnProxy.status = new TransactionStatus("unknown"); + // transactionOnAPI.status = new TransactionStatus("unknown"); + + // // Ignore fields which are not present on API response: + // transactionOnProxy.epoch = 0; + // transactionOnProxy.type = new TransactionOnNetworkType(); + // transactionOnProxy.blockNonce = new Nonce(0); + // transactionOnProxy.hyperblockNonce = new Nonce(0); + // transactionOnProxy.hyperblockHash = new Hash(""); + + // let immediateContractResultOnAPI: SmartContractResultItem = (transactionOnAPI).results.immediate; + // let contractResultsOnAPI: SmartContractResultItem[] = (transactionOnAPI).results.items; + // let resultingCallsOnAPI: SmartContractResultItem[] = (transactionOnAPI).results.resultingCalls; + // let allContractResults = [immediateContractResultOnAPI].concat(resultingCallsOnAPI).concat(contractResultsOnAPI); + + // // Important issue (existing bug)! When working with TransactionOnNetwork objects, SCRs cannot be parsed correctly from API, only from Proxy. + // // On API response, base64 decode "data" from smart contract results: + // for (const item of allContractResults) { + // item.data = Buffer.from(item.data, "base64").toString(); + // } + + // // On API response, convert "callType" of smart contract results to a number: + // for (const item of allContractResults) { + // item.callType = Number(item.callType); + // } + + // // On API response, sort contract results by nonce: + // contractResultsOnAPI.sort(function (a: SmartContractResultItem, b: SmartContractResultItem) { + // return a.nonce.valueOf() - b.nonce.valueOf(); + // }); + // } +}); diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index 9f77bcc4..340b060c 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -4,7 +4,7 @@ import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrApiProviderGet, ErrContractQuery } from "../errors"; -import { INetworkProvider } from "../interface.networkProvider"; +import { IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, Pagination } from "../interface.networkProvider"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; @@ -17,7 +17,6 @@ import { Transaction, TransactionHash, TransactionStatus } from "../transaction" import { TransactionOnNetwork } from "../transactionOnNetwork"; import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; - export class ProxyNetworkProvider implements INetworkProvider { private url: string; private config: AxiosRequestConfig; @@ -62,35 +61,39 @@ export class ProxyNetworkProvider implements INetworkProvider { return account; } - async getFungibleTokensOfAccount(address: Address): Promise { + async getFungibleTokensOfAccount(address: Address, _pagination?: Pagination): Promise { let url = `address/${address.bech32()}/esdt`; let response = await this.doGetGeneric(url); let responseItems: any[] = Object.values(response.esdts); // Skip NFTs / SFTs. - let responseItemsFiltered = responseItems.filter(item => !item.attributes); + let responseItemsFiltered = responseItems.filter(item => !item.nonce); let tokens = responseItemsFiltered.map(item => FungibleTokenOfAccountOnNetwork.fromHttpResponse(item)); + + // TODO: Fix sorting tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); return tokens; } - async getNonFungibleTokensOfAccount(address: Address): Promise { + async getNonFungibleTokensOfAccount(address: Address, _pagination?: Pagination): Promise { let url = `address/${address.bech32()}/esdt`; let response = await this.doGetGeneric(url); let responseItems: any[] = Object.values(response.esdts); // Skip fungible tokens. let responseItemsFiltered = responseItems.filter(item => item.nonce >= 0); let tokens = responseItemsFiltered.map(item => NonFungibleTokenOfAccountOnNetwork.fromProxyHttpResponse(item)); + + // TODO: Fix sorting tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); return tokens; } - async getFungibleTokenOfAccount(address: Address, tokenIdentifier: string): Promise { + async getFungibleTokenOfAccount(address: Address, tokenIdentifier: string): Promise { let response = await this.doGetGeneric(`address/${address.bech32()}/esdt/${tokenIdentifier}`); let tokenData = FungibleTokenOfAccountOnNetwork.fromHttpResponse(response.tokenData); return tokenData; } - async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise { + async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise { let response = await this.doGetGeneric(`address/${address.bech32()}/nft/${collection}/nonce/${nonce}`); let tokenData = NonFungibleTokenOfAccountOnNetwork.fromProxyHttpResponse(response.tokenData); return tokenData; From 08a386ca4586d6896528beefffc0e4fa9d28ab92 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Tue, 22 Feb 2022 11:31:00 +0200 Subject: [PATCH 06/15] WIP. --- src/interface.networkProvider.ts | 35 +++++- src/networkProvider/apiNetworkProvider.ts | 17 ++- src/networkProvider/index.ts | 3 + src/networkProvider/providers.dev.net.spec.ts | 118 +++++++----------- src/networkProvider/proxyNetworkProvider.ts | 14 +-- src/networkProvider/tokens.ts | 13 +- src/networkProvider/transactions.ts | 90 +++++++++++++ src/nonce.ts | 5 + src/utils.codec.ts | 10 ++ 9 files changed, 210 insertions(+), 95 deletions(-) create mode 100644 src/networkProvider/transactions.ts create mode 100644 src/utils.codec.ts diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index 3f4387fb..30d87f07 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -1,16 +1,17 @@ import { BigNumber } from "bignumber.js"; -import { Nonce } from "."; +import { Balance, GasLimit, GasPrice, Nonce, TransactionPayload } from "."; import { AccountOnNetwork } from "./account"; import { Address } from "./address"; +import { Hash } from "./hash"; import { NetworkConfig } from "./networkConfig"; import { NetworkStake } from "./networkStake"; import { NetworkStatus } from "./networkStatus"; import { NFTToken } from "./nftToken"; +import { Signature } from "./signature"; import { Query, QueryResponse } from "./smartcontracts"; import { Stats } from "./stats"; import { Token } from "./token"; import { Transaction, TransactionHash, TransactionStatus } from "./transaction"; -import { TransactionOnNetwork } from "./transactionOnNetwork"; /** * An interface that defines the endpoints of an HTTP API Provider. @@ -59,12 +60,12 @@ export interface INetworkProvider { /** * Fetches data about a specific non-fungible token (instance) held by an account. */ - getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise; + getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: Nonce): Promise; /** * Fetches the state of a {@link Transaction}. */ - getTransaction(txHash: TransactionHash, hintSender?: Address): Promise; + getTransaction(txHash: TransactionHash, hintSender?: Address): Promise; /** * Queries the status of a {@link Transaction}. @@ -120,7 +121,6 @@ export interface INetworkProvider { doPostGeneric(resourceUrl: string, payload: any): Promise; } - export interface IFungibleTokenOfAccountOnNetwork { tokenIdentifier: string; balance: BigNumber; @@ -128,6 +128,7 @@ export interface IFungibleTokenOfAccountOnNetwork { export interface INonFungibleTokenOfAccountOnNetwork { tokenIdentifier: string; + collection: string; attributes: Buffer; balance: BigNumber; nonce: Nonce; @@ -143,3 +144,27 @@ export class Pagination { return { from: 0, size: 100 }; } } + +export interface ITransactionOnNetwork { + hash: TransactionHash; + type: ITypeOfTransactionOnNetwork; + nonce: Nonce; + round: number; + epoch: number; + value: Balance; + receiver: Address; + sender: Address; + gasPrice: GasPrice; + gasLimit: GasLimit; + data: TransactionPayload; + signature: Signature; + status: TransactionStatus; + timestamp: number; + blockNonce: Nonce; + hyperblockNonce: Nonce; + hyperblockHash: Hash; +} + + export interface ITypeOfTransactionOnNetwork { + readonly value: string; +} diff --git a/src/networkProvider/apiNetworkProvider.ts b/src/networkProvider/apiNetworkProvider.ts index c82b3a71..93eddac8 100644 --- a/src/networkProvider/apiNetworkProvider.ts +++ b/src/networkProvider/apiNetworkProvider.ts @@ -4,20 +4,20 @@ import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrApiProviderGet, ErrContractQuery } from "../errors"; -import { IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, Pagination } from "../interface.networkProvider"; +import { IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; import { NetworkStatus } from "../networkStatus"; import { NFTToken } from "../nftToken"; +import { Nonce } from "../nonce"; import { Query, QueryResponse } from "../smartcontracts"; -import { getHexMagnitudeOfBigInt } from "../smartcontracts/codec/utils"; import { Stats } from "../stats"; import { Token } from "../token"; import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; -import { TransactionOnNetwork } from "../transactionOnNetwork"; import { ProxyNetworkProvider } from "./proxyNetworkProvider"; import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; +import { TransactionOnNetwork } from "./transactions"; export class ApiNetworkProvider implements INetworkProvider { private url: string; @@ -91,16 +91,15 @@ export class ApiNetworkProvider implements INetworkProvider { return tokenData; } - async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise { - let nonceHex = getHexMagnitudeOfBigInt(new BigNumber(nonce)); - let response = await this.doGetGeneric(`accounts/${address.bech32()}/nfts/${collection}-${nonceHex}`); - let tokenData = NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(response.tokenData); + async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: Nonce): Promise { + let response = await this.doGetGeneric(`accounts/${address.bech32()}/nfts/${collection}-${nonce.hex()}`); + let tokenData = NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(response); return tokenData; } - async getTransaction(txHash: TransactionHash): Promise { + async getTransaction(txHash: TransactionHash): Promise { let response = await this.doGetGeneric(`transactions/${txHash.toString()}`); - let transaction = TransactionOnNetwork.fromHttpResponse(txHash, response.transaction); + let transaction = TransactionOnNetwork.fromApiHttpResponse(txHash, response); return transaction; } diff --git a/src/networkProvider/index.ts b/src/networkProvider/index.ts index 0969c4db..a4276ae7 100644 --- a/src/networkProvider/index.ts +++ b/src/networkProvider/index.ts @@ -1,2 +1,5 @@ export * from "./apiNetworkProvider"; export * from "./proxyNetworkProvider"; + +// do not export "./tokens" (encapsulation) +// do not export "./transactions" (encapsulation) diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts index a0a9c7d2..2ef52b30 100644 --- a/src/networkProvider/providers.dev.net.spec.ts +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -1,10 +1,11 @@ import { assert } from "chai"; import { ApiNetworkProvider, ProxyNetworkProvider } from "."; import { Hash } from "../hash"; -import { TransactionOnNetwork, TransactionOnNetworkType } from "../transactionOnNetwork"; -import { INetworkProvider } from "../interface.networkProvider"; +import { INetworkProvider, ITransactionOnNetwork } from "../interface.networkProvider"; import { Address } from "../address"; import { loadTestWallets, TestWallet } from "../testutils"; +import { TransactionHash, TransactionStatus } from "../transaction"; +import { Nonce } from "../nonce"; describe("test network providers on devnet: Proxy and API", function () { let apiProvider: INetworkProvider = new ApiNetworkProvider("https://devnet-api.elrond.com", { timeout: 5000 }); @@ -53,7 +54,7 @@ describe("test network providers on devnet: Proxy and API", function () { } }); - it.only("should have same response for getFungibleTokensOfAccount(), getFungibleTokenOfAccount()", async function () { + it("should have same response for getFungibleTokensOfAccount(), getFungibleTokenOfAccount()", async function () { this.timeout(10000); for (const user of [alice, bob, carol, dan]) { @@ -61,15 +62,15 @@ describe("test network providers on devnet: Proxy and API", function () { let proxyResponse = await proxyProvider.getFungibleTokensOfAccount(user.address); assert.deepEqual(apiResponse.slice(0, 100), proxyResponse.slice(0, 100)); - for (const item of apiResponse.slice(0, 5)) { - let apiResponse = await apiProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); - let proxyResponse = await proxyProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); - //assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.tokenIdentifier}`); - } + // for (const item of apiResponse.slice(0, 5)) { + // let apiResponse = await apiProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); + // let proxyResponse = await proxyProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); + // //assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.tokenIdentifier}`); + // } } }); - it("should have same response for getNonFungibleTokensOfAccount(), getNonFungibleTokenOfAccount", async function () { + it.only("should have same response for getNonFungibleTokensOfAccount(), getNonFungibleTokenOfAccount", async function () { this.timeout(10000); for (const user of [alice, bob, carol, dan]) { @@ -78,72 +79,45 @@ describe("test network providers on devnet: Proxy and API", function () { assert.deepEqual(apiResponse.slice(0, 100), proxyResponse.slice(0, 100)); for (const item of apiResponse.slice(0, 5)) { - // let apiResponse = await apiProvider.getNonFungibleTokenOfAccount(user.address, item.tokenIdentifier); - // let proxyResponse = await proxyProvider.getNonFungibleTokenOfAccount(user.address, item.tokenIdentifier); + let apiResponse = await apiProvider.getNonFungibleTokenOfAccount(user.address, item.collection, item.nonce); + let proxyResponse = await proxyProvider.getNonFungibleTokenOfAccount(user.address, item.collection, item.nonce); assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.tokenIdentifier}`); } } }); - // it("check get transaction", async function () { - // this.timeout(20000); - - // let sender = new Address("erd1testnlersh4z0wsv8kjx39me4rmnvjkwu8dsaea7ukdvvc9z396qykv7z7"); - // let proxyProvider = chooseProxyProvider("elrond-devnet"); - // let apiProvider = chooseApiProvider("elrond-devnet"); - - // let hashes = [ - // new TransactionHash("b41f5fc39e96b1f194d07761c6efd6cb92278b95f5012ab12cbc910058ca8b54"), - // new TransactionHash("7757397a59378e9d0f6d5f08cc934c260e33a50ae0d73fdf869f7c02b6b47b33"), - // new TransactionHash("b87238089e81527158a6daee520280324bc7e5322ba54d1b3c9a5678abe953ea"), - // new TransactionHash("b45dd5e598bc85ba71639f2cbce8c5dff2fbe93159e637852fddeb16c0e84a48"), - // new TransactionHash("83db780e98d4d3c917668c47b33ba51445591efacb0df2a922f88e7dfbb5fc7d"), - // new TransactionHash("c2eb62b28cc7320da2292d87944c5424a70e1f443323c138c1affada7f6e9705") - // ] - - // for (const hash of hashes) { - // let transactionOnProxy = await proxyProvider.getTransaction(hash, sender, true); - // let transactionOnAPI = await apiProvider.getTransaction(hash); - - // ignoreKnownDifferencesBetweenProviders(transactionOnProxy, transactionOnAPI); - // assert.deepEqual(transactionOnProxy, transactionOnAPI); - // } - // }); - - // // TODO: Strive to have as little differences as possible between Proxy and API. - // // ... On client-side (erdjs), try to handle the differences in ProxyProvider & ApiProvider, or in TransactionOnNetwork. - // // ... Merging the providers (in the future) should solve this as well. - // function ignoreKnownDifferencesBetweenProviders(transactionOnProxy: TransactionOnNetwork, transactionOnAPI: TransactionOnNetwork) { - // // Ignore status, since it differs between Proxy and API (for smart contract calls): - // transactionOnProxy.status = new TransactionStatus("unknown"); - // transactionOnAPI.status = new TransactionStatus("unknown"); - - // // Ignore fields which are not present on API response: - // transactionOnProxy.epoch = 0; - // transactionOnProxy.type = new TransactionOnNetworkType(); - // transactionOnProxy.blockNonce = new Nonce(0); - // transactionOnProxy.hyperblockNonce = new Nonce(0); - // transactionOnProxy.hyperblockHash = new Hash(""); - - // let immediateContractResultOnAPI: SmartContractResultItem = (transactionOnAPI).results.immediate; - // let contractResultsOnAPI: SmartContractResultItem[] = (transactionOnAPI).results.items; - // let resultingCallsOnAPI: SmartContractResultItem[] = (transactionOnAPI).results.resultingCalls; - // let allContractResults = [immediateContractResultOnAPI].concat(resultingCallsOnAPI).concat(contractResultsOnAPI); - - // // Important issue (existing bug)! When working with TransactionOnNetwork objects, SCRs cannot be parsed correctly from API, only from Proxy. - // // On API response, base64 decode "data" from smart contract results: - // for (const item of allContractResults) { - // item.data = Buffer.from(item.data, "base64").toString(); - // } - - // // On API response, convert "callType" of smart contract results to a number: - // for (const item of allContractResults) { - // item.callType = Number(item.callType); - // } - - // // On API response, sort contract results by nonce: - // contractResultsOnAPI.sort(function (a: SmartContractResultItem, b: SmartContractResultItem) { - // return a.nonce.valueOf() - b.nonce.valueOf(); - // }); - // } + it("should have same response for getTransaction(), getTransactionStatus()", async function () { + this.timeout(20000); + + let sender = new Address("erd1testnlersh4z0wsv8kjx39me4rmnvjkwu8dsaea7ukdvvc9z396qykv7z7"); + + let hashes = [ + new TransactionHash("b41f5fc39e96b1f194d07761c6efd6cb92278b95f5012ab12cbc910058ca8b54"), + new TransactionHash("7757397a59378e9d0f6d5f08cc934c260e33a50ae0d73fdf869f7c02b6b47b33"), + new TransactionHash("b87238089e81527158a6daee520280324bc7e5322ba54d1b3c9a5678abe953ea"), + new TransactionHash("b45dd5e598bc85ba71639f2cbce8c5dff2fbe93159e637852fddeb16c0e84a48"), + new TransactionHash("83db780e98d4d3c917668c47b33ba51445591efacb0df2a922f88e7dfbb5fc7d"), + new TransactionHash("c2eb62b28cc7320da2292d87944c5424a70e1f443323c138c1affada7f6e9705") + ]; + + for (const hash of hashes) { + let apiResponse = await apiProvider.getTransaction(hash); + let proxyResponse = await proxyProvider.getTransaction(hash, sender); + + ignoreKnownTxDifferencesBetweenProviders(apiResponse, proxyResponse); + assert.deepEqual(apiResponse, proxyResponse); + } + }); + + // TODO: Strive to have as little differences as possible between Proxy and API. + function ignoreKnownTxDifferencesBetweenProviders(apiResponse: ITransactionOnNetwork, proxyResponse: ITransactionOnNetwork) { + apiResponse.status = proxyResponse.status = new TransactionStatus("ignore"); + apiResponse.type = proxyResponse.type = { value: "ignore" }; + + // Ignore fields which are not present on API response: + proxyResponse.epoch = 0; + proxyResponse.blockNonce = new Nonce(0); + proxyResponse.hyperblockNonce = new Nonce(0); + proxyResponse.hyperblockHash = new Hash(""); + } }); diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index 340b060c..99874e9d 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -1,21 +1,21 @@ import axios, { AxiosRequestConfig } from "axios"; -import { BigNumber } from "bignumber.js"; import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrApiProviderGet, ErrContractQuery } from "../errors"; -import { IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, Pagination } from "../interface.networkProvider"; +import { IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; import { NetworkStatus } from "../networkStatus"; import { NFTToken } from "../nftToken"; +import { Nonce } from "../nonce"; import { Query, QueryResponse } from "../smartcontracts"; import { Stats } from "../stats"; import { Token } from "../token"; import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; -import { TransactionOnNetwork } from "../transactionOnNetwork"; import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; +import { TransactionOnNetwork } from "./transactions"; export class ProxyNetworkProvider implements INetworkProvider { private url: string; @@ -93,20 +93,20 @@ export class ProxyNetworkProvider implements INetworkProvider { return tokenData; } - async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: BigNumber.Value): Promise { - let response = await this.doGetGeneric(`address/${address.bech32()}/nft/${collection}/nonce/${nonce}`); + async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: Nonce): Promise { + let response = await this.doGetGeneric(`address/${address.bech32()}/nft/${collection}/nonce/${nonce.hex()}`); let tokenData = NonFungibleTokenOfAccountOnNetwork.fromProxyHttpResponse(response.tokenData); return tokenData; } - async getTransaction(txHash: TransactionHash, hintSender?: Address): Promise { + async getTransaction(txHash: TransactionHash, hintSender?: Address): Promise { let url = this.buildUrlWithQueryParameters(`transaction/${txHash.toString()}`, { withSender: hintSender ? hintSender.bech32() : "", withResults: "true" }); let response = await this.doGetGeneric(url); - let transaction = TransactionOnNetwork.fromHttpResponse(txHash, response.transaction); + let transaction = TransactionOnNetwork.fromProxyHttpResponse(txHash, response.transaction); return transaction; } diff --git a/src/networkProvider/tokens.ts b/src/networkProvider/tokens.ts index fdb780f9..5cf66794 100644 --- a/src/networkProvider/tokens.ts +++ b/src/networkProvider/tokens.ts @@ -19,6 +19,7 @@ export class FungibleTokenOfAccountOnNetwork implements IFungibleTokenOfAccountO export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAccountOnNetwork { tokenIdentifier: string = ""; + collection: string = ""; attributes: Buffer = Buffer.from([]); balance: BigNumber = new BigNumber(0); nonce: Nonce = new Nonce(0); @@ -28,7 +29,8 @@ export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAc static fromProxyHttpResponse(payload: any): NonFungibleTokenOfAccountOnNetwork { let result = new NonFungibleTokenOfAccountOnNetwork(); - result.tokenIdentifier = payload.tokenIdentifier || payload.identifier || ""; + result.tokenIdentifier = payload.tokenIdentifier || ""; + result.collection = NonFungibleTokenOfAccountOnNetwork.parseCollectionFromIdentifier(result.tokenIdentifier); result.attributes = Buffer.from(payload.attributes || "", "base64"); result.balance = new BigNumber(payload.balance || 0); result.nonce = new Nonce(payload.nonce || 0); @@ -38,10 +40,17 @@ export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAc return result; } + private static parseCollectionFromIdentifier(identifier: string): string { + let parts = identifier.split("-"); + let collection = parts.slice(0, 2).join("-"); + return collection; + } + static fromApiHttpResponse(payload: any): NonFungibleTokenOfAccountOnNetwork { let result = new NonFungibleTokenOfAccountOnNetwork(); - result.tokenIdentifier = payload.tokenIdentifier || payload.identifier || ""; + result.tokenIdentifier = payload.identifier || ""; + result.collection = payload.collection || ""; result.attributes = Buffer.from(payload.attributes || "", "base64"); // On API, missing balance means NFT. result.balance = new BigNumber(payload.balance || 1); diff --git a/src/networkProvider/transactions.ts b/src/networkProvider/transactions.ts new file mode 100644 index 00000000..0e9a76c3 --- /dev/null +++ b/src/networkProvider/transactions.ts @@ -0,0 +1,90 @@ +import { Address } from "../address"; +import { Balance } from "../balance"; +import { Hash } from "../hash"; +import { ITransactionOnNetwork, ITypeOfTransactionOnNetwork } from "../interface.networkProvider"; +import { GasLimit, GasPrice } from "../networkParams"; +import { Nonce } from "../nonce"; +import { Signature } from "../signature"; +import { TransactionHash, TransactionStatus } from "../transaction"; +import { TransactionPayload } from "../transactionPayload"; + + export class TransactionOnNetwork implements ITransactionOnNetwork { + hash: TransactionHash = new TransactionHash(""); + type: ITypeOfTransactionOnNetwork = new TypeOfTransactionOnNetwork(); + nonce: Nonce = new Nonce(0); + round: number = 0; + epoch: number = 0; + value: Balance = Balance.Zero(); + receiver: Address = new Address(); + sender: Address = new Address(); + gasPrice: GasPrice = new GasPrice(0); + gasLimit: GasLimit = new GasLimit(0); + data: TransactionPayload = new TransactionPayload(); + signature: Signature = Signature.empty(); + status: TransactionStatus = TransactionStatus.createUnknown(); + timestamp: number = 0; + + blockNonce: Nonce = new Nonce(0); + hyperblockNonce: Nonce = new Nonce(0); + hyperblockHash: Hash = Hash.empty(); + + static fromProxyHttpResponse(txHash: TransactionHash, response: any): TransactionOnNetwork { + let transactionOnNetwork = new TransactionOnNetwork(); + + transactionOnNetwork.hash = txHash; + transactionOnNetwork.type = new TypeOfTransactionOnNetwork(response.type || ""); + transactionOnNetwork.nonce = new Nonce(response.nonce || 0); + transactionOnNetwork.round = response.round; + transactionOnNetwork.epoch = response.epoch || 0; + transactionOnNetwork.value = Balance.fromString(response.value); + transactionOnNetwork.sender = Address.fromBech32(response.sender); + transactionOnNetwork.receiver = Address.fromBech32(response.receiver); + transactionOnNetwork.gasPrice = new GasPrice(response.gasPrice); + transactionOnNetwork.gasLimit = new GasLimit(response.gasLimit); + transactionOnNetwork.data = TransactionPayload.fromEncoded(response.data); + transactionOnNetwork.status = new TransactionStatus(response.status); + transactionOnNetwork.timestamp = response.timestamp || 0; + + transactionOnNetwork.blockNonce = new Nonce(response.blockNonce || 0); + transactionOnNetwork.hyperblockNonce = new Nonce(response.hyperblockNonce || 0); + transactionOnNetwork.hyperblockHash = new Hash(response.hyperblockHash); + + return transactionOnNetwork; + } + + static fromApiHttpResponse(txHash: TransactionHash, response: any): TransactionOnNetwork { + let transactionOnNetwork = new TransactionOnNetwork(); + + transactionOnNetwork.hash = txHash; + transactionOnNetwork.type = new TypeOfTransactionOnNetwork(response.type || ""); + transactionOnNetwork.nonce = new Nonce(response.nonce || 0); + transactionOnNetwork.round = response.round; + transactionOnNetwork.epoch = response.epoch || 0; + transactionOnNetwork.value = Balance.fromString(response.value); + transactionOnNetwork.sender = Address.fromBech32(response.sender); + transactionOnNetwork.receiver = Address.fromBech32(response.receiver); + transactionOnNetwork.gasPrice = new GasPrice(response.gasPrice); + transactionOnNetwork.gasLimit = new GasLimit(response.gasLimit); + transactionOnNetwork.data = TransactionPayload.fromEncoded(response.data); + transactionOnNetwork.status = new TransactionStatus(response.status); + transactionOnNetwork.timestamp = response.timestamp || 0; + + transactionOnNetwork.blockNonce = new Nonce(response.blockNonce || 0); + transactionOnNetwork.hyperblockNonce = new Nonce(response.hyperblockNonce || 0); + transactionOnNetwork.hyperblockHash = new Hash(response.hyperblockHash); + + return transactionOnNetwork; + } + + getDateTime(): Date { + return new Date(this.timestamp * 1000); + } +} + +class TypeOfTransactionOnNetwork implements ITypeOfTransactionOnNetwork { + readonly value: string; + + constructor(value?: string) { + this.value = value || "unknown"; + } +} diff --git a/src/nonce.ts b/src/nonce.ts index 2355dd54..9c865d13 100644 --- a/src/nonce.ts +++ b/src/nonce.ts @@ -1,4 +1,5 @@ import * as errors from "./errors"; +import { numberToPaddedHex } from "./utils.codec"; /** * The nonce, as an immutable object. @@ -29,6 +30,10 @@ export class Nonce { return new Nonce(this.value + 1); } + hex(): string { + return numberToPaddedHex(this.value); + } + valueOf(): number { return this.value; } diff --git a/src/utils.codec.ts b/src/utils.codec.ts new file mode 100644 index 00000000..1e8d3cbb --- /dev/null +++ b/src/utils.codec.ts @@ -0,0 +1,10 @@ +export function numberToPaddedHex(value: number) { + let hex = value.toString(16); + let padding = "0"; + + if (hex.length % 2 == 1) { + hex = padding + hex; + } + + return hex; +} From e7583f1903bd2959d4a2ccc67ef974eb4e959dde Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Tue, 22 Feb 2022 11:54:12 +0200 Subject: [PATCH 07/15] WIP. --- src/networkProvider/providers.dev.net.spec.ts | 24 +++++++++---------- src/networkProvider/proxyNetworkProvider.ts | 4 ++-- src/networkProvider/tokens.ts | 19 +++++++++++++++ 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts index 2ef52b30..fa7448ed 100644 --- a/src/networkProvider/providers.dev.net.spec.ts +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -8,8 +8,8 @@ import { TransactionHash, TransactionStatus } from "../transaction"; import { Nonce } from "../nonce"; describe("test network providers on devnet: Proxy and API", function () { - let apiProvider: INetworkProvider = new ApiNetworkProvider("https://devnet-api.elrond.com", { timeout: 5000 }); - let proxyProvider: INetworkProvider = new ProxyNetworkProvider("https://devnet-gateway.elrond.com", { timeout: 5000 }); + let apiProvider: INetworkProvider = new ApiNetworkProvider("https://devnet-api.elrond.com", { timeout: 10000 }); + let proxyProvider: INetworkProvider = new ProxyNetworkProvider("https://devnet-gateway.elrond.com", { timeout: 10000 }); let alice: TestWallet; let bob: TestWallet; @@ -47,7 +47,7 @@ describe("test network providers on devnet: Proxy and API", function () { }); it("should have same response for getAccount()", async function () { - for (const user of [alice, bob, carol, dan]) { + for (const user of [bob, carol, dan]) { let apiResponse = await apiProvider.getAccount(user.address); let proxyResponse = await proxyProvider.getAccount(user.address); assert.deepEqual(apiResponse, proxyResponse); @@ -55,23 +55,23 @@ describe("test network providers on devnet: Proxy and API", function () { }); it("should have same response for getFungibleTokensOfAccount(), getFungibleTokenOfAccount()", async function () { - this.timeout(10000); + this.timeout(30000); - for (const user of [alice, bob, carol, dan]) { + for (const user of [carol, dan]) { let apiResponse = await apiProvider.getFungibleTokensOfAccount(user.address); let proxyResponse = await proxyProvider.getFungibleTokensOfAccount(user.address); assert.deepEqual(apiResponse.slice(0, 100), proxyResponse.slice(0, 100)); - // for (const item of apiResponse.slice(0, 5)) { - // let apiResponse = await apiProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); - // let proxyResponse = await proxyProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); - // //assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.tokenIdentifier}`); - // } + for (const item of apiResponse.slice(0, 5)) { + let apiResponse = await apiProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); + let proxyResponse = await proxyProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); + assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.tokenIdentifier}`); + } } }); - it.only("should have same response for getNonFungibleTokensOfAccount(), getNonFungibleTokenOfAccount", async function () { - this.timeout(10000); + it("should have same response for getNonFungibleTokensOfAccount(), getNonFungibleTokenOfAccount", async function () { + this.timeout(30000); for (const user of [alice, bob, carol, dan]) { let apiResponse = await apiProvider.getNonFungibleTokensOfAccount(user.address); diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index 99874e9d..b615aa13 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -94,8 +94,8 @@ export class ProxyNetworkProvider implements INetworkProvider { } async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: Nonce): Promise { - let response = await this.doGetGeneric(`address/${address.bech32()}/nft/${collection}/nonce/${nonce.hex()}`); - let tokenData = NonFungibleTokenOfAccountOnNetwork.fromProxyHttpResponse(response.tokenData); + let response = await this.doGetGeneric(`address/${address.bech32()}/nft/${collection}/nonce/${nonce.valueOf()}`); + let tokenData = NonFungibleTokenOfAccountOnNetwork.fromProxyHttpResponseByNonce(response.tokenData); return tokenData; } diff --git a/src/networkProvider/tokens.ts b/src/networkProvider/tokens.ts index 5cf66794..54a73efe 100644 --- a/src/networkProvider/tokens.ts +++ b/src/networkProvider/tokens.ts @@ -40,6 +40,20 @@ export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAc return result; } + static fromProxyHttpResponseByNonce(payload: any): NonFungibleTokenOfAccountOnNetwork { + let result = new NonFungibleTokenOfAccountOnNetwork(); + + result.attributes = Buffer.from(payload.attributes || "", "base64"); + result.balance = new BigNumber(payload.balance || 0); + result.nonce = new Nonce(payload.nonce || 0); + result.creator = new Address(payload.creator || ""); + result.royalties = new BigNumber(payload.royalties || 0).div(100); + result.tokenIdentifier = `${payload.tokenIdentifier}-${result.nonce.hex()}`; + result.collection = payload.tokenIdentifier || ""; + + return result; + } + private static parseCollectionFromIdentifier(identifier: string): string { let parts = identifier.split("-"); let collection = parts.slice(0, 2).join("-"); @@ -58,6 +72,11 @@ export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAc result.creator = new Address(payload.creator || ""); result.royalties = new BigNumber(payload.royalties || 0); + if (!payload.creator) { + console.log(payload); + throw new Error("??? api"); + } + return result; } } From e23e23ede2414fcd62522c680fbae3ff5f799db5 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Tue, 22 Feb 2022 12:31:03 +0200 Subject: [PATCH 08/15] Simplifications. --- src/interface.networkProvider.ts | 21 +++++++------------ src/networkProvider/providers.dev.net.spec.ts | 17 ++++++++++----- src/networkProvider/proxyNetworkProvider.ts | 8 ++----- src/networkProvider/transactions.ts | 12 +---------- 4 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index 30d87f07..7a7d5ea5 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -65,7 +65,7 @@ export interface INetworkProvider { /** * Fetches the state of a {@link Transaction}. */ - getTransaction(txHash: TransactionHash, hintSender?: Address): Promise; + getTransaction(txHash: TransactionHash): Promise; /** * Queries the status of a {@link Transaction}. @@ -136,18 +136,8 @@ export interface INonFungibleTokenOfAccountOnNetwork { royalties: BigNumber; } -export class Pagination { - from: number = 0; - size: number = 100; - - static default(): Pagination { - return { from: 0, size: 100 }; - } -} - export interface ITransactionOnNetwork { hash: TransactionHash; - type: ITypeOfTransactionOnNetwork; nonce: Nonce; round: number; epoch: number; @@ -165,6 +155,11 @@ export interface ITransactionOnNetwork { hyperblockHash: Hash; } - export interface ITypeOfTransactionOnNetwork { - readonly value: string; +export class Pagination { + from: number = 0; + size: number = 100; + + static default(): Pagination { + return { from: 0, size: 100 }; + } } diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts index fa7448ed..c177ad09 100644 --- a/src/networkProvider/providers.dev.net.spec.ts +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -102,17 +102,24 @@ describe("test network providers on devnet: Proxy and API", function () { for (const hash of hashes) { let apiResponse = await apiProvider.getTransaction(hash); - let proxyResponse = await proxyProvider.getTransaction(hash, sender); + let proxyResponse = await proxyProvider.getTransaction(hash); - ignoreKnownTxDifferencesBetweenProviders(apiResponse, proxyResponse); - assert.deepEqual(apiResponse, proxyResponse); + ignoreKnownTransactionDifferencesBetweenProviders(apiResponse, proxyResponse); + assert.deepEqual(apiResponse, proxyResponse, `transaction: ${hash}`); + } + + for (const hash of hashes) { + let apiResponse = await apiProvider.getTransactionStatus(hash); + let proxyResponse = await proxyProvider.getTransactionStatus(hash); + + // TODO: Fix differences of "tx.status". + // assert.deepEqual(apiResponse, proxyResponse, `transaction: ${hash}`); } }); // TODO: Strive to have as little differences as possible between Proxy and API. - function ignoreKnownTxDifferencesBetweenProviders(apiResponse: ITransactionOnNetwork, proxyResponse: ITransactionOnNetwork) { + function ignoreKnownTransactionDifferencesBetweenProviders(apiResponse: ITransactionOnNetwork, proxyResponse: ITransactionOnNetwork) { apiResponse.status = proxyResponse.status = new TransactionStatus("ignore"); - apiResponse.type = proxyResponse.type = { value: "ignore" }; // Ignore fields which are not present on API response: proxyResponse.epoch = 0; diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index b615aa13..d5ef1f8b 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -99,12 +99,8 @@ export class ProxyNetworkProvider implements INetworkProvider { return tokenData; } - async getTransaction(txHash: TransactionHash, hintSender?: Address): Promise { - let url = this.buildUrlWithQueryParameters(`transaction/${txHash.toString()}`, { - withSender: hintSender ? hintSender.bech32() : "", - withResults: "true" - }); - + async getTransaction(txHash: TransactionHash): Promise { + let url = this.buildUrlWithQueryParameters(`transaction/${txHash.toString()}`, { withResults: "true" }); let response = await this.doGetGeneric(url); let transaction = TransactionOnNetwork.fromProxyHttpResponse(txHash, response.transaction); return transaction; diff --git a/src/networkProvider/transactions.ts b/src/networkProvider/transactions.ts index 0e9a76c3..fd46e4fb 100644 --- a/src/networkProvider/transactions.ts +++ b/src/networkProvider/transactions.ts @@ -1,7 +1,7 @@ import { Address } from "../address"; import { Balance } from "../balance"; import { Hash } from "../hash"; -import { ITransactionOnNetwork, ITypeOfTransactionOnNetwork } from "../interface.networkProvider"; +import { ITransactionOnNetwork } from "../interface.networkProvider"; import { GasLimit, GasPrice } from "../networkParams"; import { Nonce } from "../nonce"; import { Signature } from "../signature"; @@ -10,7 +10,6 @@ import { TransactionPayload } from "../transactionPayload"; export class TransactionOnNetwork implements ITransactionOnNetwork { hash: TransactionHash = new TransactionHash(""); - type: ITypeOfTransactionOnNetwork = new TypeOfTransactionOnNetwork(); nonce: Nonce = new Nonce(0); round: number = 0; epoch: number = 0; @@ -32,7 +31,6 @@ import { TransactionPayload } from "../transactionPayload"; let transactionOnNetwork = new TransactionOnNetwork(); transactionOnNetwork.hash = txHash; - transactionOnNetwork.type = new TypeOfTransactionOnNetwork(response.type || ""); transactionOnNetwork.nonce = new Nonce(response.nonce || 0); transactionOnNetwork.round = response.round; transactionOnNetwork.epoch = response.epoch || 0; @@ -56,7 +54,6 @@ import { TransactionPayload } from "../transactionPayload"; let transactionOnNetwork = new TransactionOnNetwork(); transactionOnNetwork.hash = txHash; - transactionOnNetwork.type = new TypeOfTransactionOnNetwork(response.type || ""); transactionOnNetwork.nonce = new Nonce(response.nonce || 0); transactionOnNetwork.round = response.round; transactionOnNetwork.epoch = response.epoch || 0; @@ -81,10 +78,3 @@ import { TransactionPayload } from "../transactionPayload"; } } -class TypeOfTransactionOnNetwork implements ITypeOfTransactionOnNetwork { - readonly value: string; - - constructor(value?: string) { - this.value = value || "unknown"; - } -} From 5ef2a443865b45c32e6bf07d97ae16b5535d7521 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Tue, 22 Feb 2022 20:19:20 +0200 Subject: [PATCH 09/15] WIP. --- src/interface.networkProvider.ts | 50 ++++++++++--- src/networkProvider/apiNetworkProvider.ts | 31 ++++---- src/networkProvider/providers.dev.net.spec.ts | 58 +++++++++++++-- src/networkProvider/proxyNetworkProvider.ts | 19 ++--- src/networkProvider/tokenDefinitions.ts | 73 +++++++++++++++++++ src/networkProvider/tokens.ts | 25 +++---- 6 files changed, 197 insertions(+), 59 deletions(-) create mode 100644 src/networkProvider/tokenDefinitions.ts diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index 7a7d5ea5..da813090 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -91,24 +91,20 @@ export interface INetworkProvider { /** * Fetches the definition of a fungible token. - * - * TODO: Rename to "GetDefinitionOfFungibleToken" or "GetFungibleTokenDefinition" (not renamed yet in order to preserve interface compatibility). + * */ - getToken(tokenIdentifier: string): Promise; + getDefinitionOfFungibleToken(tokenIdentifier: string): Promise; /** * Fetches the definition of a SFT (including Meta ESDT) or NFT. * - * TODO: Return explicit type. */ - getDefinitionOfTokenCollection(collection: string): Promise; + getDefinitionOfTokenCollection(collection: string): Promise; /** - * Fetches an instance of a SFT (including Meta ESDT) or NFT. - * - * TODO: Rename to "GetTokenInstance" (not renamed yet in order to preserve interface compatibility). + * Fetches data about a specific non-fungible token (instance). */ - getNFTToken(tokenIdentifier: string): Promise; + getNonFungibleToken(collection: string, nonce: Nonce): Promise; /** * Performs a generic GET action against the provider (useful for new HTTP endpoints, not yet supported by erdjs). @@ -122,12 +118,12 @@ export interface INetworkProvider { } export interface IFungibleTokenOfAccountOnNetwork { - tokenIdentifier: string; + identifier: string; balance: BigNumber; } export interface INonFungibleTokenOfAccountOnNetwork { - tokenIdentifier: string; + identifier: string; collection: string; attributes: Buffer; balance: BigNumber; @@ -155,6 +151,38 @@ export interface ITransactionOnNetwork { hyperblockHash: Hash; } +export interface IDefinitionOfFungibleTokenOnNetwork { + identifier: string; + name: string; + ticker: string; + owner: Address; + decimals: number; + supply: BigNumber; + isPaused: boolean; + canUpgrade: boolean; + canMint: boolean; + canBurn: boolean; + canChangeOwner: boolean; + canPause: boolean; + canFreeze: boolean; + canWipe: boolean; + canAddSpecialRoles: boolean; +} + +export interface IDefinitionOfTokenCollectionOnNetwork { + collection: string; + type: string; + name: string; + ticker: string; + owner: Address; + decimals: number; + canPause: boolean; + canFreeze: boolean; + canWipe: boolean; + canTransferRole: boolean; + // TODO: add "assets", "roles" +} + export class Pagination { from: number = 0; size: number = 100; diff --git a/src/networkProvider/apiNetworkProvider.ts b/src/networkProvider/apiNetworkProvider.ts index 93eddac8..f27e1f65 100644 --- a/src/networkProvider/apiNetworkProvider.ts +++ b/src/networkProvider/apiNetworkProvider.ts @@ -1,21 +1,19 @@ import axios, { AxiosRequestConfig } from "axios"; -import { BigNumber } from "bignumber.js"; import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrApiProviderGet, ErrContractQuery } from "../errors"; -import { IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; +import { IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; import { NetworkStatus } from "../networkStatus"; -import { NFTToken } from "../nftToken"; import { Nonce } from "../nonce"; import { Query, QueryResponse } from "../smartcontracts"; import { Stats } from "../stats"; -import { Token } from "../token"; import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; import { ProxyNetworkProvider } from "./proxyNetworkProvider"; +import { DefinitionOfFungibleTokenOnNetwork, DefinitionOfTokenCollectionOnNetwork } from "./tokenDefinitions"; import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; import { TransactionOnNetwork } from "./transactions"; @@ -69,7 +67,7 @@ export class ApiNetworkProvider implements INetworkProvider { let tokens = response.map(item => FungibleTokenOfAccountOnNetwork.fromHttpResponse(item)); // TODO: Fix sorting - tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); + tokens.sort((a, b) => a.identifier.localeCompare(b.identifier)); return tokens; } @@ -81,7 +79,7 @@ export class ApiNetworkProvider implements INetworkProvider { let tokens = response.map(item => NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(item)); // TODO: Fix sorting - tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); + tokens.sort((a, b) => a.identifier.localeCompare(b.identifier)); return tokens; } @@ -131,21 +129,22 @@ export class ApiNetworkProvider implements INetworkProvider { } } - async getToken(tokenIdentifier: string): Promise { + async getDefinitionOfFungibleToken(tokenIdentifier: string): Promise { let response = await this.doGetGeneric(`tokens/${tokenIdentifier}`); - let token = Token.fromHttpResponse(response); - return token; + let definition = DefinitionOfFungibleTokenOnNetwork.fromApiHttpResponse(response); + return definition; } - async getNFTToken(tokenIdentifier: string): Promise { - let response = await this.doGetGeneric(`nfts/${tokenIdentifier}`); - let token = NFTToken.fromHttpResponse(response); - return token; + async getDefinitionOfTokenCollection(collection: string): Promise { + let response = await this.doGetGeneric(`collections/${collection}`); + let definition = DefinitionOfTokenCollectionOnNetwork.fromApiHttpResponse(response); + return definition; } - async getDefinitionOfTokenCollection(collection: string): Promise { - let response = await this.doGetGeneric(`collections/${collection}`); - return response; + async getNonFungibleToken(collection: string, nonce: Nonce): Promise { + let response = await this.doGetGeneric(`nfts/${collection}-${nonce.hex()}`); + let token = NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(response); + return token; } async doGetGeneric(resourceUrl: string): Promise { diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts index c177ad09..871a282d 100644 --- a/src/networkProvider/providers.dev.net.spec.ts +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -63,9 +63,9 @@ describe("test network providers on devnet: Proxy and API", function () { assert.deepEqual(apiResponse.slice(0, 100), proxyResponse.slice(0, 100)); for (const item of apiResponse.slice(0, 5)) { - let apiResponse = await apiProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); - let proxyResponse = await proxyProvider.getFungibleTokenOfAccount(user.address, item.tokenIdentifier); - assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.tokenIdentifier}`); + let apiResponse = await apiProvider.getFungibleTokenOfAccount(user.address, item.identifier); + let proxyResponse = await proxyProvider.getFungibleTokenOfAccount(user.address, item.identifier); + assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.identifier}`); } } }); @@ -81,7 +81,7 @@ describe("test network providers on devnet: Proxy and API", function () { for (const item of apiResponse.slice(0, 5)) { let apiResponse = await apiProvider.getNonFungibleTokenOfAccount(user.address, item.collection, item.nonce); let proxyResponse = await proxyProvider.getNonFungibleTokenOfAccount(user.address, item.collection, item.nonce); - assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.tokenIdentifier}`); + assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.identifier}`); } } }); @@ -103,7 +103,7 @@ describe("test network providers on devnet: Proxy and API", function () { for (const hash of hashes) { let apiResponse = await apiProvider.getTransaction(hash); let proxyResponse = await proxyProvider.getTransaction(hash); - + ignoreKnownTransactionDifferencesBetweenProviders(apiResponse, proxyResponse); assert.deepEqual(apiResponse, proxyResponse, `transaction: ${hash}`); } @@ -111,7 +111,7 @@ describe("test network providers on devnet: Proxy and API", function () { for (const hash of hashes) { let apiResponse = await apiProvider.getTransactionStatus(hash); let proxyResponse = await proxyProvider.getTransactionStatus(hash); - + // TODO: Fix differences of "tx.status". // assert.deepEqual(apiResponse, proxyResponse, `transaction: ${hash}`); } @@ -127,4 +127,50 @@ describe("test network providers on devnet: Proxy and API", function () { proxyResponse.hyperblockNonce = new Nonce(0); proxyResponse.hyperblockHash = new Hash(""); } + + it("should have same response for getDefinitionOfFungibleToken()", async function () { + this.timeout(10000); + + let identifiers = ["MEX-b6bb7d", "WEGLD-88600a", "RIDE-482531", "USDC-a32906"]; + + for (const identifier of identifiers) { + let apiResponse = await apiProvider.getDefinitionOfFungibleToken(identifier); + + assert.equal(apiResponse.identifier, identifier); + + // TODO: Uncomment after implementing the function in the proxy provider. + // let proxyResponse = await proxyProvider.getDefinitionOfFungibleToken(identifier); + // assert.deepEqual(apiResponse, proxyResponse); + } + }); + + it("should have same response for getDefinitionOfTokenCollection()", async function () { + this.timeout(10000); + + let collections = ["LKMEX-9acade", "LKFARM-c20c1c", "MEXFARM-bab93a", "ART-264971", "MOS-ff0040"]; + + for (const collection of collections) { + let apiResponse = await apiProvider.getDefinitionOfTokenCollection(collection); + assert.equal(apiResponse.collection, collection); + + // TODO: Uncomment after implementing the function in the proxy provider. + // let proxyResponse = await proxyProvider.getDefinitionOfTokenCollection(identifier); + // assert.deepEqual(apiResponse, proxyResponse); + } + }); + + it("should have same response for getNonFungibleToken()", async function () { + this.timeout(10000); + + let tokens = [{ id: "ERDJSNFT-4a5669", nonce: new Nonce(1) }]; + + for (const token of tokens) { + let apiResponse = await apiProvider.getNonFungibleToken(token.id, token.nonce); + assert.equal(apiResponse.collection, token.id); + + // TODO: Uncomment after implementing the function in the proxy provider. + // let proxyResponse = await proxyProvider.getNonFungibleToken(token.id, token.nonce); + // assert.deepEqual(apiResponse, proxyResponse); + } + }); }); diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index d5ef1f8b..0db43da3 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -3,16 +3,14 @@ import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrApiProviderGet, ErrContractQuery } from "../errors"; -import { IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; +import { IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; import { NetworkStatus } from "../networkStatus"; -import { NFTToken } from "../nftToken"; import { Nonce } from "../nonce"; import { Query, QueryResponse } from "../smartcontracts"; import { Stats } from "../stats"; -import { Token } from "../token"; import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; import { TransactionOnNetwork } from "./transactions"; @@ -70,7 +68,7 @@ export class ProxyNetworkProvider implements INetworkProvider { let tokens = responseItemsFiltered.map(item => FungibleTokenOfAccountOnNetwork.fromHttpResponse(item)); // TODO: Fix sorting - tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); + tokens.sort((a, b) => a.identifier.localeCompare(b.identifier)); return tokens; } @@ -83,7 +81,7 @@ export class ProxyNetworkProvider implements INetworkProvider { let tokens = responseItemsFiltered.map(item => NonFungibleTokenOfAccountOnNetwork.fromProxyHttpResponse(item)); // TODO: Fix sorting - tokens.sort((a, b) => a.tokenIdentifier.localeCompare(b.tokenIdentifier)); + tokens.sort((a, b) => a.identifier.localeCompare(b.identifier)); return tokens; } @@ -134,21 +132,20 @@ export class ProxyNetworkProvider implements INetworkProvider { } } - async getToken(_tokenIdentifier: string): Promise { + async getDefinitionOfFungibleToken(_tokenIdentifier: string): Promise { // TODO: Implement wrt.: // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/esdt/esdt.service.ts#L221 throw new Error("Method not implemented."); } - async getNFTToken(_tokenIdentifier: string): Promise { + async getDefinitionOfTokenCollection(_collection: string): Promise { // TODO: Implement wrt.: + // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/collections/collection.service.ts + // https://docs.elrond.com/developers/esdt-tokens/#get-esdt-token-properties throw new Error("Method not implemented."); } - async getDefinitionOfTokenCollection(_collection: string): Promise { - // TODO: Implement wrt.: - // https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/collections/collection.service.ts - // https://docs.elrond.com/developers/esdt-tokens/#get-esdt-token-properties + async getNonFungibleToken(_collection: string, _nonce: Nonce): Promise { throw new Error("Method not implemented."); } diff --git a/src/networkProvider/tokenDefinitions.ts b/src/networkProvider/tokenDefinitions.ts new file mode 100644 index 00000000..407e05d1 --- /dev/null +++ b/src/networkProvider/tokenDefinitions.ts @@ -0,0 +1,73 @@ +import { BigNumber } from "bignumber.js"; +import { Address } from "../address"; +import { IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INonFungibleTokenOfAccountOnNetwork } from "../interface.networkProvider"; + +export class DefinitionOfFungibleTokenOnNetwork implements IDefinitionOfFungibleTokenOnNetwork { + identifier: string = ""; + name: string = ""; + ticker: string = ""; + owner: Address = new Address(); + decimals: number = 0; + supply: BigNumber = new BigNumber(0); + isPaused: boolean = false; + canUpgrade: boolean = false; + canMint: boolean = false; + canBurn: boolean = false; + canChangeOwner: boolean = false; + canPause: boolean = false; + canFreeze: boolean = false; + canWipe: boolean = false; + canAddSpecialRoles: boolean = false; + + static fromApiHttpResponse(payload: any): DefinitionOfFungibleTokenOnNetwork { + let result = new DefinitionOfFungibleTokenOnNetwork(); + + result.identifier = payload.identifier || ""; + result.name = payload.name || ""; + result.ticker = payload.ticker || ""; + result.owner = new Address(payload.owner || ""); + result.decimals = payload.decimals || 0; + result.supply = new BigNumber(payload.supply || "0"); + result.isPaused = payload.isPaused || false; + result.canUpgrade = payload.canUpgrade || false; + result.canMint = payload.canMint || false; + result.canBurn = payload.canBurn || false; + result.canChangeOwner = payload.canChangeOwner || false; + result.canPause = payload.canPause || false; + result.canFreeze = payload.canFreeze || false; + result.canWipe = payload.canWipe || false; + result.canAddSpecialRoles = payload.canAddSpecialRoles || false; + + return result; + } +} + +export class DefinitionOfTokenCollectionOnNetwork implements IDefinitionOfTokenCollectionOnNetwork { + collection: string = ""; + type: string = ""; + name: string = ""; + ticker: string = ""; + owner: Address = new Address(); + decimals: number = 0; + canPause: boolean = false; + canFreeze: boolean = false; + canWipe: boolean = false; + canTransferRole: boolean = false; + + static fromApiHttpResponse(payload: any): DefinitionOfTokenCollectionOnNetwork { + let result = new DefinitionOfTokenCollectionOnNetwork(); + + result.collection = payload.collection || ""; + result.type = payload.type || ""; + result.name = payload.name || ""; + result.ticker = payload.ticker || ""; + result.owner = new Address(payload.owner || ""); + result.decimals = payload.decimals || 0; + result.canPause = payload.canPause || false; + result.canFreeze = payload.canFreeze || false; + result.canWipe = payload.canWipe || false; + result.canTransferRole = payload.canTransferRole || false; + + return result; + } +} diff --git a/src/networkProvider/tokens.ts b/src/networkProvider/tokens.ts index 54a73efe..235c33f8 100644 --- a/src/networkProvider/tokens.ts +++ b/src/networkProvider/tokens.ts @@ -4,13 +4,13 @@ import { IFungibleTokenOfAccountOnNetwork, INonFungibleTokenOfAccountOnNetwork } import { Nonce } from "../nonce"; export class FungibleTokenOfAccountOnNetwork implements IFungibleTokenOfAccountOnNetwork { - tokenIdentifier: string = ""; + identifier: string = ""; balance: BigNumber = new BigNumber(0); - + static fromHttpResponse(payload: any): FungibleTokenOfAccountOnNetwork { let result = new FungibleTokenOfAccountOnNetwork(); - result.tokenIdentifier = payload.tokenIdentifier || payload.identifier || ""; + result.identifier = payload.tokenIdentifier || payload.identifier || ""; result.balance = new BigNumber(payload.balance || 0); return result; @@ -18,7 +18,7 @@ export class FungibleTokenOfAccountOnNetwork implements IFungibleTokenOfAccountO } export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAccountOnNetwork { - tokenIdentifier: string = ""; + identifier: string = ""; collection: string = ""; attributes: Buffer = Buffer.from([]); balance: BigNumber = new BigNumber(0); @@ -29,8 +29,8 @@ export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAc static fromProxyHttpResponse(payload: any): NonFungibleTokenOfAccountOnNetwork { let result = new NonFungibleTokenOfAccountOnNetwork(); - result.tokenIdentifier = payload.tokenIdentifier || ""; - result.collection = NonFungibleTokenOfAccountOnNetwork.parseCollectionFromIdentifier(result.tokenIdentifier); + result.identifier = payload.tokenIdentifier || ""; + result.collection = NonFungibleTokenOfAccountOnNetwork.parseCollectionFromIdentifier(result.identifier); result.attributes = Buffer.from(payload.attributes || "", "base64"); result.balance = new BigNumber(payload.balance || 0); result.nonce = new Nonce(payload.nonce || 0); @@ -48,9 +48,9 @@ export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAc result.nonce = new Nonce(payload.nonce || 0); result.creator = new Address(payload.creator || ""); result.royalties = new BigNumber(payload.royalties || 0).div(100); - result.tokenIdentifier = `${payload.tokenIdentifier}-${result.nonce.hex()}`; + result.identifier = `${payload.tokenIdentifier}-${result.nonce.hex()}`; result.collection = payload.tokenIdentifier || ""; - + return result; } @@ -63,7 +63,7 @@ export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAc static fromApiHttpResponse(payload: any): NonFungibleTokenOfAccountOnNetwork { let result = new NonFungibleTokenOfAccountOnNetwork(); - result.tokenIdentifier = payload.identifier || ""; + result.identifier = payload.identifier || ""; result.collection = payload.collection || ""; result.attributes = Buffer.from(payload.attributes || "", "base64"); // On API, missing balance means NFT. @@ -71,12 +71,7 @@ export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAc result.nonce = new Nonce(payload.nonce || 0); result.creator = new Address(payload.creator || ""); result.royalties = new BigNumber(payload.royalties || 0); - - if (!payload.creator) { - console.log(payload); - throw new Error("??? api"); - } - + return result; } } From 6c004e17b9057b1644fdba89960f1d681ee58e6d Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Wed, 23 Feb 2022 12:31:27 +0200 Subject: [PATCH 10/15] Contract queries. WIP. --- src/errors.ts | 10 ++++ src/interface.networkProvider.ts | 16 ++++-- src/networkProvider/apiNetworkProvider.ts | 25 ++++----- src/networkProvider/contractResults.ts | 34 ++++++++++++ src/networkProvider/providers.dev.net.spec.ts | 53 +++++++++++++++++++ src/networkProvider/proxyNetworkProvider.ts | 25 ++++----- src/smartcontracts/returnCode.ts | 2 - 7 files changed, 131 insertions(+), 34 deletions(-) create mode 100644 src/networkProvider/contractResults.ts diff --git a/src/errors.ts b/src/errors.ts index 54b783fa..5aa28431 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -272,6 +272,16 @@ export class ErrInvalidFunctionName extends Err { } } +/** + * Signals an error that happened during a request against the Network. + */ + export class ErrNetworkProvider extends Err { + public constructor(url: string, error: string, inner?: Error) { + let message = `Request error on url [${url}]: [${error}]`; + super(message, inner); + } +} + /** * Signals an error that happened during a HTTP GET request. */ diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index da813090..eff8eb5a 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -6,11 +6,9 @@ import { Hash } from "./hash"; import { NetworkConfig } from "./networkConfig"; import { NetworkStake } from "./networkStake"; import { NetworkStatus } from "./networkStatus"; -import { NFTToken } from "./nftToken"; import { Signature } from "./signature"; -import { Query, QueryResponse } from "./smartcontracts"; +import { EndpointDefinition, Query, ReturnCode, TypedValue } from "./smartcontracts"; import { Stats } from "./stats"; -import { Token } from "./token"; import { Transaction, TransactionHash, TransactionStatus } from "./transaction"; /** @@ -87,7 +85,7 @@ export interface INetworkProvider { /** * Queries a Smart Contract - runs a pure function defined by the contract and returns its results. */ - queryContract(query: Query): Promise; + queryContract(query: Query): Promise; /** * Fetches the definition of a fungible token. @@ -183,6 +181,16 @@ export interface IDefinitionOfTokenCollectionOnNetwork { // TODO: add "assets", "roles" } +export interface IContractQueryResponse { + returnData: string[]; + returnCode: ReturnCode; + returnMessage: string; + gasUsed: GasLimit; + + getOutputUntyped(): Buffer[]; + getOutputTyped(endpointDefinition: EndpointDefinition): TypedValue[]; +} + export class Pagination { from: number = 0; size: number = 100; diff --git a/src/networkProvider/apiNetworkProvider.ts b/src/networkProvider/apiNetworkProvider.ts index f27e1f65..1b87925c 100644 --- a/src/networkProvider/apiNetworkProvider.ts +++ b/src/networkProvider/apiNetworkProvider.ts @@ -2,16 +2,17 @@ import axios, { AxiosRequestConfig } from "axios"; import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; -import { ErrApiProviderGet, ErrContractQuery } from "../errors"; -import { IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; +import { ErrNetworkProvider } from "../errors"; +import { IContractQueryResponse, IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; import { NetworkStatus } from "../networkStatus"; import { Nonce } from "../nonce"; -import { Query, QueryResponse } from "../smartcontracts"; +import { Query } from "../smartcontracts"; import { Stats } from "../stats"; import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; +import { ContractQueryResponse } from "./contractResults"; import { ProxyNetworkProvider } from "./proxyNetworkProvider"; import { DefinitionOfFungibleTokenOnNetwork, DefinitionOfTokenCollectionOnNetwork } from "./tokenDefinitions"; import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; @@ -118,15 +119,11 @@ export class ApiNetworkProvider implements INetworkProvider { return await this.backingProxyNetworkProvider.simulateTransaction(tx); } - async queryContract(query: Query): Promise { - try { - let data = query.toHttpRequest(); - let response = await this.doPostGeneric("query", data); - let queryResponse = QueryResponse.fromHttpResponse(response) - return queryResponse; - } catch (err: any) { - throw ErrContractQuery.increaseSpecificity(err); - } + async queryContract(query: Query): Promise { + let data = query.toHttpRequest(); + let response = await this.doPostGeneric("query", data); + let queryResponse = ContractQueryResponse.fromHttpResponse(response); + return queryResponse; } async getDefinitionOfFungibleToken(tokenIdentifier: string): Promise { @@ -191,11 +188,11 @@ export class ApiNetworkProvider implements INetworkProvider { private handleApiError(error: any, resourceUrl: string) { if (!error.response) { Logger.warn(error); - throw new ErrApiProviderGet(resourceUrl, error.toString(), error); + throw new ErrNetworkProvider(resourceUrl, error.toString(), error); } let errorData = error.response.data; let originalErrorMessage = errorData.error || errorData.message || JSON.stringify(errorData); - throw new ErrApiProviderGet(resourceUrl, originalErrorMessage, error); + throw new ErrNetworkProvider(resourceUrl, originalErrorMessage, error); } } diff --git a/src/networkProvider/contractResults.ts b/src/networkProvider/contractResults.ts new file mode 100644 index 00000000..457dd648 --- /dev/null +++ b/src/networkProvider/contractResults.ts @@ -0,0 +1,34 @@ +import { BigNumber } from "bignumber.js"; +import { IContractQueryResponse } from "../interface.networkProvider"; +import { GasLimit } from "../networkParams"; +import { ArgSerializer, EndpointDefinition, MaxUint64, ReturnCode, TypedValue } from "../smartcontracts"; + +export class ContractQueryResponse implements IContractQueryResponse { + returnData: string[] = []; + returnCode: ReturnCode = ReturnCode.None; + returnMessage: string = ""; + gasUsed: GasLimit = new GasLimit(0); + + static fromHttpResponse(payload: any): ContractQueryResponse { + let response = new ContractQueryResponse(); + let gasRemaining = new BigNumber(payload["gasRemaining"] || payload["GasRemaining"] || 0); + + response.returnData = payload["returnData"] || []; + response.returnCode = payload["returnCode"] || ""; + response.returnMessage = payload["returnMessage"] || ""; + response.gasUsed = new GasLimit(MaxUint64.minus(gasRemaining).toNumber()); + + return response; + } + + getOutputUntyped(): Buffer[] { + let buffers = this.returnData.map((item) => Buffer.from(item || "", "base64")); + return buffers; + } + + getOutputTyped(endpointDefinition: EndpointDefinition): TypedValue[] { + let buffers = this.getOutputUntyped(); + let values = new ArgSerializer().buffersToValues(buffers, endpointDefinition!.output); + return values; + } +} diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts index 871a282d..63aa28e4 100644 --- a/src/networkProvider/providers.dev.net.spec.ts +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -6,6 +6,10 @@ import { Address } from "../address"; import { loadTestWallets, TestWallet } from "../testutils"; import { TransactionHash, TransactionStatus } from "../transaction"; import { Nonce } from "../nonce"; +import { ContractFunction, Query } from "../smartcontracts"; +import { BigUIntValue, U32Value, BytesValue, VariadicValue, VariadicType, CompositeType, BytesType, BooleanType } from "../smartcontracts/typesystem"; +import { BigNumber } from "bignumber.js"; +import { Balance } from "../balance"; describe("test network providers on devnet: Proxy and API", function () { let apiProvider: INetworkProvider = new ApiNetworkProvider("https://devnet-api.elrond.com", { timeout: 10000 }); @@ -173,4 +177,53 @@ describe("test network providers on devnet: Proxy and API", function () { // assert.deepEqual(apiResponse, proxyResponse); } }); + + // TODO: enable when API fixes the imprecision around "gasRemaining". + // TODO: enable when API supports queries with "value". + it.skip("should have same response for queryContract()", async function () { + this.timeout(10000); + + // Query: get ultimate answer + let query = new Query({ + address: new Address("erd1qqqqqqqqqqqqqpgqggww7tjryk9saqzfpq09tw3vm06kl8h3396qqz277y"), + func: new ContractFunction("getUltimateAnswer"), + args: [] + }); + + let apiResponse = await apiProvider.queryContract(query); + let proxyResponse = await proxyProvider.queryContract(query); + assert.deepEqual(apiResponse, proxyResponse); + assert.deepEqual(apiResponse.getOutputUntyped(), proxyResponse.getOutputUntyped()); + + // Query: increment counter + query = new Query({ + address: new Address("erd1qqqqqqqqqqqqqpgqz045rw74nthgzw2te9lytgah775n3l08396q3wt4qq"), + func: new ContractFunction("increment"), + args: [] + }); + + apiResponse = await apiProvider.queryContract(query); + proxyResponse = await proxyProvider.queryContract(query); + assert.deepEqual(apiResponse, proxyResponse); + assert.deepEqual(apiResponse.getOutputUntyped(), proxyResponse.getOutputUntyped()); + + // Query: issue ESDT + query = new Query({ + address: new Address("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"), + func: new ContractFunction("issue"), + value: Balance.egld(0.05), + args: [ + BytesValue.fromUTF8("FOO"), + BytesValue.fromUTF8("FOO"), + new BigUIntValue(new BigNumber("10000")), + new U32Value(18), + new VariadicValue(new VariadicType(new CompositeType(new BytesType(), new BooleanType())), []) + ] + }); + + apiResponse = await apiProvider.queryContract(query); + proxyResponse = await proxyProvider.queryContract(query); + assert.deepEqual(apiResponse, proxyResponse); + assert.deepEqual(apiResponse.getOutputUntyped(), proxyResponse.getOutputUntyped()); + }); }); diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index 0db43da3..c9f1d348 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -2,16 +2,17 @@ import axios, { AxiosRequestConfig } from "axios"; import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; -import { ErrApiProviderGet, ErrContractQuery } from "../errors"; -import { IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; +import { ErrNetworkProvider } from "../errors"; +import { IContractQueryResponse, IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; import { NetworkStatus } from "../networkStatus"; import { Nonce } from "../nonce"; -import { Query, QueryResponse } from "../smartcontracts"; +import { Query } from "../smartcontracts"; import { Stats } from "../stats"; import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; +import { ContractQueryResponse } from "./contractResults"; import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; import { TransactionOnNetwork } from "./transactions"; @@ -121,15 +122,11 @@ export class ProxyNetworkProvider implements INetworkProvider { return response; } - async queryContract(query: Query): Promise { - try { - let data = query.toHttpRequest(); - let response = await this.doPostGeneric("vm-values/query", data); - let queryResponse = QueryResponse.fromHttpResponse(response.data) - return queryResponse; - } catch (err: any) { - throw ErrContractQuery.increaseSpecificity(err); - } + async queryContract(query: Query): Promise { + let data = query.toHttpRequest(); + let response = await this.doPostGeneric("vm-values/query", data); + let queryResponse = ContractQueryResponse.fromHttpResponse(response.data); + return queryResponse; } async getDefinitionOfFungibleToken(_tokenIdentifier: string): Promise { @@ -201,11 +198,11 @@ export class ProxyNetworkProvider implements INetworkProvider { private handleApiError(error: any, resourceUrl: string) { if (!error.response) { Logger.warn(error); - throw new ErrApiProviderGet(resourceUrl, error.toString(), error); + throw new ErrNetworkProvider(resourceUrl, error.toString(), error); } let errorData = error.response.data; let originalErrorMessage = errorData.error || errorData.message || JSON.stringify(errorData); - throw new ErrApiProviderGet(resourceUrl, originalErrorMessage, error); + throw new ErrNetworkProvider(resourceUrl, originalErrorMessage, error); } } diff --git a/src/smartcontracts/returnCode.ts b/src/smartcontracts/returnCode.ts index 8d4dfe40..b537062e 100644 --- a/src/smartcontracts/returnCode.ts +++ b/src/smartcontracts/returnCode.ts @@ -1,5 +1,3 @@ -import { EndpointDefinition } from "./typesystem"; - export class ReturnCode { static None = new ReturnCode(""); static Ok = new ReturnCode("ok"); From 46cb583fcbec6a8485f02fa36917368e63b23f50 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Wed, 23 Feb 2022 14:57:44 +0200 Subject: [PATCH 11/15] Logs, results, refactor, WIP. --- src/interface.networkProvider.ts | 22 +++++++++++ src/networkProvider/contractResults.ts | 35 ++++++++++++++++- src/networkProvider/providers.dev.net.spec.ts | 8 ++-- src/networkProvider/tokens.ts | 38 +++++++++---------- 4 files changed, 78 insertions(+), 25 deletions(-) diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index eff8eb5a..790bc11e 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -10,6 +10,7 @@ import { Signature } from "./signature"; import { EndpointDefinition, Query, ReturnCode, TypedValue } from "./smartcontracts"; import { Stats } from "./stats"; import { Transaction, TransactionHash, TransactionStatus } from "./transaction"; +import { TransactionLogs } from "./transactionLogs"; /** * An interface that defines the endpoints of an HTTP API Provider. @@ -147,6 +148,27 @@ export interface ITransactionOnNetwork { blockNonce: Nonce; hyperblockNonce: Nonce; hyperblockHash: Hash; + logs: TransactionLogs; + contractResults: IContractResults; +} + +export interface IContractResults { + items: IContractResultItem[]; +} + +export interface IContractResultItem { + hash: Hash; + nonce: Nonce; + value: Balance; + receiver: Address; + sender: Address; + data: string; + previousHash: Hash; + originalHash: Hash; + gasLimit: GasLimit; + gasPrice: GasPrice; + callType: number; + returnMessage: string; } export interface IDefinitionOfFungibleTokenOnNetwork { diff --git a/src/networkProvider/contractResults.ts b/src/networkProvider/contractResults.ts index 457dd648..0915d525 100644 --- a/src/networkProvider/contractResults.ts +++ b/src/networkProvider/contractResults.ts @@ -1,8 +1,39 @@ import { BigNumber } from "bignumber.js"; -import { IContractQueryResponse } from "../interface.networkProvider"; -import { GasLimit } from "../networkParams"; +import { Address } from "../address"; +import { Balance } from "../balance"; +import { Hash } from "../hash"; +import { IContractQueryResponse, IContractResultItem, IContractResults } from "../interface.networkProvider"; +import { GasLimit, GasPrice } from "../networkParams"; +import { Nonce } from "../nonce"; import { ArgSerializer, EndpointDefinition, MaxUint64, ReturnCode, TypedValue } from "../smartcontracts"; +export class ContractResults implements IContractResults { + readonly items: IContractResultItem[]; + + constructor(items: IContractResultItem[]) { + this.items = items; + } + + static empty(): ContractResults { + return new ContractResults([]); + } +} + +export class ContractResultItem implements IContractResultItem { + hash: Hash = Hash.empty(); + nonce: Nonce = new Nonce(0); + value: Balance = Balance.Zero(); + receiver: Address = new Address(); + sender: Address = new Address(); + data: string = ""; + previousHash: Hash = Hash.empty(); + originalHash: Hash = Hash.empty(); + gasLimit: GasLimit = new GasLimit(0); + gasPrice: GasPrice = new GasPrice(0); + callType: number = 0; + returnMessage: string = ""; +} + export class ContractQueryResponse implements IContractQueryResponse { returnData: string[] = []; returnCode: ReturnCode = ReturnCode.None; diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts index 63aa28e4..817bc6f7 100644 --- a/src/networkProvider/providers.dev.net.spec.ts +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -93,15 +93,17 @@ describe("test network providers on devnet: Proxy and API", function () { it("should have same response for getTransaction(), getTransactionStatus()", async function () { this.timeout(20000); - let sender = new Address("erd1testnlersh4z0wsv8kjx39me4rmnvjkwu8dsaea7ukdvvc9z396qykv7z7"); - let hashes = [ new TransactionHash("b41f5fc39e96b1f194d07761c6efd6cb92278b95f5012ab12cbc910058ca8b54"), new TransactionHash("7757397a59378e9d0f6d5f08cc934c260e33a50ae0d73fdf869f7c02b6b47b33"), new TransactionHash("b87238089e81527158a6daee520280324bc7e5322ba54d1b3c9a5678abe953ea"), new TransactionHash("b45dd5e598bc85ba71639f2cbce8c5dff2fbe93159e637852fddeb16c0e84a48"), new TransactionHash("83db780e98d4d3c917668c47b33ba51445591efacb0df2a922f88e7dfbb5fc7d"), - new TransactionHash("c2eb62b28cc7320da2292d87944c5424a70e1f443323c138c1affada7f6e9705") + new TransactionHash("c2eb62b28cc7320da2292d87944c5424a70e1f443323c138c1affada7f6e9705"), + new TransactionHash("98e913c2a78cafdf4fa7f0113c1285fb29c2409bd7a746bb6f5506ad76841d54"), + new TransactionHash("5b05945be8ba2635e7c13d792ad727533494358308b5fcf36a816e52b5b272b8"), + new TransactionHash("47b089b5f0220299a017359003694a01fd75d075100166b8072c418d5143fe06"), + new TransactionHash("85021f20b06662240d8302d62f68031bbf7261bacb53b84e3dc9346c0f10a8e7") ]; for (const hash of hashes) { diff --git a/src/networkProvider/tokens.ts b/src/networkProvider/tokens.ts index 235c33f8..716de777 100644 --- a/src/networkProvider/tokens.ts +++ b/src/networkProvider/tokens.ts @@ -27,51 +27,49 @@ export class NonFungibleTokenOfAccountOnNetwork implements INonFungibleTokenOfAc royalties: BigNumber = new BigNumber(0); static fromProxyHttpResponse(payload: any): NonFungibleTokenOfAccountOnNetwork { - let result = new NonFungibleTokenOfAccountOnNetwork(); + let result = NonFungibleTokenOfAccountOnNetwork.fromHttpResponse(payload); result.identifier = payload.tokenIdentifier || ""; result.collection = NonFungibleTokenOfAccountOnNetwork.parseCollectionFromIdentifier(result.identifier); - result.attributes = Buffer.from(payload.attributes || "", "base64"); - result.balance = new BigNumber(payload.balance || 0); - result.nonce = new Nonce(payload.nonce || 0); - result.creator = new Address(payload.creator || ""); result.royalties = new BigNumber(payload.royalties || 0).div(100); return result; } static fromProxyHttpResponseByNonce(payload: any): NonFungibleTokenOfAccountOnNetwork { - let result = new NonFungibleTokenOfAccountOnNetwork(); + let result = NonFungibleTokenOfAccountOnNetwork.fromHttpResponse(payload); - result.attributes = Buffer.from(payload.attributes || "", "base64"); - result.balance = new BigNumber(payload.balance || 0); - result.nonce = new Nonce(payload.nonce || 0); - result.creator = new Address(payload.creator || ""); - result.royalties = new BigNumber(payload.royalties || 0).div(100); result.identifier = `${payload.tokenIdentifier}-${result.nonce.hex()}`; result.collection = payload.tokenIdentifier || ""; + result.royalties = new BigNumber(payload.royalties || 0).div(100); return result; } - private static parseCollectionFromIdentifier(identifier: string): string { - let parts = identifier.split("-"); - let collection = parts.slice(0, 2).join("-"); - return collection; - } - static fromApiHttpResponse(payload: any): NonFungibleTokenOfAccountOnNetwork { - let result = new NonFungibleTokenOfAccountOnNetwork(); + let result = NonFungibleTokenOfAccountOnNetwork.fromHttpResponse(payload); result.identifier = payload.identifier || ""; result.collection = payload.collection || ""; + + return result; + } + + private static fromHttpResponse(payload: any): NonFungibleTokenOfAccountOnNetwork { + let result = new NonFungibleTokenOfAccountOnNetwork(); + result.attributes = Buffer.from(payload.attributes || "", "base64"); - // On API, missing balance means NFT. result.balance = new BigNumber(payload.balance || 1); result.nonce = new Nonce(payload.nonce || 0); result.creator = new Address(payload.creator || ""); result.royalties = new BigNumber(payload.royalties || 0); - + return result; } + + private static parseCollectionFromIdentifier(identifier: string): string { + let parts = identifier.split("-"); + let collection = parts.slice(0, 2).join("-"); + return collection; + } } From 7930610651f00ff71429fbc3cf8388f8af6e775b Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Wed, 23 Feb 2022 16:12:14 +0200 Subject: [PATCH 12/15] SCRs, tests. --- src/networkProvider/contractResults.ts | 48 ++++++++++++ src/networkProvider/providers.dev.net.spec.ts | 38 +++++++--- src/networkProvider/transactions.ts | 74 +++++++++---------- 3 files changed, 112 insertions(+), 48 deletions(-) diff --git a/src/networkProvider/contractResults.ts b/src/networkProvider/contractResults.ts index 0915d525..ef719ef5 100644 --- a/src/networkProvider/contractResults.ts +++ b/src/networkProvider/contractResults.ts @@ -6,17 +6,32 @@ import { IContractQueryResponse, IContractResultItem, IContractResults } from ". import { GasLimit, GasPrice } from "../networkParams"; import { Nonce } from "../nonce"; import { ArgSerializer, EndpointDefinition, MaxUint64, ReturnCode, TypedValue } from "../smartcontracts"; +import { TransactionHash } from "../transaction"; export class ContractResults implements IContractResults { readonly items: IContractResultItem[]; constructor(items: IContractResultItem[]) { this.items = items; + + this.items.sort(function (a: IContractResultItem, b: IContractResultItem) { + return a.nonce.valueOf() - b.nonce.valueOf(); + }); } static empty(): ContractResults { return new ContractResults([]); } + + static fromProxyHttpResponse(results: any[]): ContractResults { + let items = results.map(item => ContractResultItem.fromProxyHttpResponse(item)); + return new ContractResults(items); + } + + static fromApiHttpResponse(results: any[]): ContractResults { + let items = results.map(item => ContractResultItem.fromApiHttpResponse(item)); + return new ContractResults(items); + } } export class ContractResultItem implements IContractResultItem { @@ -32,6 +47,39 @@ export class ContractResultItem implements IContractResultItem { gasPrice: GasPrice = new GasPrice(0); callType: number = 0; returnMessage: string = ""; + + static fromProxyHttpResponse(response: any): ContractResultItem { + let item = ContractResultItem.fromHttpResponse(response); + return item; + } + + static fromApiHttpResponse(response: any): ContractResultItem { + let item = ContractResultItem.fromHttpResponse(response); + + item.data = Buffer.from(item.data, "base64").toString(); + item.callType = Number(item.callType); + + return item; + } + + private static fromHttpResponse(response: any): ContractResultItem { + let item = new ContractResultItem(); + + item.hash = new TransactionHash(response.hash); + item.nonce = new Nonce(response.nonce || 0); + item.value = Balance.fromString(response.value); + item.receiver = new Address(response.receiver); + item.sender = new Address(response.sender); + item.previousHash = new TransactionHash(response.prevTxHash); + item.originalHash = new TransactionHash(response.originalTxHash); + item.gasLimit = new GasLimit(response.gasLimit); + item.gasPrice = new GasPrice(response.gasPrice); + item.data = response.data || ""; + item.callType = response.callType; + item.returnMessage = response.returnMessage; + + return item; + } } export class ContractQueryResponse implements IContractQueryResponse { diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts index 817bc6f7..280c66fe 100644 --- a/src/networkProvider/providers.dev.net.spec.ts +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -90,7 +90,7 @@ describe("test network providers on devnet: Proxy and API", function () { } }); - it("should have same response for getTransaction(), getTransactionStatus()", async function () { + it("should have same response for getTransaction()", async function () { this.timeout(20000); let hashes = [ @@ -100,7 +100,8 @@ describe("test network providers on devnet: Proxy and API", function () { new TransactionHash("b45dd5e598bc85ba71639f2cbce8c5dff2fbe93159e637852fddeb16c0e84a48"), new TransactionHash("83db780e98d4d3c917668c47b33ba51445591efacb0df2a922f88e7dfbb5fc7d"), new TransactionHash("c2eb62b28cc7320da2292d87944c5424a70e1f443323c138c1affada7f6e9705"), - new TransactionHash("98e913c2a78cafdf4fa7f0113c1285fb29c2409bd7a746bb6f5506ad76841d54"), + // TODO: Uncomment once the Gateway returns all SCRs in this case, as well. + // new TransactionHash("98e913c2a78cafdf4fa7f0113c1285fb29c2409bd7a746bb6f5506ad76841d54"), new TransactionHash("5b05945be8ba2635e7c13d792ad727533494358308b5fcf36a816e52b5b272b8"), new TransactionHash("47b089b5f0220299a017359003694a01fd75d075100166b8072c418d5143fe06"), new TransactionHash("85021f20b06662240d8302d62f68031bbf7261bacb53b84e3dc9346c0f10a8e7") @@ -113,18 +114,11 @@ describe("test network providers on devnet: Proxy and API", function () { ignoreKnownTransactionDifferencesBetweenProviders(apiResponse, proxyResponse); assert.deepEqual(apiResponse, proxyResponse, `transaction: ${hash}`); } - - for (const hash of hashes) { - let apiResponse = await apiProvider.getTransactionStatus(hash); - let proxyResponse = await proxyProvider.getTransactionStatus(hash); - - // TODO: Fix differences of "tx.status". - // assert.deepEqual(apiResponse, proxyResponse, `transaction: ${hash}`); - } }); // TODO: Strive to have as little differences as possible between Proxy and API. function ignoreKnownTransactionDifferencesBetweenProviders(apiResponse: ITransactionOnNetwork, proxyResponse: ITransactionOnNetwork) { + // TODO: Remove this once "tx.status" is uniformized. apiResponse.status = proxyResponse.status = new TransactionStatus("ignore"); // Ignore fields which are not present on API response: @@ -134,6 +128,30 @@ describe("test network providers on devnet: Proxy and API", function () { proxyResponse.hyperblockHash = new Hash(""); } + // TODO: Fix differences of "tx.status", then enable this test. + it.skip("should have same response for getTransactionStatus()", async function () { + this.timeout(20000); + + let hashes = [ + new TransactionHash("b41f5fc39e96b1f194d07761c6efd6cb92278b95f5012ab12cbc910058ca8b54"), + new TransactionHash("7757397a59378e9d0f6d5f08cc934c260e33a50ae0d73fdf869f7c02b6b47b33"), + new TransactionHash("b87238089e81527158a6daee520280324bc7e5322ba54d1b3c9a5678abe953ea"), + new TransactionHash("b45dd5e598bc85ba71639f2cbce8c5dff2fbe93159e637852fddeb16c0e84a48"), + new TransactionHash("83db780e98d4d3c917668c47b33ba51445591efacb0df2a922f88e7dfbb5fc7d"), + new TransactionHash("c2eb62b28cc7320da2292d87944c5424a70e1f443323c138c1affada7f6e9705"), + new TransactionHash("98e913c2a78cafdf4fa7f0113c1285fb29c2409bd7a746bb6f5506ad76841d54"), + new TransactionHash("5b05945be8ba2635e7c13d792ad727533494358308b5fcf36a816e52b5b272b8"), + new TransactionHash("47b089b5f0220299a017359003694a01fd75d075100166b8072c418d5143fe06"), + new TransactionHash("85021f20b06662240d8302d62f68031bbf7261bacb53b84e3dc9346c0f10a8e7") + ]; + + for (const hash of hashes) { + let apiResponse = await apiProvider.getTransactionStatus(hash); + let proxyResponse = await proxyProvider.getTransactionStatus(hash); + assert.deepEqual(apiResponse, proxyResponse, `transaction: ${hash}`); + } + }); + it("should have same response for getDefinitionOfFungibleToken()", async function () { this.timeout(10000); diff --git a/src/networkProvider/transactions.ts b/src/networkProvider/transactions.ts index fd46e4fb..91ae09bb 100644 --- a/src/networkProvider/transactions.ts +++ b/src/networkProvider/transactions.ts @@ -1,12 +1,14 @@ import { Address } from "../address"; import { Balance } from "../balance"; import { Hash } from "../hash"; -import { ITransactionOnNetwork } from "../interface.networkProvider"; +import { IContractResults, ITransactionOnNetwork } from "../interface.networkProvider"; import { GasLimit, GasPrice } from "../networkParams"; import { Nonce } from "../nonce"; import { Signature } from "../signature"; import { TransactionHash, TransactionStatus } from "../transaction"; +import { TransactionLogs } from "../transactionLogs"; import { TransactionPayload } from "../transactionPayload"; +import { ContractResults } from "./contractResults"; export class TransactionOnNetwork implements ITransactionOnNetwork { hash: TransactionHash = new TransactionHash(""); @@ -27,50 +29,46 @@ import { TransactionPayload } from "../transactionPayload"; hyperblockNonce: Nonce = new Nonce(0); hyperblockHash: Hash = Hash.empty(); - static fromProxyHttpResponse(txHash: TransactionHash, response: any): TransactionOnNetwork { - let transactionOnNetwork = new TransactionOnNetwork(); - - transactionOnNetwork.hash = txHash; - transactionOnNetwork.nonce = new Nonce(response.nonce || 0); - transactionOnNetwork.round = response.round; - transactionOnNetwork.epoch = response.epoch || 0; - transactionOnNetwork.value = Balance.fromString(response.value); - transactionOnNetwork.sender = Address.fromBech32(response.sender); - transactionOnNetwork.receiver = Address.fromBech32(response.receiver); - transactionOnNetwork.gasPrice = new GasPrice(response.gasPrice); - transactionOnNetwork.gasLimit = new GasLimit(response.gasLimit); - transactionOnNetwork.data = TransactionPayload.fromEncoded(response.data); - transactionOnNetwork.status = new TransactionStatus(response.status); - transactionOnNetwork.timestamp = response.timestamp || 0; + logs: TransactionLogs = TransactionLogs.empty(); + contractResults: IContractResults = ContractResults.empty(); - transactionOnNetwork.blockNonce = new Nonce(response.blockNonce || 0); - transactionOnNetwork.hyperblockNonce = new Nonce(response.hyperblockNonce || 0); - transactionOnNetwork.hyperblockHash = new Hash(response.hyperblockHash); - - return transactionOnNetwork; + static fromProxyHttpResponse(txHash: TransactionHash, response: any): TransactionOnNetwork { + let result = TransactionOnNetwork.fromHttpResponse(txHash, response); + result.contractResults = ContractResults.fromProxyHttpResponse(response.smartContractResults || []); + // TODO: uniformize transaction status + return result; } static fromApiHttpResponse(txHash: TransactionHash, response: any): TransactionOnNetwork { - let transactionOnNetwork = new TransactionOnNetwork(); + let result = TransactionOnNetwork.fromHttpResponse(txHash, response); + result.contractResults = ContractResults.fromApiHttpResponse(response.results || []); + // TODO: uniformize transaction status + return result; + } + + private static fromHttpResponse(txHash: TransactionHash, response: any): TransactionOnNetwork { + let result = new TransactionOnNetwork(); + + result.hash = txHash; + result.nonce = new Nonce(response.nonce || 0); + result.round = response.round; + result.epoch = response.epoch || 0; + result.value = Balance.fromString(response.value); + result.sender = Address.fromBech32(response.sender); + result.receiver = Address.fromBech32(response.receiver); + result.gasPrice = new GasPrice(response.gasPrice); + result.gasLimit = new GasLimit(response.gasLimit); + result.data = TransactionPayload.fromEncoded(response.data); + result.status = new TransactionStatus(response.status); + result.timestamp = response.timestamp || 0; - transactionOnNetwork.hash = txHash; - transactionOnNetwork.nonce = new Nonce(response.nonce || 0); - transactionOnNetwork.round = response.round; - transactionOnNetwork.epoch = response.epoch || 0; - transactionOnNetwork.value = Balance.fromString(response.value); - transactionOnNetwork.sender = Address.fromBech32(response.sender); - transactionOnNetwork.receiver = Address.fromBech32(response.receiver); - transactionOnNetwork.gasPrice = new GasPrice(response.gasPrice); - transactionOnNetwork.gasLimit = new GasLimit(response.gasLimit); - transactionOnNetwork.data = TransactionPayload.fromEncoded(response.data); - transactionOnNetwork.status = new TransactionStatus(response.status); - transactionOnNetwork.timestamp = response.timestamp || 0; + result.blockNonce = new Nonce(response.blockNonce || 0); + result.hyperblockNonce = new Nonce(response.hyperblockNonce || 0); + result.hyperblockHash = new Hash(response.hyperblockHash); - transactionOnNetwork.blockNonce = new Nonce(response.blockNonce || 0); - transactionOnNetwork.hyperblockNonce = new Nonce(response.hyperblockNonce || 0); - transactionOnNetwork.hyperblockHash = new Hash(response.hyperblockHash); + result.logs = TransactionLogs.fromHttpResponse(response.logs || {}); - return transactionOnNetwork; + return result; } getDateTime(): Date { From 0fa058bb2f016082e4e5dd7c8f894878bff0c89e Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Wed, 23 Feb 2022 16:45:05 +0200 Subject: [PATCH 13/15] Fix after self-review. --- src/interface.networkProvider.ts | 6 ++++-- src/networkProvider/apiNetworkProvider.ts | 7 ------- src/networkProvider/index.ts | 4 ++-- src/networkProvider/providers.dev.net.spec.ts | 15 +++++++++++++++ src/networkProvider/proxyNetworkProvider.ts | 5 ----- src/networkProvider/tokenDefinitions.ts | 2 +- 6 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index 790bc11e..40f57c79 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -79,9 +79,8 @@ export interface INetworkProvider { /** * Simulates the processing of an already-signed {@link Transaction}. * - * TODO: Return explicit type. */ - simulateTransaction(tx: Transaction): Promise; + simulateTransaction(tx: Transaction): Promise; /** * Queries a Smart Contract - runs a pure function defined by the contract and returns its results. @@ -213,6 +212,9 @@ export interface IContractQueryResponse { getOutputTyped(endpointDefinition: EndpointDefinition): TypedValue[]; } +export interface IContractSimulation { +} + export class Pagination { from: number = 0; size: number = 100; diff --git a/src/networkProvider/apiNetworkProvider.ts b/src/networkProvider/apiNetworkProvider.ts index 1b87925c..d297e213 100644 --- a/src/networkProvider/apiNetworkProvider.ts +++ b/src/networkProvider/apiNetworkProvider.ts @@ -23,11 +23,6 @@ export class ApiNetworkProvider implements INetworkProvider { private config: AxiosRequestConfig; private backingProxyNetworkProvider; - /** - * Creates a new ApiProvider. - * @param url the URL of the Elrond Api - * @param config axios request config options - */ constructor(url: string, config?: AxiosRequestConfig) { this.url = url; this.config = { ...defaultConfig, ...config }; @@ -110,7 +105,6 @@ export class ApiNetworkProvider implements INetworkProvider { async sendTransaction(tx: Transaction): Promise { let response = await this.doPostGeneric("transactions", tx.toSendable()); - // Also see: https://github.com/ElrondNetwork/api.elrond.com/blob/main/src/endpoints/transactions/entities/transaction.send.result.ts let hash = new TransactionHash(response.txHash); return hash; } @@ -162,7 +156,6 @@ export class ApiNetworkProvider implements INetworkProvider { try { let url = `${this.url}/${resourceUrl}`; let response = await axios.get(url, this.config); - return response.data; } catch (error) { this.handleApiError(error, resourceUrl); diff --git a/src/networkProvider/index.ts b/src/networkProvider/index.ts index a4276ae7..0c4e6aa0 100644 --- a/src/networkProvider/index.ts +++ b/src/networkProvider/index.ts @@ -1,5 +1,5 @@ export * from "./apiNetworkProvider"; export * from "./proxyNetworkProvider"; -// do not export "./tokens" (encapsulation) -// do not export "./transactions" (encapsulation) +// we do not export "./tokens" +// we do not export "./transactions" diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts index 280c66fe..8fb4c267 100644 --- a/src/networkProvider/providers.dev.net.spec.ts +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -27,12 +27,14 @@ describe("test network providers on devnet: Proxy and API", function () { it("should have same response for getNetworkConfig()", async function () { let apiResponse = await apiProvider.getNetworkConfig(); let proxyResponse = await proxyProvider.getNetworkConfig(); + assert.deepEqual(apiResponse, proxyResponse); }); it("should have same response for getNetworkStatus()", async function () { let apiResponse = await apiProvider.getNetworkStatus(); let proxyResponse = await proxyProvider.getNetworkStatus(); + assert.deepEqual(apiResponse, proxyResponse); }); @@ -40,6 +42,7 @@ describe("test network providers on devnet: Proxy and API", function () { it.skip("should have same response for getNetworkStakeStatistics()", async function () { let apiResponse = await apiProvider.getNetworkStakeStatistics(); let proxyResponse = await proxyProvider.getNetworkStakeStatistics(); + assert.deepEqual(apiResponse, proxyResponse); }); @@ -47,6 +50,7 @@ describe("test network providers on devnet: Proxy and API", function () { it.skip("should have same response for getNetworkGeneralStatistics()", async function () { let apiResponse = await apiProvider.getNetworkGeneralStatistics(); let proxyResponse = await proxyProvider.getNetworkGeneralStatistics(); + assert.deepEqual(apiResponse, proxyResponse); }); @@ -54,6 +58,7 @@ describe("test network providers on devnet: Proxy and API", function () { for (const user of [bob, carol, dan]) { let apiResponse = await apiProvider.getAccount(user.address); let proxyResponse = await proxyProvider.getAccount(user.address); + assert.deepEqual(apiResponse, proxyResponse); } }); @@ -64,11 +69,13 @@ describe("test network providers on devnet: Proxy and API", function () { for (const user of [carol, dan]) { let apiResponse = await apiProvider.getFungibleTokensOfAccount(user.address); let proxyResponse = await proxyProvider.getFungibleTokensOfAccount(user.address); + assert.deepEqual(apiResponse.slice(0, 100), proxyResponse.slice(0, 100)); for (const item of apiResponse.slice(0, 5)) { let apiResponse = await apiProvider.getFungibleTokenOfAccount(user.address, item.identifier); let proxyResponse = await proxyProvider.getFungibleTokenOfAccount(user.address, item.identifier); + assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.identifier}`); } } @@ -80,11 +87,13 @@ describe("test network providers on devnet: Proxy and API", function () { for (const user of [alice, bob, carol, dan]) { let apiResponse = await apiProvider.getNonFungibleTokensOfAccount(user.address); let proxyResponse = await proxyProvider.getNonFungibleTokensOfAccount(user.address); + assert.deepEqual(apiResponse.slice(0, 100), proxyResponse.slice(0, 100)); for (const item of apiResponse.slice(0, 5)) { let apiResponse = await apiProvider.getNonFungibleTokenOfAccount(user.address, item.collection, item.nonce); let proxyResponse = await proxyProvider.getNonFungibleTokenOfAccount(user.address, item.collection, item.nonce); + assert.deepEqual(apiResponse, proxyResponse, `user: ${user.address.bech32()}, token: ${item.identifier}`); } } @@ -148,6 +157,7 @@ describe("test network providers on devnet: Proxy and API", function () { for (const hash of hashes) { let apiResponse = await apiProvider.getTransactionStatus(hash); let proxyResponse = await proxyProvider.getTransactionStatus(hash); + assert.deepEqual(apiResponse, proxyResponse, `transaction: ${hash}`); } }); @@ -175,6 +185,7 @@ describe("test network providers on devnet: Proxy and API", function () { for (const collection of collections) { let apiResponse = await apiProvider.getDefinitionOfTokenCollection(collection); + assert.equal(apiResponse.collection, collection); // TODO: Uncomment after implementing the function in the proxy provider. @@ -190,6 +201,7 @@ describe("test network providers on devnet: Proxy and API", function () { for (const token of tokens) { let apiResponse = await apiProvider.getNonFungibleToken(token.id, token.nonce); + assert.equal(apiResponse.collection, token.id); // TODO: Uncomment after implementing the function in the proxy provider. @@ -212,6 +224,7 @@ describe("test network providers on devnet: Proxy and API", function () { let apiResponse = await apiProvider.queryContract(query); let proxyResponse = await proxyProvider.queryContract(query); + assert.deepEqual(apiResponse, proxyResponse); assert.deepEqual(apiResponse.getOutputUntyped(), proxyResponse.getOutputUntyped()); @@ -224,6 +237,7 @@ describe("test network providers on devnet: Proxy and API", function () { apiResponse = await apiProvider.queryContract(query); proxyResponse = await proxyProvider.queryContract(query); + assert.deepEqual(apiResponse, proxyResponse); assert.deepEqual(apiResponse.getOutputUntyped(), proxyResponse.getOutputUntyped()); @@ -243,6 +257,7 @@ describe("test network providers on devnet: Proxy and API", function () { apiResponse = await apiProvider.queryContract(query); proxyResponse = await proxyProvider.queryContract(query); + assert.deepEqual(apiResponse, proxyResponse); assert.deepEqual(apiResponse.getOutputUntyped(), proxyResponse.getOutputUntyped()); }); diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index c9f1d348..45d6f644 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -20,11 +20,6 @@ export class ProxyNetworkProvider implements INetworkProvider { private url: string; private config: AxiosRequestConfig; - /** - * Creates a new {@link INetworkProvider} backed by an Elrond Proxy. - * @param url the URL of the Elrond Proxy - * @param config axios request config options - */ constructor(url: string, config?: AxiosRequestConfig) { this.url = url; this.config = { ...defaultConfig, ...config }; diff --git a/src/networkProvider/tokenDefinitions.ts b/src/networkProvider/tokenDefinitions.ts index 407e05d1..3e058109 100644 --- a/src/networkProvider/tokenDefinitions.ts +++ b/src/networkProvider/tokenDefinitions.ts @@ -1,6 +1,6 @@ import { BigNumber } from "bignumber.js"; import { Address } from "../address"; -import { IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INonFungibleTokenOfAccountOnNetwork } from "../interface.networkProvider"; +import { IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork } from "../interface.networkProvider"; export class DefinitionOfFungibleTokenOnNetwork implements IDefinitionOfFungibleTokenOnNetwork { identifier: string = ""; From 37c56b83f5287999d80c711ecde98883963acf53 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Wed, 23 Feb 2022 16:52:31 +0200 Subject: [PATCH 14/15] Fix after self-review. --- src/interface.networkProvider.ts | 66 ++++++++++++++------------ src/networkProvider/contractResults.ts | 10 ++++ 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/src/interface.networkProvider.ts b/src/interface.networkProvider.ts index 40f57c79..04206cae 100644 --- a/src/interface.networkProvider.ts +++ b/src/interface.networkProvider.ts @@ -130,6 +130,39 @@ export interface INonFungibleTokenOfAccountOnNetwork { royalties: BigNumber; } + +export interface IDefinitionOfFungibleTokenOnNetwork { + identifier: string; + name: string; + ticker: string; + owner: Address; + decimals: number; + supply: BigNumber; + isPaused: boolean; + canUpgrade: boolean; + canMint: boolean; + canBurn: boolean; + canChangeOwner: boolean; + canPause: boolean; + canFreeze: boolean; + canWipe: boolean; + canAddSpecialRoles: boolean; +} + +export interface IDefinitionOfTokenCollectionOnNetwork { + collection: string; + type: string; + name: string; + ticker: string; + owner: Address; + decimals: number; + canPause: boolean; + canFreeze: boolean; + canWipe: boolean; + canTransferRole: boolean; + // TODO: add "assets", "roles" +} + export interface ITransactionOnNetwork { hash: TransactionHash; nonce: Nonce; @@ -168,38 +201,9 @@ export interface IContractResultItem { gasPrice: GasPrice; callType: number; returnMessage: string; -} -export interface IDefinitionOfFungibleTokenOnNetwork { - identifier: string; - name: string; - ticker: string; - owner: Address; - decimals: number; - supply: BigNumber; - isPaused: boolean; - canUpgrade: boolean; - canMint: boolean; - canBurn: boolean; - canChangeOwner: boolean; - canPause: boolean; - canFreeze: boolean; - canWipe: boolean; - canAddSpecialRoles: boolean; -} - -export interface IDefinitionOfTokenCollectionOnNetwork { - collection: string; - type: string; - name: string; - ticker: string; - owner: Address; - decimals: number; - canPause: boolean; - canFreeze: boolean; - canWipe: boolean; - canTransferRole: boolean; - // TODO: add "assets", "roles" + getOutputUntyped(): Buffer[]; + getOutputTyped(endpointDefinition: EndpointDefinition): TypedValue[]; } export interface IContractQueryResponse { diff --git a/src/networkProvider/contractResults.ts b/src/networkProvider/contractResults.ts index ef719ef5..69e5f574 100644 --- a/src/networkProvider/contractResults.ts +++ b/src/networkProvider/contractResults.ts @@ -80,6 +80,16 @@ export class ContractResultItem implements IContractResultItem { return item; } + + getOutputUntyped(): Buffer[] { + // TODO: Decide how to parse "data" (immediate results vs. other results). + throw new Error("Method not implemented."); + } + + getOutputTyped(_endpointDefinition: EndpointDefinition): TypedValue[] { + // TODO: Decide how to parse "data" (immediate results vs. other results). + throw new Error("Method not implemented."); + } } export class ContractQueryResponse implements IContractQueryResponse { From 02448861ddfd62d1945eb449c4dfcb31720f12ec Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Thu, 24 Feb 2022 12:22:08 +0200 Subject: [PATCH 15/15] Fix after review. --- src/account.ts | 2 +- src/networkProvider/apiNetworkProvider.ts | 3 ++- src/networkProvider/contractResults.ts | 2 +- .../interface.ts} | 24 +++++++++---------- src/networkProvider/providers.dev.net.spec.ts | 2 +- src/networkProvider/proxyNetworkProvider.ts | 3 ++- src/networkProvider/tokenDefinitions.ts | 2 +- src/networkProvider/tokens.ts | 2 +- src/networkProvider/transactions.ts | 2 +- 9 files changed, 22 insertions(+), 20 deletions(-) rename src/{interface.networkProvider.ts => networkProvider/interface.ts} (93%) diff --git a/src/account.ts b/src/account.ts index 0d622921..b7ed953f 100644 --- a/src/account.ts +++ b/src/account.ts @@ -114,7 +114,7 @@ export class AccountOnNetwork { result.address = new Address(payload["address"] || 0); result.nonce = new Nonce(payload["nonce"] || 0); - result.balance = Balance.fromString(payload["balance"]); + result.balance = Balance.fromString(payload["balance"] || "0"); result.code = payload["code"] || ""; result.userName = payload["username"] || ""; diff --git a/src/networkProvider/apiNetworkProvider.ts b/src/networkProvider/apiNetworkProvider.ts index d297e213..3d809404 100644 --- a/src/networkProvider/apiNetworkProvider.ts +++ b/src/networkProvider/apiNetworkProvider.ts @@ -3,7 +3,7 @@ import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrNetworkProvider } from "../errors"; -import { IContractQueryResponse, IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; +import { IContractQueryResponse, IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "./interface"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; @@ -18,6 +18,7 @@ import { DefinitionOfFungibleTokenOnNetwork, DefinitionOfTokenCollectionOnNetwor import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; import { TransactionOnNetwork } from "./transactions"; +// TODO: Find & remove duplicate code between "ProxyNetworkProvider" and "ApiNetworkProvider". export class ApiNetworkProvider implements INetworkProvider { private url: string; private config: AxiosRequestConfig; diff --git a/src/networkProvider/contractResults.ts b/src/networkProvider/contractResults.ts index 69e5f574..c4b0126a 100644 --- a/src/networkProvider/contractResults.ts +++ b/src/networkProvider/contractResults.ts @@ -2,7 +2,7 @@ import { BigNumber } from "bignumber.js"; import { Address } from "../address"; import { Balance } from "../balance"; import { Hash } from "../hash"; -import { IContractQueryResponse, IContractResultItem, IContractResults } from "../interface.networkProvider"; +import { IContractQueryResponse, IContractResultItem, IContractResults } from "./interface"; import { GasLimit, GasPrice } from "../networkParams"; import { Nonce } from "../nonce"; import { ArgSerializer, EndpointDefinition, MaxUint64, ReturnCode, TypedValue } from "../smartcontracts"; diff --git a/src/interface.networkProvider.ts b/src/networkProvider/interface.ts similarity index 93% rename from src/interface.networkProvider.ts rename to src/networkProvider/interface.ts index 04206cae..a5f9689c 100644 --- a/src/interface.networkProvider.ts +++ b/src/networkProvider/interface.ts @@ -1,16 +1,16 @@ import { BigNumber } from "bignumber.js"; -import { Balance, GasLimit, GasPrice, Nonce, TransactionPayload } from "."; -import { AccountOnNetwork } from "./account"; -import { Address } from "./address"; -import { Hash } from "./hash"; -import { NetworkConfig } from "./networkConfig"; -import { NetworkStake } from "./networkStake"; -import { NetworkStatus } from "./networkStatus"; -import { Signature } from "./signature"; -import { EndpointDefinition, Query, ReturnCode, TypedValue } from "./smartcontracts"; -import { Stats } from "./stats"; -import { Transaction, TransactionHash, TransactionStatus } from "./transaction"; -import { TransactionLogs } from "./transactionLogs"; +import { Balance, GasLimit, GasPrice, Nonce, TransactionPayload } from ".."; +import { AccountOnNetwork } from "../account"; +import { Address } from "../address"; +import { Hash } from "../hash"; +import { NetworkConfig } from "../networkConfig"; +import { NetworkStake } from "../networkStake"; +import { NetworkStatus } from "../networkStatus"; +import { Signature } from "../signature"; +import { EndpointDefinition, Query, ReturnCode, TypedValue } from "../smartcontracts"; +import { Stats } from "../stats"; +import { Transaction, TransactionHash, TransactionStatus } from "../transaction"; +import { TransactionLogs } from "../transactionLogs"; /** * An interface that defines the endpoints of an HTTP API Provider. diff --git a/src/networkProvider/providers.dev.net.spec.ts b/src/networkProvider/providers.dev.net.spec.ts index 8fb4c267..7c953b5b 100644 --- a/src/networkProvider/providers.dev.net.spec.ts +++ b/src/networkProvider/providers.dev.net.spec.ts @@ -1,7 +1,7 @@ import { assert } from "chai"; import { ApiNetworkProvider, ProxyNetworkProvider } from "."; import { Hash } from "../hash"; -import { INetworkProvider, ITransactionOnNetwork } from "../interface.networkProvider"; +import { INetworkProvider, ITransactionOnNetwork } from "./interface"; import { Address } from "../address"; import { loadTestWallets, TestWallet } from "../testutils"; import { TransactionHash, TransactionStatus } from "../transaction"; diff --git a/src/networkProvider/proxyNetworkProvider.ts b/src/networkProvider/proxyNetworkProvider.ts index 45d6f644..57f5b238 100644 --- a/src/networkProvider/proxyNetworkProvider.ts +++ b/src/networkProvider/proxyNetworkProvider.ts @@ -3,7 +3,7 @@ import { AccountOnNetwork } from "../account"; import { Address } from "../address"; import { defaultConfig } from "../constants"; import { ErrNetworkProvider } from "../errors"; -import { IContractQueryResponse, IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "../interface.networkProvider"; +import { IContractQueryResponse, IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork, IFungibleTokenOfAccountOnNetwork, INetworkProvider, INonFungibleTokenOfAccountOnNetwork, ITransactionOnNetwork, Pagination } from "./interface"; import { Logger } from "../logger"; import { NetworkConfig } from "../networkConfig"; import { NetworkStake } from "../networkStake"; @@ -16,6 +16,7 @@ import { ContractQueryResponse } from "./contractResults"; import { FungibleTokenOfAccountOnNetwork, NonFungibleTokenOfAccountOnNetwork } from "./tokens"; import { TransactionOnNetwork } from "./transactions"; +// TODO: Find & remove duplicate code between "ProxyNetworkProvider" and "ApiNetworkProvider". export class ProxyNetworkProvider implements INetworkProvider { private url: string; private config: AxiosRequestConfig; diff --git a/src/networkProvider/tokenDefinitions.ts b/src/networkProvider/tokenDefinitions.ts index 3e058109..77b784e6 100644 --- a/src/networkProvider/tokenDefinitions.ts +++ b/src/networkProvider/tokenDefinitions.ts @@ -1,6 +1,6 @@ import { BigNumber } from "bignumber.js"; import { Address } from "../address"; -import { IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork } from "../interface.networkProvider"; +import { IDefinitionOfFungibleTokenOnNetwork, IDefinitionOfTokenCollectionOnNetwork } from "./interface"; export class DefinitionOfFungibleTokenOnNetwork implements IDefinitionOfFungibleTokenOnNetwork { identifier: string = ""; diff --git a/src/networkProvider/tokens.ts b/src/networkProvider/tokens.ts index 716de777..303fdb7c 100644 --- a/src/networkProvider/tokens.ts +++ b/src/networkProvider/tokens.ts @@ -1,6 +1,6 @@ import { BigNumber } from "bignumber.js"; import { Address } from "../address"; -import { IFungibleTokenOfAccountOnNetwork, INonFungibleTokenOfAccountOnNetwork } from "../interface.networkProvider"; +import { IFungibleTokenOfAccountOnNetwork, INonFungibleTokenOfAccountOnNetwork } from "./interface"; import { Nonce } from "../nonce"; export class FungibleTokenOfAccountOnNetwork implements IFungibleTokenOfAccountOnNetwork { diff --git a/src/networkProvider/transactions.ts b/src/networkProvider/transactions.ts index 91ae09bb..6fd3c906 100644 --- a/src/networkProvider/transactions.ts +++ b/src/networkProvider/transactions.ts @@ -1,7 +1,7 @@ import { Address } from "../address"; import { Balance } from "../balance"; import { Hash } from "../hash"; -import { IContractResults, ITransactionOnNetwork } from "../interface.networkProvider"; +import { IContractResults, ITransactionOnNetwork } from "./interface"; import { GasLimit, GasPrice } from "../networkParams"; import { Nonce } from "../nonce"; import { Signature } from "../signature";