UNPKG

@radixdlt/application

Version:

A JavaScript client library for interacting with the Radix Distributed Ledger.

307 lines (282 loc) 8.35 kB
import { JSONDecoding } from '@radixdlt/data-formats' import { err, ok, Result } from 'neverthrow' import { BuildTransactionEndpoint, SubmitTransactionEndpoint, LookupTransactionEndpoint, NetworkIdEndpoint, NetworkTransactionDemandEndpoint, NetworkTransactionThroughputEndpoint, StakePositionsEndpoint, FinalizeTransactionEndpoint, TokenBalancesEndpoint, TokenInfoEndpoint, TransactionHistoryEndpoint, TransactionStatusEndpoint, UnstakePositionsEndpoint, ValidatorsEndpoint, LookupValidatorEndpoint, } from './_types' import { pipe } from 'ramda' import { Message } from '@radixdlt/crypto' import { addressDecoder, amountDecoder, dateDecoder, networkDecoder, RRIDecoder, transactionIdentifierDecoder, URLDecoder, validatorAddressDecoder, } from '../decoders' const executedTXDecoders = JSONDecoding.withDecoders( amountDecoder('amount', 'fee'), dateDecoder('sentAt'), addressDecoder('from', 'to'), validatorAddressDecoder('validator'), transactionIdentifierDecoder('txID'), RRIDecoder('rri'), ) export type RPCRequestFailureResponse = Readonly<{ failure: string }> const isRPCRequestFailureResponse = ( something: unknown, ): something is RPCRequestFailureResponse => { const inspection = something as RPCRequestFailureResponse return inspection.failure !== undefined } const hasRequiredProps = <T extends Record<string, unknown>>( methodName: string, obj: T, props: string[], ): Result<T, Error[]> => { for (const prop of props) { if (obj[prop] === undefined) { return err([ Error( `Prop validation failed for ${methodName} response. ${prop} was undefined.`, ), ]) } } return ok(obj) } export const handleTransactionHistoryResponse = ( json: TransactionHistoryEndpoint.Response, ) => executedTXDecoders .create< TransactionHistoryEndpoint.Response, TransactionHistoryEndpoint.DecodedResponse >()(json) .andThen(decoded => ok({ ...decoded, transactions: decoded.transactions.map(tx => ({ ...tx, message: (() => { if (!tx.message) return undefined // Check format if (!/^(00|01)[0-9a-fA-F]+$/.test(tx.message)) return '<Failed to interpret message>' return Message.isPlaintext(tx.message) ? Message.plaintextToString( Buffer.from(tx.message, 'hex'), ) : tx.message })(), })), }), ) export const handleLookupTXResponse = ( json: LookupTransactionEndpoint.Response, ) => executedTXDecoders .create< LookupTransactionEndpoint.Response, LookupTransactionEndpoint.DecodedResponse >()(json) .andThen(decoded => ok({ ...decoded, message: (() => { if (!decoded.message) return undefined // Check format if (!/^(00|01)[0-9a-fA-F]+$/.test(decoded.message)) return '<Failed to interpret message>' return Message.isPlaintext(decoded.message) ? Message.plaintextToString( Buffer.from(decoded.message, 'hex'), ) : decoded.message })(), }), ) export const handleNetworkIdResponse = (json: NetworkIdEndpoint.Response) => JSONDecoding.withDecoders(networkDecoder('networkId')) .create< NetworkIdEndpoint.Response, NetworkIdEndpoint.DecodedResponse >()(json) .andThen(decoded => hasRequiredProps('networkId', decoded, ['networkId']), ) export const handleTokenBalancesResponse = ( json: TokenBalancesEndpoint.Response, ): Result<TokenBalancesEndpoint.DecodedResponse, Error[]> => pipe( (json: TokenBalancesEndpoint.Response) => ({ owner: json.owner, tokenBalances: json.tokenBalances.map(balance => ({ tokenIdentifier: balance.rri, amount: balance.amount, })), }), JSONDecoding.withDecoders( addressDecoder('owner'), RRIDecoder('tokenIdentifier'), amountDecoder('amount'), ).create< { owner: string tokenBalances: { tokenIdentifier: string amount: string }[] }, TokenBalancesEndpoint.DecodedResponse >(), )(json).andThen(decoded => hasRequiredProps('tokenBalances', decoded, ['owner', 'tokenBalances']), ) const validatorDecoders = JSONDecoding.withDecoders( validatorAddressDecoder('address'), addressDecoder('ownerAddress'), URLDecoder('infoURL'), amountDecoder('totalDelegatedStake', 'ownerDelegation'), ) export const handleValidatorsResponse = (json: ValidatorsEndpoint.Response) => validatorDecoders .create< ValidatorsEndpoint.Response, ValidatorsEndpoint.DecodedResponse >()(json) .andThen(decoded => hasRequiredProps('validators', decoded, ['cursor', 'validators']), ) export const handleLookupValidatorResponse = validatorDecoders.create< LookupValidatorEndpoint.Response, LookupValidatorEndpoint.DecodedResponse >() export const handleTokenInfoResponse = (json: TokenInfoEndpoint.Response) => JSONDecoding.withDecoders( RRIDecoder('rri'), amountDecoder('granularity', 'currentSupply'), URLDecoder('tokenInfoURL', 'iconURL'), ) .create< TokenInfoEndpoint.Response, TokenInfoEndpoint.DecodedResponse >()(json) .andThen(decoded => hasRequiredProps('tokenInfo', decoded, [ 'name', 'rri', 'symbol', 'granularity', 'isSupplyMutable', 'currentSupply', 'tokenInfoURL', 'iconURL', ]), ) export const handleStakesResponse = JSONDecoding.withDecoders( validatorAddressDecoder('validator'), amountDecoder('amount'), ).create< StakePositionsEndpoint.Response, StakePositionsEndpoint.DecodedResponse >() export const handleUnstakesResponse = JSONDecoding.withDecoders( validatorAddressDecoder('validator'), amountDecoder('amount'), transactionIdentifierDecoder('withdrawTxID'), ).create< UnstakePositionsEndpoint.Response, UnstakePositionsEndpoint.DecodedResponse >() export const handleTransactionStatusResponse = ( json: TransactionStatusEndpoint.Response, ): Result<TransactionStatusEndpoint.DecodedResponse, Error[]> => isRPCRequestFailureResponse(json) ? err([new Error(json.failure)]) : JSONDecoding.withDecoders(transactionIdentifierDecoder('txID')) .create< TransactionStatusEndpoint.Response, TransactionStatusEndpoint.DecodedResponse >()(json) .andThen(decoded => hasRequiredProps('transactionStatus', decoded, [ 'txID', 'status', ]), ) export const handleNetworkxThroughputResponse = ( json: NetworkTransactionThroughputEndpoint.Response, ) => JSONDecoding.create< NetworkTransactionThroughputEndpoint.Response, NetworkTransactionThroughputEndpoint.DecodedResponse >()(json).andThen(decoded => hasRequiredProps('NetworkTransactionThroughput', decoded, ['tps']), ) export const handleNetworkxDemandResponse = ( json: NetworkTransactionDemandEndpoint.Response, ) => JSONDecoding.create< NetworkTransactionDemandEndpoint.Response, NetworkTransactionDemandEndpoint.DecodedResponse >()(json).andThen(decoded => hasRequiredProps('NetworkTransactionDemand', decoded, ['tps']), ) export const handleBuildTransactionResponse = ( json: BuildTransactionEndpoint.Response, ): Result<BuildTransactionEndpoint.DecodedResponse, Error[]> => JSONDecoding.withDecoders(amountDecoder('fee')) .create< BuildTransactionEndpoint.Response, BuildTransactionEndpoint.DecodedResponse >()(json) .andThen(decoded => hasRequiredProps('buildTransaction', decoded, [ 'transaction', 'fee', ]), ) export const handleFinalizeTransactionResponse = ( json: FinalizeTransactionEndpoint.Response, ): Result<FinalizeTransactionEndpoint.DecodedResponse, Error[]> => isRPCRequestFailureResponse(json) ? err([new Error(json.failure)]) : JSONDecoding.withDecoders(transactionIdentifierDecoder('txID')) .create< FinalizeTransactionEndpoint.Response, FinalizeTransactionEndpoint.DecodedResponse >()(json) .andThen(decoded => hasRequiredProps('finalizeTransaction', decoded, ['txID']), ) export const handleSubmitTransactionResponse = ( json: SubmitTransactionEndpoint.Response, ): Result<SubmitTransactionEndpoint.DecodedResponse, Error[]> => isRPCRequestFailureResponse(json) ? err([new Error(json.failure)]) : JSONDecoding.withDecoders(transactionIdentifierDecoder('txID')) .create< SubmitTransactionEndpoint.Response, SubmitTransactionEndpoint.DecodedResponse >()(json) .andThen(decoded => hasRequiredProps('submitTransaction', decoded, ['txID']), )