-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #133 from ElrondNetwork/network-provider
Merge IProvider and IApiProvider into INetworkProvider - part 1
- Loading branch information
Showing
16 changed files
with
1,273 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
import axios, { AxiosRequestConfig } from "axios"; | ||
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"; | ||
import { Logger } from "../logger"; | ||
import { NetworkConfig } from "../networkConfig"; | ||
import { NetworkStake } from "../networkStake"; | ||
import { NetworkStatus } from "../networkStatus"; | ||
import { Nonce } from "../nonce"; | ||
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"; | ||
import { TransactionOnNetwork } from "./transactions"; | ||
|
||
// TODO: Find & remove duplicate code between "ProxyNetworkProvider" and "ApiNetworkProvider". | ||
export class ApiNetworkProvider implements INetworkProvider { | ||
private url: string; | ||
private config: AxiosRequestConfig; | ||
private backingProxyNetworkProvider; | ||
|
||
constructor(url: string, config?: AxiosRequestConfig) { | ||
this.url = url; | ||
this.config = { ...defaultConfig, ...config }; | ||
this.backingProxyNetworkProvider = new ProxyNetworkProvider(url, config); | ||
} | ||
|
||
async getNetworkConfig(): Promise<NetworkConfig> { | ||
return await this.backingProxyNetworkProvider.getNetworkConfig(); | ||
} | ||
|
||
async getNetworkStatus(): Promise<NetworkStatus> { | ||
return await this.backingProxyNetworkProvider.getNetworkStatus(); | ||
} | ||
|
||
async getNetworkStakeStatistics(): Promise<NetworkStake> { | ||
let response = await this.doGetGeneric("stake"); | ||
let networkStake = NetworkStake.fromHttpResponse(response) | ||
return networkStake; | ||
} | ||
|
||
async getNetworkGeneralStatistics(): Promise<Stats> { | ||
let response = await this.doGetGeneric("stats"); | ||
let stats = Stats.fromHttpResponse(response) | ||
return stats; | ||
} | ||
|
||
async getAccount(address: Address): Promise<AccountOnNetwork> { | ||
let response = await this.doGetGeneric(`accounts/${address.bech32()}`); | ||
let account = AccountOnNetwork.fromHttpResponse(response); | ||
return account; | ||
} | ||
|
||
async getFungibleTokensOfAccount(address: Address, pagination?: Pagination): Promise<IFungibleTokenOfAccountOnNetwork[]> { | ||
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.identifier.localeCompare(b.identifier)); | ||
return tokens; | ||
} | ||
|
||
async getNonFungibleTokensOfAccount(address: Address, pagination?: Pagination): Promise<INonFungibleTokenOfAccountOnNetwork[]> { | ||
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.identifier.localeCompare(b.identifier)); | ||
return tokens; | ||
} | ||
|
||
async getFungibleTokenOfAccount(address: Address, tokenIdentifier: string): Promise<IFungibleTokenOfAccountOnNetwork> { | ||
let response = await this.doGetGeneric(`accounts/${address.bech32()}/tokens/${tokenIdentifier}`); | ||
let tokenData = FungibleTokenOfAccountOnNetwork.fromHttpResponse(response); | ||
return tokenData; | ||
} | ||
|
||
async getNonFungibleTokenOfAccount(address: Address, collection: string, nonce: Nonce): Promise<INonFungibleTokenOfAccountOnNetwork> { | ||
let response = await this.doGetGeneric(`accounts/${address.bech32()}/nfts/${collection}-${nonce.hex()}`); | ||
let tokenData = NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(response); | ||
return tokenData; | ||
} | ||
|
||
async getTransaction(txHash: TransactionHash): Promise<ITransactionOnNetwork> { | ||
let response = await this.doGetGeneric(`transactions/${txHash.toString()}`); | ||
let transaction = TransactionOnNetwork.fromApiHttpResponse(txHash, response); | ||
return transaction; | ||
} | ||
|
||
async getTransactionStatus(txHash: TransactionHash): Promise<TransactionStatus> { | ||
let response = await this.doGetGeneric(`transactions/${txHash.toString()}?fields=status`); | ||
let status = new TransactionStatus(response.status); | ||
return status; | ||
} | ||
|
||
async sendTransaction(tx: Transaction): Promise<TransactionHash> { | ||
let response = await this.doPostGeneric("transactions", tx.toSendable()); | ||
let hash = new TransactionHash(response.txHash); | ||
return hash; | ||
} | ||
|
||
async simulateTransaction(tx: Transaction): Promise<any> { | ||
return await this.backingProxyNetworkProvider.simulateTransaction(tx); | ||
} | ||
|
||
async queryContract(query: Query): Promise<IContractQueryResponse> { | ||
let data = query.toHttpRequest(); | ||
let response = await this.doPostGeneric("query", data); | ||
let queryResponse = ContractQueryResponse.fromHttpResponse(response); | ||
return queryResponse; | ||
} | ||
|
||
async getDefinitionOfFungibleToken(tokenIdentifier: string): Promise<IDefinitionOfFungibleTokenOnNetwork> { | ||
let response = await this.doGetGeneric(`tokens/${tokenIdentifier}`); | ||
let definition = DefinitionOfFungibleTokenOnNetwork.fromApiHttpResponse(response); | ||
return definition; | ||
} | ||
|
||
async getDefinitionOfTokenCollection(collection: string): Promise<IDefinitionOfTokenCollectionOnNetwork> { | ||
let response = await this.doGetGeneric(`collections/${collection}`); | ||
let definition = DefinitionOfTokenCollectionOnNetwork.fromApiHttpResponse(response); | ||
return definition; | ||
} | ||
|
||
async getNonFungibleToken(collection: string, nonce: Nonce): Promise<INonFungibleTokenOfAccountOnNetwork> { | ||
let response = await this.doGetGeneric(`nfts/${collection}-${nonce.hex()}`); | ||
let token = NonFungibleTokenOfAccountOnNetwork.fromApiHttpResponse(response); | ||
return token; | ||
} | ||
|
||
async doGetGeneric(resourceUrl: string): Promise<any> { | ||
let response = await this.doGet(resourceUrl); | ||
return response; | ||
} | ||
|
||
async doPostGeneric(resourceUrl: string, payload: any): Promise<any> { | ||
let response = await this.doPost(resourceUrl, payload); | ||
return response; | ||
} | ||
|
||
private buildPaginationParams(pagination: Pagination) { | ||
return `from=${pagination.from}&size=${pagination.size}`; | ||
} | ||
|
||
private async doGet(resourceUrl: string): Promise<any> { | ||
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<any> { | ||
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 ErrNetworkProvider(resourceUrl, error.toString(), error); | ||
} | ||
|
||
let errorData = error.response.data; | ||
let originalErrorMessage = errorData.error || errorData.message || JSON.stringify(errorData); | ||
throw new ErrNetworkProvider(resourceUrl, originalErrorMessage, error); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { BigNumber } from "bignumber.js"; | ||
import { Address } from "../address"; | ||
import { Balance } from "../balance"; | ||
import { Hash } from "../hash"; | ||
import { IContractQueryResponse, IContractResultItem, IContractResults } from "./interface"; | ||
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 { | ||
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 = ""; | ||
|
||
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; | ||
} | ||
|
||
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 { | ||
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export * from "./apiNetworkProvider"; | ||
export * from "./proxyNetworkProvider"; | ||
|
||
// we do not export "./tokens" | ||
// we do not export "./transactions" |
Oops, something went wrong.