UNPKG

@radixdlt/application

Version:

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

860 lines (769 loc) 26.4 kB
import { AmountOrUnsafeInput, AmountT, uint256Max, Network, } from '@radixdlt/primitives' import { AccountAddress, AccountAddressT, ResourceIdentifier, ResourceIdentifierT, ValidatorAddress, ValidatorAddressT, } from '@radixdlt/account' import { Observable, of } from 'rxjs' import { BuiltTransaction, ExecutedTransaction, FinalizedTransaction, NetworkTransactionDemand, NetworkTransactionThroughput, PendingTransaction, SignedTransaction, SimpleExecutedTransaction, SimpleTokenBalance, SimpleTokenBalances, StakePosition, StakePositions, StatusOfTransaction, Token, TransactionHistory, TransactionHistoryRequestInput, TransactionIdentifierT, TransactionIntent, TransactionStatus, TransactionType, UnstakePosition, UnstakePositions, Validator, Validators, ValidatorsRequestInput, TransactionIdentifier, } from './dto' import { RadixCoreAPI } from './api' import { shareReplay } from 'rxjs/operators' import { PrivateKey, PublicKeyT, sha256 } from '@radixdlt/crypto' import { ActionType, ExecutedAction } from './actions' import { Amount } from '@radixdlt/primitives' export const xrd: Token = { name: 'Rad', rri: ResourceIdentifier.fromUnsafe('xrd_tr1qyf0x76s')._unsafeUnwrap(), symbol: 'XRD', description: 'The native coin of Radix network', granularity: Amount.fromUnsafe(1)._unsafeUnwrap(), isSupplyMutable: false, currentSupply: uint256Max, tokenInfoURL: new URL('https://www.radixdlt.com'), iconURL: new URL('https://www.image.radixdlt.com/'), } export const fooToken: Token = { name: 'Foo token', rri: ResourceIdentifier.fromUnsafe('xrd_tr1qyf0x76s')._unsafeUnwrap(), symbol: 'FOO', description: 'FOOest token.', granularity: Amount.fromUnsafe(1)._unsafeUnwrap(), isSupplyMutable: false, currentSupply: uint256Max, tokenInfoURL: new URL('https://www.footoken.com'), iconURL: new URL('https://www.image.footoken.com/'), } export const barToken: Token = { name: 'Bar token', rri: ResourceIdentifier.fromUnsafe('xrd_tr1qyf0x76s')._unsafeUnwrap(), symbol: 'BAR', description: 'Bar token. Granularity E-3.', granularity: Amount.fromUnsafe(1000)._unsafeUnwrap(), isSupplyMutable: true, currentSupply: uint256Max, tokenInfoURL: new URL('https://www.bartoken.com'), iconURL: new URL('https://www.image.bartoken.com/'), } export const goldToken: Token = { name: 'Gold token', rri: ResourceIdentifier.fromUnsafe('xrd_tr1qyf0x76s')._unsafeUnwrap(), symbol: 'GOLD', description: 'Gold token. Granularity E-12.', granularity: Amount.fromUnsafe(1_000_000)._unsafeUnwrap(), isSupplyMutable: false, currentSupply: uint256Max, tokenInfoURL: new URL('https://www.goldtoken.com'), iconURL: new URL('https://www.image.goldtoken.com/'), } export const radixWrappedBitcoinToken: Token = { name: 'Bitcoin (wrapped on Radix)', rri: ResourceIdentifier.fromUnsafe('xrd_tr1qyf0x76s')._unsafeUnwrap(), symbol: 'BTCRW', description: 'Radix wrapped Bitcoin. Granularity E-18.', granularity: Amount.fromUnsafe(1)._unsafeUnwrap(), isSupplyMutable: true, currentSupply: uint256Max, tokenInfoURL: new URL('https://www.bitcoin.radix.com'), iconURL: new URL('https://www.image.bitcoin.radix.com/'), } export const radixWrappedEtherToken: Token = { name: 'Ether (wrapped on Radix)', rri: ResourceIdentifier.fromUnsafe('xrd_tr1qyf0x76s')._unsafeUnwrap(), symbol: 'ETHRW', description: 'Radix wrapped Ether. Granularity E-9.', granularity: Amount.fromUnsafe(1_000_000_000)._unsafeUnwrap(), isSupplyMutable: true, currentSupply: uint256Max, tokenInfoURL: new URL('https://www.ether.radix.com'), iconURL: new URL('https://www.image.ether.radix.com/'), } export const __fallBackAlexToken: Token = { name: 'Alex token', rri: ResourceIdentifier.fromUnsafe('xrd_tr1qyf0x76s')._unsafeUnwrap(), symbol: 'ALEX', description: 'Fallback token for when token for requested symbol was not found.', granularity: Amount.fromUnsafe(1)._unsafeUnwrap(), isSupplyMutable: true, currentSupply: uint256Max, tokenInfoURL: new URL('https://www.alex.token.com'), iconURL: new URL('https://www.image.alex.token.com/'), } export const balanceOfFor = ( input: Readonly<{ token: Token amount: AmountOrUnsafeInput }>, ): SimpleTokenBalance => { const amt = Amount.fromUnsafe(input.amount)._unsafeUnwrap() return { tokenIdentifier: input.token.rri, amount: amt.lt(input.token.currentSupply) ? amt : input.token.currentSupply, } } export const balancesFor = ( address: AccountAddressT, amount: number, ): SimpleTokenBalances => ({ owner: address, tokenBalances: [ balanceOfFor({ token: xrd, amount, }), ], }) const differentTokens: Token[] = [ xrd, fooToken, barToken, radixWrappedBitcoinToken, radixWrappedEtherToken, goldToken, ] // PLEASE KEEP - used as Cast of characters: https://en.wikipedia.org/wiki/Alice_and_Bob#Cast_of_characters export const tokenByRRIMap: Map< ResourceIdentifierT, Token > = differentTokens.reduce( (a: Map<ResourceIdentifierT, Token>, b: Token) => a.set(b.rri, b), new Map<ResourceIdentifierT, Token>(), ) const detPRNGWithBuffer = (buffer: Buffer): (() => number) => { const bufCopy = Buffer.from(buffer) let bytes = Buffer.from(buffer) return (): number => { if (bytes.length === 0) { bytes = sha256(bufCopy) } const lengthToSlice = 2 const buf = bytes.slice(0, lengthToSlice) bytes = bytes.slice(lengthToSlice, bytes.length) return Number.parseInt(buf.toString('hex'), 16) } } const addressesString: string[] = [ 'tdx1qspksqs77z9e24e2dr9t5de6a9kymzhszp9k7jmr2ldkzl4hvn45xsqk409dt', 'tdx1qspksqs77z9e24e2dr9t5de6a9kymzhszp9k7jmr2ldkzl4hvn45xsqk409dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', 'rdx1qsps28kdn4epn0c9ej2rcmwfz5a4jdhq2ez03x7h6jefvr4fnwnrtqqjaj7dt', ] const characterNames: string[] = [ 'alice', 'bob', 'carol', 'dan', 'erin', 'frank', 'grace', 'heidi', 'ivan', 'judy', 'klara', 'leonard', 'mallory', 'niaj', 'olivia', 'peggy', 'quentin', 'rupert', 'stella', 'ted', 'ursula', 'victor', 'webdy', 'xerxez', 'yara', 'zelda', ] /* * [Property in keyof ReturnType<typeof getAPI>]: ReturnType< typeof getAPI >[Property] * */ export const castOfCharacters: AccountAddressT[] = addressesString .map(s => AccountAddress.fromUnsafe(s)._unsafeUnwrap({ withStackTrace: true }), ) .slice(0, characterNames.length) export const alice = castOfCharacters[0] export const bob = castOfCharacters[1] export const carol = castOfCharacters[2] export const dan = castOfCharacters[3] export const erin = castOfCharacters[4] const makeListOfValidatorAddresses = (): ValidatorAddressT[] => { const stringAddresses = [ 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', 'tv1qdqft0u899axwce955fkh9rundr5s2sgvhpp8wzfe3ty0rn0rgqj2x6y86p', ] return stringAddresses.map(s => ValidatorAddress.fromUnsafe(s)._unsafeUnwrap({ withStackTrace: true }), ) } const listOfValidatorAddresses: ValidatorAddressT[] = makeListOfValidatorAddresses() const detRandomValidatorAddressWithPRNG = ( anInt: () => number, ) => (): ValidatorAddressT => { const randomInt = anInt() const index = randomInt % (listOfValidatorAddresses.length - 1) return listOfValidatorAddresses[index] } const randomValidatorList = ( size: number, validatorAddress?: ValidatorAddressT, ): Validator[] => { const validatorList: Validator[] = [] const randomBuf = validatorAddress !== undefined ? sha256(validatorAddress.toString()) : sha256(size.toString(16)) const prng = detPRNGWithBuffer(randomBuf) const detRandomValidatorAddress = detRandomValidatorAddressWithPRNG(prng) const listSize = prng() % 5 === 1 ? size - Math.round(size / 2) : size for (let i = 0; i < listSize; i++) { const random = prng() const ownerAddress = castOfCharacters[random % castOfCharacters.length] const name = characterNames[random % characterNames.length] const amount = Amount.fromUnsafe(random)._unsafeUnwrap() const bool = random % 2 === 0 validatorList.push({ address: detRandomValidatorAddress(), ownerAddress, name, infoURL: new URL('https://rewards.radixtokens.comcom'), totalDelegatedStake: amount, ownerDelegation: amount, validatorFee: 2.5, registered: bool, isExternalStakeAccepted: bool, uptimePercentage: 100.0, proposalsMissed: 10, proposalsCompleted: 20, }) } return validatorList } const randomUnsignedTransaction = ( transactionIntent: TransactionIntent, ): BuiltTransaction => { const transactionIntentDet = { ...transactionIntent, actions: transactionIntent.actions.map(a => ({ ...a, })), } const detBlob = JSON.stringify(transactionIntentDet, null, 4) const blobBytes = Buffer.from(detBlob) const bytes32 = sha256(blobBytes) const anInt = detPRNGWithBuffer(bytes32) return { transaction: { blob: blobBytes.toString('hex'), hashOfBlobToSign: bytes32.toString('hex'), }, fee: Amount.fromUnsafe(anInt())._unsafeUnwrap(), } } const randomPendingTransaction = (signedTx: SignedTransaction) => ({ txID: TransactionIdentifier.create( sha256(Buffer.from(signedTx.transaction.blob)), )._unsafeUnwrap(), blob: 'awd', }) const detRandomSignedUnconfirmedTransaction = ( signedTransaction: SignedTransaction, ): FinalizedTransaction => ({ ...randomPendingTransaction(signedTransaction), }) const rndDemand = detPRNGWithBuffer(Buffer.from('dmnd')) const randomDemand = (): NetworkTransactionDemand => ({ tps: rndDemand() % 200, }) const rndThroughput = detPRNGWithBuffer(Buffer.from('trpt')) const randomThroughput = (): NetworkTransactionDemand => ({ tps: rndThroughput() % 200, }) const detPRNGWithPubKey = (pubKey: PublicKeyT): (() => number) => { // cannot use first, since it is always 02 or 03 const bytes = pubKey.asData({ compressed: true }).slice(1, 33) return detPRNGWithBuffer(bytes) } type BalanceOfTokenWithInfo = Readonly<{ token: Token amount: AmountT }> const detRandBalanceOfTokenWithInfo = ( png: () => number, ): BalanceOfTokenWithInfo[] => { const anInt = png const availableTokens = [...differentTokens] const deterministicRandomToken = (): Token => { const tokenCount = availableTokens.length const tokenIndex = anInt() % tokenCount const token = availableTokens[tokenIndex] availableTokens.splice(tokenIndex, 1) return token } const size = Math.max(anInt() % availableTokens.length, 1) return Array(size) .fill(undefined) .map( (_): BalanceOfTokenWithInfo => { const token = deterministicRandomToken() const amtOrZero = anInt() % 10_000 const amtFactor = Amount.fromUnsafe( Math.max(10, amtOrZero), )._unsafeUnwrap() const amount = Amount.fromUnsafe( token.granularity.mul(amtFactor), )._unsafeUnwrap() return { token, amount, } }, ) } export const deterministicRandomBalancesForAddress = ( address: AccountAddressT, ): SimpleTokenBalances => { const anInt = detPRNGWithPubKey(address.publicKey) const tokenBalances = detRandBalanceOfTokenWithInfo(anInt).map(bti => balanceOfFor(bti), ) return { owner: address, tokenBalances, } } export const deterministicRandomUnstakesForAddress = ( address: AccountAddressT, ): UnstakePositions => { const anInt = detPRNGWithPubKey(address.publicKey) const size = anInt() % 7 return Array(size) .fill(undefined) .map( (_, index): UnstakePosition => { const detRandomValidatorAddress = detRandomValidatorAddressWithPRNG( anInt, ) const validator: ValidatorAddressT = detRandomValidatorAddress() const amount = Amount.fromUnsafe(anInt())._unsafeUnwrap() const bytesFromIndex = Buffer.allocUnsafe(2) bytesFromIndex.writeUInt16BE(index) const txIDBuffer = sha256( Buffer.concat([ address.publicKey.asData({ compressed: true }), bytesFromIndex, ]), ) const withdrawTxID = TransactionIdentifier.create( txIDBuffer, )._unsafeUnwrap() const epochsUntil = anInt() % 5 return { amount, validator, epochsUntil: epochsUntil > 60 ? 0 : epochsUntil, withdrawTxID, } }, ) } export const deterministicRandomStakesForAddress = ( address: AccountAddressT, ): StakePositions => deterministicRandomUnstakesForAddress(address).map( (un): StakePosition => ({ ...un, }), ) export const deterministicRandomTxHistoryWithInput = ( input: TransactionHistoryRequestInput, ): TransactionHistory => { const address = input.address const anInt: () => number = detPRNGWithPubKey(address.publicKey) const pubKeyBytes = address.publicKey .asData({ compressed: true }) .slice(1, 33) const detRandomAddress = (): AccountAddressT => castOfCharacters[anInt() % castOfCharacters.length] const detRandomValidatorAddress = detRandomValidatorAddressWithPRNG(anInt) const tokenAndAmounts = detRandBalanceOfTokenWithInfo(anInt) const deterministicRandomExecutedTransactions = (): ExecutedTransaction[] => Array(input.size) .fill(undefined) .map( (_, index): ExecutedTransaction => { const bytesFromIndex = Buffer.allocUnsafe(2) bytesFromIndex.writeUInt16BE(index) const txIDBuffer = sha256( Buffer.concat([pubKeyBytes, bytesFromIndex]), ) const date = new Date('2020-03-14T15:32:05') date.setMonth(index % 12) const txID = TransactionIdentifier.create( txIDBuffer, )._unsafeUnwrap() const detMakeActionForTx = (): ExecutedAction[] => { // mock max 5 actions per tx in history, min 1. const actionCount = Math.max(anInt() % 5, 1) return Array(actionCount) .fill(undefined) .map( (_, actionIndex): ExecutedAction => { const v: number = anInt() % 4 // Transfer, Stake, Unstake, Other const actionType: ActionType = v === 0 ? ActionType.TOKEN_TRANSFER : v === 1 ? ActionType.STAKE_TOKENS : v === 2 ? ActionType.UNSTAKE_TOKENS : ActionType.OTHER let executedAction: ExecutedAction const tokenAndAmount = tokenAndAmounts[ actionIndex % tokenAndAmounts.length ]! switch (actionType) { case ActionType.OTHER: executedAction = { type: ActionType.OTHER, } break case ActionType.STAKE_TOKENS: executedAction = { type: ActionType.STAKE_TOKENS, from: address, amount: Amount.fromUnsafe( anInt(), )._unsafeUnwrap(), validator: detRandomValidatorAddress(), } as any break case ActionType.UNSTAKE_TOKENS: executedAction = { type: ActionType.UNSTAKE_TOKENS, from: address, amount: Amount.fromUnsafe( anInt(), )._unsafeUnwrap(), validator: detRandomValidatorAddress(), } as any break case ActionType.TOKEN_TRANSFER: executedAction = { type: ActionType.TOKEN_TRANSFER, from_account: address.toString(), to_account: detRandomAddress().toString(), amount: tokenAndAmount.amount, rri: tokenAndAmount.token.rri, } break } return executedAction }, ) } const rndTxTypeInt = anInt() % 3 const transactionType = rndTxTypeInt === 0 ? TransactionType.INCOMING : rndTxTypeInt === 1 ? TransactionType.FROM_ME_TO_ME : TransactionType.OUTGOING return { txID, sentAt: date, transactionType, fee: Amount.fromUnsafe(anInt())._unsafeUnwrap(), // message?: { // msg: string // encryptionScheme: string // } actions: detMakeActionForTx(), } as any }, ) const updatedCursor = sha256( input.cursor !== undefined ? Buffer.from(input.cursor) : pubKeyBytes, ).toString('hex') return { cursor: updatedCursor, transactions: deterministicRandomExecutedTransactions(), } } const deterministicRandomLookupTXUsingHist = ( txID: TransactionIdentifierT, ): SimpleExecutedTransaction => { const seed = sha256(Buffer.from(txID.__hex, 'hex')) const addressWithTXIdBytesAsSeed = AccountAddress.fromPublicKeyAndNetwork({ publicKey: PrivateKey.fromBuffer(seed)._unsafeUnwrap().publicKey(), network: Network.MAINNET, }) const txs = deterministicRandomTxHistoryWithInput({ size: 1, address: addressWithTXIdBytesAsSeed, }).transactions if (txs.length === 0) { throw new Error('Expected at least one tx...') } return { ...txs[0], txID, } } export const deterministicRandomBalances = ( address: AccountAddressT, ): Observable<SimpleTokenBalances> => of(deterministicRandomBalancesForAddress(address)) export const deterministicRandomTXHistory = ( input: TransactionHistoryRequestInput, ): Observable<TransactionHistory> => of(deterministicRandomTxHistoryWithInput(input)) export const deterministicRandomLookupTX = ( txID: TransactionIdentifierT, ): Observable<SimpleExecutedTransaction> => of(deterministicRandomLookupTXUsingHist(txID)) export const deterministicRandomUnstakesForAddr = ( address: AccountAddressT, ): Observable<UnstakePositions> => of(deterministicRandomUnstakesForAddress(address)) export const deterministicRandomStakesForAddr = ( address: AccountAddressT, ): Observable<StakePositions> => of(deterministicRandomStakesForAddress(address)) export const makeThrowingRadixCoreAPI = (nodeUrl?: string): any => ({ node: { url: new URL(nodeUrl ?? 'https://www.radixdlt.com/') }, networkId: (): Observable<Network> => { throw Error('Not implemented') }, tokenBalancesForAddress: (_address: AccountAddressT): Observable<any> => { throw Error('Not implemented') }, lookupTransaction: (_txID: any): Observable<any> => { throw Error('Not implemented') }, validators: (_input: any): Observable<any> => { throw Error('Not implemented') }, lookupValidator: (_input: ValidatorAddressT): Observable<any> => { throw Error('Not implemented') }, transactionHistory: ( _input: TransactionHistoryRequestInput, ): Observable<any> => { throw Error('Not implemented') }, nativeToken: (): Observable<any> => { throw Error('Not implemented') }, tokenInfo: (_rri: ResourceIdentifierT): Observable<any> => { throw Error('Not implemented') }, stakesForAddress: (_address: AccountAddressT): Observable<any> => { throw Error('Not implemented') }, unstakesForAddress: (_address: AccountAddressT): Observable<any> => { throw Error('Not implemented') }, transactionStatus: ( _txID: TransactionIdentifierT, ): Observable<StatusOfTransaction> => { throw Error('Not implemented') }, NetworkTransactionThroughput: (): Observable<NetworkTransactionThroughput> => { throw Error('Not implemented') }, NetworkTransactionDemand: (): Observable<NetworkTransactionDemand> => { throw Error('Not implemented') }, buildTransaction: ( _transactionIntent: TransactionIntent, ): Observable<any> => { throw Error('Not implemented') }, submitSignedTransaction: (_signedTransaction: any): Observable<any> => { throw Error('Not implemented') }, finalizeTransaction: ( _signedUnconfirmedTransaction: SignedTransaction, ): Observable<FinalizedTransaction> => { throw Error('Not implemented') }, }) let txStatusMapCounter: Map< TransactionIdentifierT, number > = (undefined as unknown) as Map<TransactionIdentifierT, number> export const mockRadixCoreAPI = ( input?: Readonly<{ nodeUrl?: string network?: Network }>, ): any => { txStatusMapCounter = new Map<TransactionIdentifierT, number>() return { node: { url: new URL(input?.nodeUrl ?? 'https://www.radixdlt.com/') }, networkId: (): Observable<Network> => of(input?.network ?? Network.MAINNET).pipe(shareReplay(1)), nativeToken: (): Observable<Token> => of(xrd), tokenInfo: (rri: ResourceIdentifierT): Observable<Token> => of(tokenByRRIMap.get(rri) ?? __fallBackAlexToken), tokenBalancesForAddress: deterministicRandomBalances, transactionStatus: ( txID: TransactionIdentifierT, ): Observable<StatusOfTransaction> => { const last = txStatusMapCounter.get(txID) ?? 0 const incremented = last + 1 txStatusMapCounter.set(txID, incremented) const status: TransactionStatus = last <= 1 ? TransactionStatus.PENDING : TransactionStatus.CONFIRMED return of({ txID, status, // when TransactionStatus.FAIL ? }) }, validators: (input: ValidatorsRequestInput): Observable<Validators> => of({ cursor: 'cursor', validators: randomValidatorList(input.size), }), lookupValidator: ( validatorAddress: ValidatorAddressT, ): Observable<Validator> => { const validatorRnd = randomValidatorList(1, validatorAddress)[0] const validator: Validator = { ...validatorRnd, address: validatorAddress, } return of(validator) }, buildTransaction: ( transactionIntent: TransactionIntent, ): Observable<BuiltTransaction> => of(randomUnsignedTransaction(transactionIntent)), finalizeTransaction: ( signedTransaction: SignedTransaction, ): Observable<FinalizedTransaction> => of(detRandomSignedUnconfirmedTransaction(signedTransaction)), submitSignedTransaction: (signedUnconfirmedTX: any) => of(signedUnconfirmedTX), NetworkTransactionDemand: (): Observable<NetworkTransactionDemand> => of(randomDemand()), NetworkTransactionThroughput: (): Observable<NetworkTransactionThroughput> => of(randomThroughput()), transactionHistory: deterministicRandomTXHistory, lookupTransaction: deterministicRandomLookupTX, unstakesForAddress: deterministicRandomUnstakesForAddr, stakesForAddress: deterministicRandomStakesForAddr, } } export const mockedAPI: Observable<any> = of(mockRadixCoreAPI())