From 4d203c6eecb525dfb8fa8ac84add2f607e77b1ee Mon Sep 17 00:00:00 2001 From: Denis Davidyuk Date: Mon, 25 Jul 2022 15:31:57 +0300 Subject: [PATCH 1/6] refactor(tx-builder): copy Tag definition from aeserialization --- docs/guides/low-vs-high-usage.md | 2 +- .../node/paying-for-tx-contract-call-tx.js | 4 +- examples/node/paying-for-tx-spend-tx.js | 4 +- src/AeSdkBase.ts | 4 +- src/account/Base.ts | 4 +- src/aens.ts | 22 ++-- src/channel/handlers.ts | 10 +- src/channel/index.ts | 14 +-- src/contract/aci.ts | 14 +-- src/contract/ga.ts | 16 +-- src/deprecated/index.ts | 58 ++++++++- src/oracle.ts | 20 ++-- src/spend.ts | 18 +-- src/tx/builder/constants.ts | 112 ++++++++++-------- src/tx/builder/field-types/fee.ts | 60 +++++----- src/tx/builder/field-types/gas-limit.ts | 6 +- src/tx/builder/index.ts | 18 +-- src/tx/builder/schema.ts | 106 ++++++++--------- src/tx/index.ts | 68 +++++------ src/tx/validator.ts | 24 ++-- test/integration/chain.ts | 4 +- test/integration/channel.ts | 46 +++---- test/integration/ga.ts | 4 +- test/integration/paying-for.ts | 4 +- test/integration/rpc.ts | 20 ++-- test/integration/transaction.ts | 30 ++--- test/integration/txVerification.ts | 10 +- test/unit/tx.ts | 6 +- 28 files changed, 388 insertions(+), 320 deletions(-) diff --git a/docs/guides/low-vs-high-usage.md b/docs/guides/low-vs-high-usage.md index 93dce285b6..c93b8120f6 100644 --- a/docs/guides/low-vs-high-usage.md +++ b/docs/guides/low-vs-high-usage.md @@ -54,7 +54,7 @@ async function spend (amount, recipient) { ) // builds an unsigned SpendTx using the debug endpoint of the node's API - const spendTx = await aeSdk.buildTx(TX_TYPE.spend, { + const spendTx = await aeSdk.buildTx(Tag.SpendTx, { senderId: await aeSdk.address(), recipientId: recipient, fee: 18000000000000, // you must provide enough fee diff --git a/examples/node/paying-for-tx-contract-call-tx.js b/examples/node/paying-for-tx-contract-call-tx.js index 9b39c2cb8a..45780ead6c 100644 --- a/examples/node/paying-for-tx-contract-call-tx.js +++ b/examples/node/paying-for-tx-contract-call-tx.js @@ -48,7 +48,7 @@ // You need to import `AeSdk`, `Node` and `MemoryAccount` classes from the SDK. // Additionally you import the `generateKeyPair` utility function to generate a new keypair. const { - AeSdk, Node, MemoryAccount, generateKeyPair, TX_TYPE, + AeSdk, Node, MemoryAccount, generateKeyPair, Tag, } = require('@aeternity/aepp-sdk'); // **Note**: @@ -131,7 +131,7 @@ const NEW_USER_KEYPAIR = generateKeyPair(); { source: CONTRACT_SOURCE, contractAddress: CONTRACT_ADDRESS }, ); const calldata = contract.calldata.encode('PayingForTxExample', 'set_last_caller', []); - const contractCallTx = await aeSdk.buildTx(TX_TYPE.contractCall, { + const contractCallTx = await aeSdk.buildTx(Tag.ContractCallTx, { callerId: await newUserAccount.address(), contractId: CONTRACT_ADDRESS, amount: 0, diff --git a/examples/node/paying-for-tx-spend-tx.js b/examples/node/paying-for-tx-spend-tx.js index f869d85836..a6261269d3 100644 --- a/examples/node/paying-for-tx-spend-tx.js +++ b/examples/node/paying-for-tx-spend-tx.js @@ -34,7 +34,7 @@ // You need to import `AeSdk`, `Node` and `MemoryAccount` classes from the SDK. // Additionally you import the `generateKeyPair` utility function to generate a new keypair. const { - AeSdk, Node, MemoryAccount, generateKeyPair, TX_TYPE, + AeSdk, Node, MemoryAccount, generateKeyPair, Tag, } = require('@aeternity/aepp-sdk'); // **Note**: @@ -91,7 +91,7 @@ const AMOUNT = 1; // - The balance should now be 1 // ## 7. Create and sign `SpendTx` on behalf of new user - const spendTx = await aeSdk.buildTx(TX_TYPE.spend, { + const spendTx = await aeSdk.buildTx(Tag.SpendTx, { senderId: await newUserAccount.address(), recipientId: await payerAccount.address(), amount: AMOUNT, diff --git a/src/AeSdkBase.ts b/src/AeSdkBase.ts index 3c43ec6b9c..1af4659355 100644 --- a/src/AeSdkBase.ts +++ b/src/AeSdkBase.ts @@ -10,7 +10,7 @@ import { _buildTx } from './tx'; import { mapObject } from './utils/other'; import Node, { getNetworkId } from './Node'; import { AE_AMOUNT_FORMATS } from './utils/amount-formatter'; -import { AMOUNT, TX_TYPE } from './tx/builder/schema'; +import { AMOUNT, Tag } from './tx/builder/schema'; import MemoryAccount, { Keypair } from './account/Memory'; import AccountBase, { isAccountBase } from './account/Base'; import { @@ -259,7 +259,7 @@ class AeSdkBase { }; } - async buildTx( + async buildTx( txType: TxType, options: Omit>[1], 'onNode'> & { onNode?: Node }, ): Promise> { diff --git a/src/account/Base.ts b/src/account/Base.ts index 57772a168f..12b0234ac8 100644 --- a/src/account/Base.ts +++ b/src/account/Base.ts @@ -17,7 +17,7 @@ import { messageToHash, verifyMessage as verifyMessageCrypto, hash } from '../utils/crypto'; import { buildTx } from '../tx/builder'; import { decode, EncodedData } from '../utils/encoder'; -import { TX_TYPE } from '../tx/builder/schema'; +import { Tag } from '../tx/builder/schema'; import { getNetworkId } from '../Node'; import { concatBuffers } from '../utils/other'; import type { createMetaTx } from '../contract/ga'; @@ -67,7 +67,7 @@ export default abstract class AccountBase { const txWithNetworkId = concatBuffers([Buffer.from(prefixes.join('-')), hash(rlpBinaryTx)]); const signatures = [await this.sign(txWithNetworkId, options)]; - return buildTx({ encodedTx: rlpBinaryTx, signatures }, TX_TYPE.signed).tx; + return buildTx({ encodedTx: rlpBinaryTx, signatures }, Tag.SignedTx).tx; } /** diff --git a/src/aens.ts b/src/aens.ts index 4c83e5ad23..323822392a 100644 --- a/src/aens.ts +++ b/src/aens.ts @@ -27,7 +27,7 @@ import BigNumber from 'bignumber.js'; import { genSalt } from './utils/crypto'; import { commitmentHash, isAuctionName } from './tx/builder/helpers'; import { - CLIENT_TTL, NAME_TTL, TX_TYPE, AensName, + CLIENT_TTL, NAME_TTL, Tag, AensName, } from './tx/builder/schema'; import { ArgumentError } from './utils/errors'; import { EncodedData } from './utils/encoder'; @@ -67,7 +67,7 @@ export async function aensRevoke( name: AensName, options: AensRevokeOptions, ): ReturnType { - const nameRevokeTx = await _buildTx(TX_TYPE.nameRevoke, { + const nameRevokeTx = await _buildTx(Tag.NameRevokeTx, { ...options, nameId: name, accountId: await options.onAccount.address(options), @@ -76,7 +76,7 @@ export async function aensRevoke( } interface AensRevokeOptions extends - BuildTxOptions, + BuildTxOptions, SendOptions {} /** @@ -121,7 +121,7 @@ export async function aensUpdate( ...pointers, }; - const nameUpdateTx = await _buildTx(TX_TYPE.nameUpdate, { + const nameUpdateTx = await _buildTx(Tag.NameUpdateTx, { clientTtl: CLIENT_TTL, nameTtl: NAME_TTL, ...options, @@ -134,7 +134,7 @@ export async function aensUpdate( } interface AensUpdateOptions extends - BuildTxOptions, + BuildTxOptions, SendOptions { extendPointers?: boolean; clientTtl?: number; @@ -169,7 +169,7 @@ export async function aensTransfer( account: EncodedData<'ak'>, options: AensTransferOptions, ): ReturnType { - const nameTransferTx = await _buildTx(TX_TYPE.nameTransfer, { + const nameTransferTx = await _buildTx(Tag.NameTransferTx, { ...options, nameId: name, accountId: await options.onAccount.address(options), @@ -180,7 +180,7 @@ export async function aensTransfer( } interface AensTransferOptions extends - BuildTxOptions, + BuildTxOptions, SendOptions {} /** @@ -296,7 +296,7 @@ export async function aensClaim( salt: number, options: AensClaimOptions, ): Promise { - const claimTx = await _buildTx(TX_TYPE.nameClaim, { + const claimTx = await _buildTx(Tag.NameClaimTx, { ...options, accountId: await options.onAccount.address(options), nameSalt: salt, @@ -313,7 +313,7 @@ export async function aensClaim( return result; } -type AensClaimOptionsType = BuildTxOptions +type AensClaimOptionsType = BuildTxOptions & SendOptions & Parameters[1]; interface AensClaimOptions extends AensClaimOptionsType {} interface AensClaimReturnType extends @@ -356,7 +356,7 @@ Awaited> & { const height = await getHeight(options); const commitmentId = commitmentHash(name, salt); - const preclaimTx = await _buildTx(TX_TYPE.namePreClaim, { + const preclaimTx = await _buildTx(Tag.NamePreclaimTx, { ...options, accountId: await options.onAccount.address(options), commitmentId, @@ -374,7 +374,7 @@ Awaited> & { } interface AensPreclaimOptions extends - BuildTxOptions, + BuildTxOptions, SendOptions, AensClaimOptions {} diff --git a/src/channel/handlers.ts b/src/channel/handlers.ts index 005aa9fa9e..db260383aa 100644 --- a/src/channel/handlers.ts +++ b/src/channel/handlers.ts @@ -43,20 +43,20 @@ import { UnexpectedChannelMessageError, } from '../utils/errors'; import type Channel from '.'; -import { TX_TYPE } from '../tx/builder/schema'; +import { Tag } from '../tx/builder/schema'; export async function appendSignature( tx: EncodedData<'tx'>, signFn: SignTx, ): Promise | number | null> { - const { signatures, encodedTx } = unpackTx(tx, TX_TYPE.signed).tx; + const { signatures, encodedTx } = unpackTx(tx, Tag.SignedTx).tx; const result = await signFn(encode(encodedTx.rlpEncoded, 'tx')); if (typeof result === 'string') { - const { tx: signedTx } = unpackTx(result, TX_TYPE.signed); + const { tx: signedTx } = unpackTx(result, Tag.SignedTx); return buildTx({ signatures: signatures.concat(signedTx.signatures), encodedTx: signedTx.encodedTx.rlpEncoded, - }, TX_TYPE.signed).tx; + }, Tag.SignedTx).tx; } return result; } @@ -660,7 +660,7 @@ export function awaitingNewContractCompletion( ): ChannelFsm { const channelOptions = options.get(channel); if (message.method === 'channels.update') { - const { round } = unpackTx(message.params.data.state, TX_TYPE.signed).tx.encodedTx.tx; + const { round } = unpackTx(message.params.data.state, Tag.SignedTx).tx.encodedTx.tx; if (channelOptions?.role != null) { let role: null | 'initiatorId' | 'responderId' = null; if (channelOptions.role === 'initiator') role = 'initiatorId'; diff --git a/src/channel/index.ts b/src/channel/index.ts index 8c2a815b89..1180183d42 100644 --- a/src/channel/index.ts +++ b/src/channel/index.ts @@ -17,7 +17,7 @@ import BigNumber from 'bignumber.js'; import { snakeToPascal } from '../utils/string'; import { buildTx, unpackTx } from '../tx/builder'; -import { MIN_GAS_PRICE, TX_TYPE } from '../tx/builder/schema'; +import { MIN_GAS_PRICE, Tag } from '../tx/builder/schema'; import * as handlers from './handlers'; import { eventEmitters, @@ -222,13 +222,13 @@ export default class Channel { if (state == null) { return null; } - const { txType, tx } = unpackTx(state, TX_TYPE.signed).tx.encodedTx; + const { txType, tx } = unpackTx(state, Tag.SignedTx).tx.encodedTx; switch (txType) { - case TX_TYPE.channelCreate: + case Tag.ChannelCreateTx: return 1; - case TX_TYPE.channelOffChain: - case TX_TYPE.channelWithdraw: - case TX_TYPE.channelDeposit: + case Tag.ChannelOffChainTx: + case Tag.ChannelWithdrawTx: + case Tag.ChannelDepositTx: return tx.round; default: return null; @@ -1008,7 +1008,7 @@ export default class Channel { return Channel.initialize({ ...options, - reconnectTx: await sign('reconnect', buildTx(txParams, TX_TYPE.channelReconnect).tx), + reconnectTx: await sign('reconnect', buildTx(txParams, Tag.ChannelClientReconnectTx).tx), }); } } diff --git a/src/contract/aci.ts b/src/contract/aci.ts index dca1ab3798..8d0d6dd516 100644 --- a/src/contract/aci.ts +++ b/src/contract/aci.ts @@ -17,7 +17,7 @@ // @ts-expect-error TODO remove import { Encoder as Calldata } from '@aeternity/aepp-calldata'; import { - DRY_RUN_ACCOUNT, TX_TYPE, AMOUNT, AensName, + DRY_RUN_ACCOUNT, Tag, AMOUNT, AensName, } from '../tx/builder/schema'; import { buildContractIdByContractTx, unpackTx } from '../tx/builder'; import { _buildTx } from '../tx'; @@ -285,7 +285,7 @@ export default async function getContractInstance({ const sendAndProcess = async (tx: EncodedData<'tx'>, options: any): Promise<{ result?: ContractInstance['deployInfo']['result']; hash: TxData['hash']; - tx: Awaited>>; + tx: Awaited>>; txData: TxData; rawTx: EncodedData<'tx'>; }> => { @@ -293,7 +293,7 @@ export default async function getContractInstance({ const txData = await send(tx, options); const result = { hash: txData.hash, - tx: unpackTx(txData.rawTx), + tx: unpackTx(txData.rawTx), txData, rawTx: txData.rawTx, }; @@ -333,7 +333,7 @@ export default async function getContractInstance({ if (instance.deployInfo.address != null) throw new DuplicateContractError(); const ownerId = await opt.onAccount.address(options); - const tx = await _buildTx(TX_TYPE.contractCreate, { + const tx = await _buildTx(Tag.ContractCreateTx, { ...opt, gasLimit: opt.gasLimit ?? await instance._estimateGas('init', params, opt), callData: instance.calldata.encode(instance._name, 'init', params), @@ -407,8 +407,8 @@ export default async function getContractInstance({ opt.nonce = (await getAccount(callerId, { hash: opt.top, onNode })).nonce + 1; } const tx = await (fn === 'init' - ? _buildTx(TX_TYPE.contractCreate, { ...txOpt, code: instance.bytecode, ownerId: callerId }) - : _buildTx(TX_TYPE.contractCall, { ...txOpt, callerId, contractId })); + ? _buildTx(Tag.ContractCreateTx, { ...txOpt, code: instance.bytecode, ownerId: callerId }) + : _buildTx(Tag.ContractCallTx, { ...txOpt, callerId, contractId })); const { callObj, ...dryRunOther } = await txDryRun(tx, callerId, { onNode, ...opt }); if (callObj == null) throw new UnexpectedTsError(); @@ -418,7 +418,7 @@ export default async function getContractInstance({ }, tx); res = { ...dryRunOther, tx: unpackTx(tx), result: callObj }; } else { - const tx = await _buildTx(TX_TYPE.contractCall, { + const tx = await _buildTx(Tag.ContractCallTx, { ...opt, onNode, gasLimit: opt.gasLimit ?? await instance._estimateGas(fn, params, opt), diff --git a/src/contract/ga.ts b/src/contract/ga.ts index 09a3679d52..9ed2b55741 100644 --- a/src/contract/ga.ts +++ b/src/contract/ga.ts @@ -19,7 +19,7 @@ * Generalized Account module - routines to use generalized account */ -import { TX_TYPE, MAX_AUTH_FUN_GAS, TxSchema } from '../tx/builder/schema'; +import { Tag, MAX_AUTH_FUN_GAS, TxSchema } from '../tx/builder/schema'; import { buildContractIdByContractTx, buildTx, BuiltTx, TxUnpacked, unpackTx, } from '../tx/builder'; @@ -81,7 +81,7 @@ export async function createGeneralizedAccount( onAccount, onCompiler, onNode, source, }); - const tx = await _buildTx(TX_TYPE.gaAttach, { + const tx = await _buildTx(Tag.GaAttachTx, { ...options, onNode, code: await contract.compile(), @@ -104,7 +104,7 @@ export async function createGeneralizedAccount( } interface CreateGeneralizedAccountOptions extends - BuildTxOptions, + BuildTxOptions, SendOptions { onAccount: AccountBase; onCompiler: Compiler; @@ -139,7 +139,7 @@ export async function createMetaTx( ): Promise> { const wrapInEmptySignedTx = ( tx: EncodedData<'tx'> | Uint8Array | TxUnpacked, - ): BuiltTx => buildTx({ encodedTx: tx, signatures: [] }, TX_TYPE.signed); + ): BuiltTx => buildTx({ encodedTx: tx, signatures: [] }, Tag.SignedTx); if (Object.keys(authData).length <= 0) throw new MissingParamError('authData is required'); @@ -156,8 +156,8 @@ export async function createMetaTx( return contract.calldata.encode(contract._name, authFnName, authData.args); })(); - const { abiVersion } = await getVmVersion(TX_TYPE.contractCall, { onNode }); - const wrappedTx = wrapInEmptySignedTx(unpackTx(rawTransaction)); + const { abiVersion } = await getVmVersion(Tag.ContractCallTx, { onNode }); + const wrappedTx = wrapInEmptySignedTx(unpackTx(rawTransaction)); const params = { ...options, tx: { @@ -172,8 +172,8 @@ export async function createMetaTx( vsn: 2, }; // @ts-expect-error createMetaTx needs to be integrated into tx builder - const { fee } = await prepareTxParams(TX_TYPE.gaMeta, { ...params, onNode }); - const { rlpEncoded: metaTxRlp } = buildTx({ ...params, fee }, TX_TYPE.gaMeta); + const { fee } = await prepareTxParams(Tag.GaMetaTx, { ...params, onNode }); + const { rlpEncoded: metaTxRlp } = buildTx({ ...params, fee }, Tag.GaMetaTx); return wrapInEmptySignedTx(metaTxRlp).tx; } diff --git a/src/deprecated/index.ts b/src/deprecated/index.ts index 45c5a7ed12..219def8018 100644 --- a/src/deprecated/index.ts +++ b/src/deprecated/index.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js'; import { buildTx } from '../tx/builder'; -import { TX_TYPE } from '../tx/builder/constants'; +import { Tag } from '../tx/builder/constants'; import { calculateMinFee as calculateMinFeeInternal } from '../tx/builder/field-types/fee'; import { TxParamsCommon } from '../tx/builder/schema'; import { AE_AMOUNT_FORMATS } from '../utils/amount-formatter'; @@ -44,7 +44,7 @@ export const createSalt = salt; * ``` */ export default function calculateMinFee( - txType: TX_TYPE, + txType: Tag, { params, vsn, denomination }: CalculateMinFeeOptions, ): BigNumber { return calculateMinFeeInternal( @@ -66,3 +66,57 @@ interface CalculateMinFeeOptions { * @hidden */ export const GAS_MAX = 1600000 - 21000; + +/** + * @deprecated use `Tag` + * @hidden + */ +export enum TX_TYPE { + account = 10, + signed = 11, + spend = 12, + oracleRegister = 22, + oracleQuery = 23, + oracleResponse = 24, + oracleExtend = 25, + nameClaim = 32, + namePreClaim = 33, + nameUpdate = 34, + nameRevoke = 35, + nameTransfer = 36, + contract = 40, + contractCallResult = 41, + contractCreate = 42, + contractCall = 43, + channelCreate = 50, + channelDeposit = 51, + channelWithdraw = 52, + channelCloseMutual = 53, + channelCloseSolo = 54, + channelSlash = 55, + channelSettle = 56, + channelOffChain = 57, + channel = 58, + channelSnapshotSolo = 59, + proofOfInclusion = 60, + stateTrees = 62, + merklePatriciaTree = 63, + merklePatriciaTreeValue = 64, + sophiaByteCode = 70, + gaAttach = 80, + gaMeta = 81, + payingFor = 82, + channelForceProgress = 521, + channelOffChainUpdateTransfer = 570, + channelOffChainUpdateDeposit = 571, + channelOffChainUpdateWithdrawal = 572, + channelOffChainCreateContract = 573, + channelOffChainCallContract = 574, + channelReconnect = 575, + contractsTree = 621, + contractCallsTree = 622, + channelsTree = 623, + nameserviceTree = 624, + oraclesTree = 625, + accountsTree = 626, +} diff --git a/src/oracle.ts b/src/oracle.ts index 371d4374ab..b2ff66829f 100644 --- a/src/oracle.ts +++ b/src/oracle.ts @@ -34,7 +34,7 @@ import { QUERY_FEE, QUERY_TTL, RESPONSE_TTL, - TX_TYPE, + Tag, } from './tx/builder/schema'; import { RequestTimedOutError } from './utils/errors'; import { EncodedData } from './utils/encoder'; @@ -169,7 +169,7 @@ export async function postQueryToOracle( options.queryFee ??= (await options.onNode.getOracleByPubkey(oracleId)).queryFee.toString(); const senderId = await options.onAccount.address(options); - const oracleQueryTx = await _buildTx(TX_TYPE.oracleQuery, { + const oracleQueryTx = await _buildTx(Tag.OracleQueryTx, { queryTtlType: QUERY_TTL.type, queryTtlValue: QUERY_TTL.value, responseTtlType: RESPONSE_TTL.type, @@ -179,7 +179,7 @@ export async function postQueryToOracle( senderId, query, }); - const { nonce } = unpackTx(oracleQueryTx, TX_TYPE.oracleQuery).tx; + const { nonce } = unpackTx(oracleQueryTx, Tag.OracleQueryTx).tx; const queryId = oracleQueryId(senderId, nonce, oracleId); return { ...await send(oracleQueryTx, options), @@ -189,7 +189,7 @@ export async function postQueryToOracle( type PostQueryToOracleOptionsType = Parameters[1] & Parameters[2] -& BuildTxOptions +& BuildTxOptions & { queryTtlType?: ORACLE_TTL_TYPES; queryTtlValue?: number; @@ -213,7 +213,7 @@ export async function extendOracleTtl( oracleId: EncodedData<'ok'>, options: ExtendOracleTtlOptions, ): Promise> & Awaited>> { - const oracleExtendTx = await _buildTx(TX_TYPE.oracleExtend, { + const oracleExtendTx = await _buildTx(Tag.OracleExtendTx, { oracleTtlType: ORACLE_TTL.type, oracleTtlValue: ORACLE_TTL.value, ...options, @@ -228,7 +228,7 @@ export async function extendOracleTtl( } type ExtendOracleTtlOptionsType = SendOptions & Parameters[1] -& BuildTxOptions +& BuildTxOptions & { oracleTtlType?: ORACLE_TTL_TYPES; oracleTtlValue?: number }; interface ExtendOracleTtlOptions extends ExtendOracleTtlOptionsType {} @@ -250,7 +250,7 @@ export async function respondToQuery( response: string, options: RespondToQueryOptions, ): Promise> & Awaited>> { - const oracleRespondTx = await _buildTx(TX_TYPE.oracleResponse, { + const oracleRespondTx = await _buildTx(Tag.OracleResponseTx, { responseTtlType: RESPONSE_TTL.type, responseTtlValue: RESPONSE_TTL.value, ...options, @@ -267,7 +267,7 @@ export async function respondToQuery( } type RespondToQueryOptionsType = SendOptions & Parameters[1] -& BuildTxOptions +& BuildTxOptions & { responseTtlType?: ORACLE_TTL_TYPES; responseTtlValue?: number }; interface RespondToQueryOptions extends RespondToQueryOptionsType {} @@ -337,7 +337,7 @@ export async function registerOracle( options: RegisterOracleOptions, ): Promise> & Awaited>> { const accountId = await options.onAccount.address(options); - const oracleRegisterTx = await _buildTx(TX_TYPE.oracleRegister, { + const oracleRegisterTx = await _buildTx(Tag.OracleRegisterTx, { queryFee: QUERY_FEE, oracleTtlValue: ORACLE_TTL.value, oracleTtlType: ORACLE_TTL.type, @@ -353,7 +353,7 @@ export async function registerOracle( } type RegisterOracleOptionsType = SendOptions & Parameters[1] -& BuildTxOptions +& BuildTxOptions & { queryFee?: number | string | BigNumber; oracleTtlType?: ORACLE_TTL_TYPES; diff --git a/src/spend.ts b/src/spend.ts index da4406d708..a0f939b264 100644 --- a/src/spend.ts +++ b/src/spend.ts @@ -22,7 +22,7 @@ import { _buildTx, BuildTxOptions } from './tx'; import { buildTxHash, unpackTx } from './tx/builder'; import { ArgumentError } from './utils/errors'; import { EncodedData } from './utils/encoder'; -import { TX_TYPE, AensName } from './tx/builder/schema'; +import { Tag, AensName } from './tx/builder/schema'; import AccountBase from './account/Base'; /** @@ -70,7 +70,7 @@ export async function spend( options: SpendOptions, ): ReturnType { return send( - await _buildTx(TX_TYPE.spend, { + await _buildTx(Tag.SpendTx, { ...options, senderId: await options.onAccount.address(options), recipientId: await resolveName(recipientIdOrName, 'account_pubkey', options), @@ -80,7 +80,7 @@ export async function spend( ); } -type SpendOptionsType = BuildTxOptions +type SpendOptionsType = BuildTxOptions & Parameters[2] & { onAccount: AccountBase } & SendOptions; interface SpendOptions extends SpendOptionsType {} @@ -108,22 +108,22 @@ export async function transferFunds( ); const desiredAmount = balance.times(fraction).integerValue(BigNumber.ROUND_HALF_UP); const { tx: { fee } } = unpackTx( - await _buildTx(TX_TYPE.spend, { + await _buildTx(Tag.SpendTx, { ...options, senderId, recipientId, amount: desiredAmount, }), - TX_TYPE.spend, + Tag.SpendTx, ); // Reducing of the amount may reduce transaction fee, so this is not completely accurate const amount = desiredAmount.plus(fee).gt(balance) ? balance.minus(fee) : desiredAmount; return send( - await _buildTx(TX_TYPE.spend, { + await _buildTx(Tag.SpendTx, { ...options, senderId, recipientId, amount, }), options, ); } -type TransferFundsOptionsType = BuildTxOptions +type TransferFundsOptionsType = BuildTxOptions & Parameters[2] & { onAccount: AccountBase } & SendOptions; interface TransferFundsOptions extends TransferFundsOptionsType {} @@ -140,7 +140,7 @@ export async function payForTransaction( ): ReturnType { return send( await _buildTx( - TX_TYPE.payingFor, + Tag.PayingForTx, { ...options, payerId: await options.onAccount.address(options), tx: transaction }, ), options, @@ -148,6 +148,6 @@ export async function payForTransaction( } interface PayForTransactionOptions extends - BuildTxOptions, SendOptions { + BuildTxOptions, SendOptions { onAccount: AccountBase; } diff --git a/src/tx/builder/constants.ts b/src/tx/builder/constants.ts index 94607cd303..3911145597 100644 --- a/src/tx/builder/constants.ts +++ b/src/tx/builder/constants.ts @@ -97,56 +97,70 @@ export const ID_TAG_PREFIX = mapObject( ); /** - * Enum with transaction types + * Enum with tag types * @category transaction builder * @see {@link https://github.com/aeternity/protocol/blob/0f6dee3d9d1e8e2469816798f5c7587a6c918f94/serializations.md#binary-serialization} + * @see {@link https://github.com/aeternity/aeserialization/blob/eb68fe331bd476910394966b7f5ede7a74d37e35/src/aeser_chain_objects.erl#L39-L97} */ -export enum TX_TYPE { - account = 10, - signed = 11, - spend = 12, - oracleRegister = 22, - oracleQuery = 23, - oracleResponse = 24, - oracleExtend = 25, - nameClaim = 32, - namePreClaim = 33, - nameUpdate = 34, - nameRevoke = 35, - nameTransfer = 36, - contract = 40, - contractCallResult = 41, - contractCreate = 42, - contractCall = 43, - channelCreate = 50, - channelDeposit = 51, - channelWithdraw = 52, - channelCloseMutual = 53, - channelCloseSolo = 54, - channelSlash = 55, - channelSettle = 56, - channelOffChain = 57, - channel = 58, - channelSnapshotSolo = 59, - proofOfInclusion = 60, - stateTrees = 62, - merklePatriciaTree = 63, - merklePatriciaTreeValue = 64, - sophiaByteCode = 70, - gaAttach = 80, - gaMeta = 81, - payingFor = 82, - channelForceProgress = 521, - channelOffChainUpdateTransfer = 570, - channelOffChainUpdateDeposit = 571, - channelOffChainUpdateWithdrawal = 572, - channelOffChainCreateContract = 573, - channelOffChainCallContract = 574, - channelReconnect = 575, - contractsTree = 621, - contractCallsTree = 622, - channelsTree = 623, - nameserviceTree = 624, - oraclesTree = 625, - accountsTree = 626, +// TODO: implement serialisation for commented-out tags +export enum Tag { + Account = 10, + SignedTx = 11, + SpendTx = 12, + // Oracle = 20, + // OracleQuery = 21, + OracleRegisterTx = 22, + OracleQueryTx = 23, + OracleResponseTx = 24, + OracleExtendTx = 25, + // Name = 30, + // NameCommitment = 31, + NameClaimTx = 32, + NamePreclaimTx = 33, + NameUpdateTx = 34, + NameRevokeTx = 35, + NameTransferTx = 36, + // NameAuction = 37, + Contract = 40, + ContractCall = 41, + ContractCreateTx = 42, + ContractCallTx = 43, + ChannelCreateTx = 50, + // ChannelSetDelegatesTx = 501, + ChannelDepositTx = 51, + ChannelWithdrawTx = 52, + ChannelForceProgressTx = 521, + ChannelCloseMutualTx = 53, + ChannelCloseSoloTx = 54, + ChannelSlashTx = 55, + ChannelSettleTx = 56, + ChannelOffChainTx = 57, + ChannelOffChainUpdateTransfer = 570, + ChannelOffChainUpdateDeposit = 571, + ChannelOffChainUpdateWithdraw = 572, + ChannelOffChainUpdateCreateContract = 573, + ChannelOffChainUpdateCallContract = 574, + // ChannelOffChainUpdateMeta = 576, + ChannelClientReconnectTx = 575, + Channel = 58, + ChannelSnapshotSoloTx = 59, + TreesPoi = 60, + // TreesDb = 61, + StateTrees = 62, + Mtree = 63, + MtreeValue = 64, + ContractsMtree = 621, + CallsMtree = 622, + ChannelsMtree = 623, + NameserviceMtree = 624, + OraclesMtree = 625, + AccountsMtree = 626, + CompilerSophia = 70, + GaAttachTx = 80, + GaMetaTx = 81, + PayingForTx = 82, + // KeyBlock = 100, + // MicroBlock = 101, + // LightMicroBlock = 102, + // Pof = 200, } diff --git a/src/tx/builder/field-types/fee.ts b/src/tx/builder/field-types/fee.ts index 13afccdff2..3495952a8e 100644 --- a/src/tx/builder/field-types/fee.ts +++ b/src/tx/builder/field-types/fee.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js'; import { IllegalArgumentError } from '../../../utils/errors'; -import { MIN_GAS_PRICE, TX_TYPE } from '../constants'; +import { MIN_GAS_PRICE, Tag } from '../constants'; import coinAmount from './coin-amount'; import { isKeyOfObject } from '../../../utils/other'; @@ -18,20 +18,20 @@ const KEY_BLOCK_INTERVAL = 3; * TX_FEE_BASE('channelForceProgress') => new BigNumber(30 * 15000) * ``` */ -const TX_FEE_BASE_GAS = (txType: TX_TYPE): BigNumber => { +const TX_FEE_BASE_GAS = (txType: Tag): BigNumber => { const feeFactors = { - [TX_TYPE.channelForceProgress]: 30, - [TX_TYPE.channelOffChain]: 0, - [TX_TYPE.channelOffChainCallContract]: 0, - [TX_TYPE.channelOffChainCreateContract]: 0, - [TX_TYPE.channelOffChainUpdateDeposit]: 0, - [TX_TYPE.channelOffChainUpdateWithdrawal]: 0, - [TX_TYPE.channelOffChainUpdateTransfer]: 0, - [TX_TYPE.contractCreate]: 5, - [TX_TYPE.contractCall]: 12, - [TX_TYPE.gaAttach]: 5, - [TX_TYPE.gaMeta]: 5, - [TX_TYPE.payingFor]: 1 / 5, + [Tag.ChannelForceProgressTx]: 30, + [Tag.ChannelOffChainTx]: 0, + [Tag.ChannelOffChainUpdateCallContract]: 0, + [Tag.ChannelOffChainUpdateCreateContract]: 0, + [Tag.ChannelOffChainUpdateDeposit]: 0, + [Tag.ChannelOffChainUpdateWithdraw]: 0, + [Tag.ChannelOffChainUpdateTransfer]: 0, + [Tag.ContractCreateTx]: 5, + [Tag.ContractCallTx]: 12, + [Tag.GaAttachTx]: 5, + [Tag.GaMetaTx]: 5, + [Tag.PayingForTx]: 1 / 5, } as const; const factor = feeFactors[txType as keyof typeof feeFactors] ?? 1; return new BigNumber(factor * BASE_GAS); @@ -53,34 +53,34 @@ const TX_FEE_BASE_GAS = (txType: TX_TYPE): BigNumber => { * ``` */ const TX_FEE_OTHER_GAS = ( - txType: TX_TYPE, + txType: Tag, txSize: number, { relativeTtl, innerTxSize }: { relativeTtl: number; innerTxSize: number }, ): BigNumber => { switch (txType) { - case TX_TYPE.oracleRegister: - case TX_TYPE.oracleExtend: - case TX_TYPE.oracleQuery: - case TX_TYPE.oracleResponse: + case Tag.OracleRegisterTx: + case Tag.OracleExtendTx: + case Tag.OracleQueryTx: + case Tag.OracleResponseTx: return new BigNumber(txSize) .times(GAS_PER_BYTE) .plus( Math.ceil((32000 * relativeTtl) / Math.floor((60 * 24 * 365) / KEY_BLOCK_INTERVAL)), ); - case TX_TYPE.gaMeta: - case TX_TYPE.payingFor: + case Tag.GaMetaTx: + case Tag.PayingForTx: return new BigNumber(txSize).minus(innerTxSize).times(GAS_PER_BYTE); default: return new BigNumber(txSize).times(GAS_PER_BYTE); } }; -function getOracleRelativeTtl(params: any, txType: TX_TYPE): number { +function getOracleRelativeTtl(params: any, txType: Tag): number { const ttlKeys = { - [TX_TYPE.oracleRegister]: 'oracleTtlValue', - [TX_TYPE.oracleExtend]: 'oracleTtlValue', - [TX_TYPE.oracleQuery]: 'queryTtlValue', - [TX_TYPE.oracleResponse]: 'responseTtlValue', + [Tag.OracleRegisterTx]: 'oracleTtlValue', + [Tag.OracleExtendTx]: 'oracleTtlValue', + [Tag.OracleQueryTx]: 'queryTtlValue', + [Tag.OracleResponseTx]: 'responseTtlValue', } as const; if (!isKeyOfObject(txType, ttlKeys)) return 1; @@ -90,13 +90,13 @@ function getOracleRelativeTtl(params: any, txType: TX_TYPE): number { /** * Calculate fee based on tx type and params */ -export function buildFee(txType: TX_TYPE, buildTx: any): BigNumber { +export function buildFee(txType: Tag, buildTx: any): BigNumber { const { rlpEncoded: { length }, txObject } = buildTx; return TX_FEE_BASE_GAS(txType) .plus(TX_FEE_OTHER_GAS(txType, length, { relativeTtl: getOracleRelativeTtl(txObject, txType), - innerTxSize: [TX_TYPE.gaMeta, TX_TYPE.payingFor].includes(txType) + innerTxSize: [Tag.GaMetaTx, Tag.PayingForTx].includes(txType) ? txObject.tx.tx.encodedTx.rlpEncoded.length : 0, })) @@ -110,7 +110,7 @@ export function buildFee(txType: TX_TYPE, buildTx: any): BigNumber { * @param rebuildTx - Callback to get built transaction with specific fee */ export function calculateMinFee( - txType: TX_TYPE, + txType: Tag, rebuildTx: (value: BigNumber) => any, ): BigNumber { let fee = new BigNumber(0); @@ -128,7 +128,7 @@ export default { serializeAettos( _value: string | undefined, { txType, rebuildTx, _computingMinFee }: - { txType: TX_TYPE; rebuildTx: (params: any) => any; _computingMinFee?: string }, + { txType: Tag; rebuildTx: (params: any) => any; _computingMinFee?: string }, ): string { if (_computingMinFee != null) return _computingMinFee; const minFee = calculateMinFee(txType, (fee) => rebuildTx({ _computingMinFee: fee })); diff --git a/src/tx/builder/field-types/gas-limit.ts b/src/tx/builder/field-types/gas-limit.ts index 90f72edc3e..4a773fd4d0 100644 --- a/src/tx/builder/field-types/gas-limit.ts +++ b/src/tx/builder/field-types/gas-limit.ts @@ -1,10 +1,10 @@ import { IllegalArgumentError } from '../../../utils/errors'; -import { MIN_GAS_PRICE, TX_TYPE } from '../constants'; +import { MIN_GAS_PRICE, Tag } from '../constants'; import shortUInt from './short-u-int'; import { buildFee } from './fee'; function calculateGasLimitMax( - txType: TX_TYPE, + txType: Tag, gasMax: number, rebuildTx: (value: number) => any, ): number { @@ -19,7 +19,7 @@ export default { { txType, rebuildTx, gasMax = 6e6, _computingGasLimit, }: { - txType: TX_TYPE; + txType: Tag; rebuildTx: (params: any) => any; gasMax: number; _computingGasLimit?: number; diff --git a/src/tx/builder/index.ts b/src/tx/builder/index.ts index f9678cd4eb..ae772bbd80 100644 --- a/src/tx/builder/index.ts +++ b/src/tx/builder/index.ts @@ -11,7 +11,7 @@ import { TxTypeSchemas, TxParamsCommon, TX_SCHEMA, - TX_TYPE, + Tag, TxSchema, } from './schema'; import { @@ -273,7 +273,7 @@ export interface BuiltTx { * @returns object.rlpEncoded rlp encoded transaction * @returns object.binary binary transaction */ -export function buildTx( +export function buildTx( _params: Omit & { VSN?: number }, type: TxType, { @@ -288,7 +288,7 @@ export function buildTx( const schemas = TX_SCHEMA[type]; vsn ??= Math.max(...Object.keys(schemas).map((a) => +a)); - if (!isKeyOfObject(vsn, schemas)) throw new SchemaNotFoundError('serialization', TX_TYPE[type], vsn); + if (!isKeyOfObject(vsn, schemas)) throw new SchemaNotFoundError('serialization', Tag[type], vsn); const schema = schemas[vsn] as unknown as TxField[]; @@ -330,7 +330,7 @@ export function buildTx( tx, rlpEncoded, binary, - txObject: unpackRawTx(binary, schema), + txObject: unpackRawTx(binary, schema), } as any; } @@ -338,7 +338,7 @@ export function buildTx( * @category transaction builder */ export interface TxUnpacked { - txType: TX_TYPE; + txType: Tag; tx: RawTxObject; rlpEncoded: Uint8Array; } @@ -351,7 +351,7 @@ export interface TxUnpacked { * @returns object.tx Object with transaction param's * @returns object.txType Transaction type */ -export function unpackTx( +export function unpackTx( encodedTx: EncodedData<'tx' | 'pi'>, txType?: TxType, ): TxUnpacked { @@ -359,7 +359,7 @@ export function unpackTx( const binary = rlpDecode(rlpEncoded); const objId = +readInt(binary[0] as Buffer); if (!isKeyOfObject(objId, TX_SCHEMA)) throw new DecodeError(`Unknown transaction tag: ${objId}`); - if (txType != null && txType !== objId) throw new DecodeError(`Expected transaction to have ${TX_TYPE[txType]} tag, got ${TX_TYPE[objId]} instead`); + if (txType != null && txType !== objId) throw new DecodeError(`Expected transaction to have ${Tag[txType]} tag, got ${Tag[objId]} instead`); const vsn = +readInt(binary[1] as Buffer); if (!isKeyOfObject(vsn, TX_SCHEMA[objId])) throw new SchemaNotFoundError('deserialization', `tag ${objId}`, vsn); const schema = TX_SCHEMA[objId][vsn]; @@ -390,8 +390,8 @@ export function buildTxHash(rawTx: EncodedData<'tx'> | Uint8Array): EncodedData< * @returns Contract public key */ export function buildContractIdByContractTx(contractTx: EncodedData<'tx'>): EncodedData<'ct'> { - const { txType, tx } = unpackTx(contractTx); - if (![TX_TYPE.contractCreate, TX_TYPE.gaAttach].includes(txType)) { + const { txType, tx } = unpackTx(contractTx); + if (![Tag.ContractCreateTx, Tag.GaAttachTx].includes(txType)) { throw new ArgumentError('contractCreateTx', 'a contractCreateTx or gaAttach', txType); } return buildContractId(tx.ownerId, +tx.nonce); diff --git a/src/tx/builder/schema.ts b/src/tx/builder/schema.ts index e98f296e1a..a05bf72ac4 100644 --- a/src/tx/builder/schema.ts +++ b/src/tx/builder/schema.ts @@ -5,7 +5,7 @@ // # https://github.com/aeternity/protocol/blob/master/serializations.md#binary-serialization import BigNumber from 'bignumber.js'; -import { TX_TYPE } from './constants'; +import { Tag } from './constants'; import { Field, uInt, shortUInt, coinAmount, name, nameId, nameFee, deposit, gasLimit, gasPrice, fee, } from './field-types'; @@ -75,14 +75,14 @@ export enum PROTOCOL_VERSIONS { // First abi/vm by default export const PROTOCOL_VM_ABI = { [PROTOCOL_VERSIONS.IRIS]: { - [TX_TYPE.contractCreate]: { + [Tag.ContractCreateTx]: { vmVersion: [VM_VERSIONS.FATE_2], abiVersion: [ABI_VERSIONS.FATE], }, // TODO: Ensure that AEVM (SOPHIA?) is still available here - [TX_TYPE.contractCall]: { + [Tag.ContractCallTx]: { vmVersion: [], abiVersion: [ABI_VERSIONS.FATE, ABI_VERSIONS.SOPHIA], }, - [TX_TYPE.oracleRegister]: { + [Tag.OracleRegisterTx]: { vmVersion: [], abiVersion: [ABI_VERSIONS.NO_ABI, ABI_VERSIONS.SOPHIA], }, }, @@ -197,7 +197,7 @@ const BASE_TX = [ ] as const; export const TX_SCHEMA = { - [TX_TYPE.account]: { + [Tag.Account]: { 1: [ ...BASE_TX, ['nonce', shortUInt], @@ -212,14 +212,14 @@ export const TX_SCHEMA = { ['gaAuthFun', FIELD_TYPES.binary, 'cb'], ], }, - [TX_TYPE.signed]: { + [Tag.SignedTx]: { 1: [ ...BASE_TX, ['signatures', FIELD_TYPES.signatures], ['encodedTx', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.spend]: { + [Tag.SpendTx]: { 1: [ ...BASE_TX, ['senderId', FIELD_TYPES.id, 'ak'], @@ -231,7 +231,7 @@ export const TX_SCHEMA = { ['payload', FIELD_TYPES.payload], ], }, - [TX_TYPE.namePreClaim]: { + [Tag.NamePreclaimTx]: { 1: [ ...BASE_TX, ['accountId', FIELD_TYPES.id, 'ak'], @@ -241,7 +241,7 @@ export const TX_SCHEMA = { ['ttl', shortUInt], ], }, - [TX_TYPE.nameClaim]: { + [Tag.NameClaimTx]: { 2: [ ...BASE_TX, ['accountId', FIELD_TYPES.id, 'ak'], @@ -253,7 +253,7 @@ export const TX_SCHEMA = { ['ttl', shortUInt], ], }, - [TX_TYPE.nameUpdate]: { + [Tag.NameUpdateTx]: { 1: [ ...BASE_TX, ['accountId', FIELD_TYPES.id, 'ak'], @@ -266,7 +266,7 @@ export const TX_SCHEMA = { ['ttl', shortUInt], ], }, - [TX_TYPE.nameTransfer]: { + [Tag.NameTransferTx]: { 1: [ ...BASE_TX, ['accountId', FIELD_TYPES.id, 'ak'], @@ -277,7 +277,7 @@ export const TX_SCHEMA = { ['ttl', shortUInt], ], }, - [TX_TYPE.nameRevoke]: { + [Tag.NameRevokeTx]: { 1: [ ...BASE_TX, ['accountId', FIELD_TYPES.id, 'ak'], @@ -287,7 +287,7 @@ export const TX_SCHEMA = { ['ttl', shortUInt], ], }, - [TX_TYPE.contract]: { + [Tag.Contract]: { 1: [ ...BASE_TX, ['owner', FIELD_TYPES.id, 'ak'], @@ -299,7 +299,7 @@ export const TX_SCHEMA = { ['deposit', deposit], ], }, - [TX_TYPE.contractCreate]: { + [Tag.ContractCreateTx]: { 1: [ ...BASE_TX, ['ownerId', FIELD_TYPES.id, 'ak'], @@ -315,7 +315,7 @@ export const TX_SCHEMA = { ['callData', FIELD_TYPES.binary, 'cb'], ], }, - [TX_TYPE.contractCall]: { + [Tag.ContractCallTx]: { 1: [ ...BASE_TX, ['callerId', FIELD_TYPES.id, 'ak'], @@ -330,7 +330,7 @@ export const TX_SCHEMA = { ['callData', FIELD_TYPES.binary, 'cb'], ], }, - [TX_TYPE.contractCallResult]: { + [Tag.ContractCall]: { 1: [ ...BASE_TX, ['callerId', FIELD_TYPES.id, 'ak'], @@ -346,7 +346,7 @@ export const TX_SCHEMA = { ['log', FIELD_TYPES.rawBinary], ], }, - [TX_TYPE.oracleRegister]: { + [Tag.OracleRegisterTx]: { 1: [ ...BASE_TX, ['accountId', FIELD_TYPES.id, 'ak'], @@ -361,7 +361,7 @@ export const TX_SCHEMA = { ['abiVersion', FIELD_TYPES.abiVersion], ], }, - [TX_TYPE.oracleExtend]: { + [Tag.OracleExtendTx]: { 1: [ ...BASE_TX, ['oracleId', FIELD_TYPES.id, ['ok', 'nm']], @@ -372,7 +372,7 @@ export const TX_SCHEMA = { ['ttl', shortUInt], ], }, - [TX_TYPE.oracleQuery]: { + [Tag.OracleQueryTx]: { 1: [ ...BASE_TX, ['senderId', FIELD_TYPES.id, 'ak'], @@ -388,7 +388,7 @@ export const TX_SCHEMA = { ['ttl', shortUInt], ], }, - [TX_TYPE.oracleResponse]: { + [Tag.OracleResponseTx]: { 1: [ ...BASE_TX, ['oracleId', FIELD_TYPES.id, 'ok'], @@ -401,7 +401,7 @@ export const TX_SCHEMA = { ['ttl', shortUInt], ], }, - [TX_TYPE.channelCreate]: { + [Tag.ChannelCreateTx]: { 2: [ ...BASE_TX, ['initiator', FIELD_TYPES.id, 'ak'], @@ -418,7 +418,7 @@ export const TX_SCHEMA = { ['nonce', shortUInt], ], }, - [TX_TYPE.channelCloseMutual]: { + [Tag.ChannelCloseMutualTx]: { 1: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -430,7 +430,7 @@ export const TX_SCHEMA = { ['nonce', shortUInt], ], }, - [TX_TYPE.channelCloseSolo]: { + [Tag.ChannelCloseSoloTx]: { 1: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -442,7 +442,7 @@ export const TX_SCHEMA = { ['nonce', shortUInt], ], }, - [TX_TYPE.channelSlash]: { + [Tag.ChannelSlashTx]: { 1: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -454,7 +454,7 @@ export const TX_SCHEMA = { ['nonce', shortUInt], ], }, - [TX_TYPE.channelDeposit]: { + [Tag.ChannelDepositTx]: { 1: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -467,7 +467,7 @@ export const TX_SCHEMA = { ['nonce', shortUInt], ], }, - [TX_TYPE.channelWithdraw]: { + [Tag.ChannelWithdrawTx]: { 1: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -480,7 +480,7 @@ export const TX_SCHEMA = { ['nonce', shortUInt], ], }, - [TX_TYPE.channelSettle]: { + [Tag.ChannelSettleTx]: { 1: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -492,7 +492,7 @@ export const TX_SCHEMA = { ['nonce', shortUInt], ], }, - [TX_TYPE.channelForceProgress]: { + [Tag.ChannelForceProgressTx]: { 1: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -507,7 +507,7 @@ export const TX_SCHEMA = { ['nonce', shortUInt], ], }, - [TX_TYPE.channelOffChain]: { + [Tag.ChannelOffChainTx]: { 2: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -515,7 +515,7 @@ export const TX_SCHEMA = { ['stateHash', FIELD_TYPES.binary, 'st'], ], }, - [TX_TYPE.channel]: { + [Tag.Channel]: { 3: [ ...BASE_TX, ['initiator', FIELD_TYPES.id, 'ak'], @@ -535,7 +535,7 @@ export const TX_SCHEMA = { ['responderAuth', FIELD_TYPES.binary, 'cb'], ], }, - [TX_TYPE.channelSnapshotSolo]: { + [Tag.ChannelSnapshotSoloTx]: { 1: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -546,7 +546,7 @@ export const TX_SCHEMA = { ['nonce', shortUInt], ], }, - [TX_TYPE.channelOffChainUpdateTransfer]: { + [Tag.ChannelOffChainUpdateTransfer]: { 1: [ ...BASE_TX, ['from', FIELD_TYPES.id, 'ak'], @@ -554,21 +554,21 @@ export const TX_SCHEMA = { ['amount', uInt], ], }, - [TX_TYPE.channelOffChainUpdateDeposit]: { + [Tag.ChannelOffChainUpdateDeposit]: { 1: [ ...BASE_TX, ['from', FIELD_TYPES.id, 'ak'], ['amount', uInt], ], }, - [TX_TYPE.channelOffChainUpdateWithdrawal]: { + [Tag.ChannelOffChainUpdateWithdraw]: { 1: [ ...BASE_TX, ['from', FIELD_TYPES.id, 'ak'], ['amount', uInt], ], }, - [TX_TYPE.channelOffChainCreateContract]: { + [Tag.ChannelOffChainUpdateCreateContract]: { 1: [ ...BASE_TX, ['owner', FIELD_TYPES.id, 'ak'], @@ -578,7 +578,7 @@ export const TX_SCHEMA = { ['callData', FIELD_TYPES.binary, 'cb'], ], }, - [TX_TYPE.channelOffChainCallContract]: { + [Tag.ChannelOffChainUpdateCallContract]: { 1: [ ...BASE_TX, ['caller', FIELD_TYPES.id, 'ak'], @@ -591,7 +591,7 @@ export const TX_SCHEMA = { ['gasLimit', gasLimit], ], }, - [TX_TYPE.channelReconnect]: { + [Tag.ChannelClientReconnectTx]: { 1: [ ...BASE_TX, ['channelId', FIELD_TYPES.id, 'ch'], @@ -600,7 +600,7 @@ export const TX_SCHEMA = { ['pubkey', FIELD_TYPES.id, 'ak'], ], }, - [TX_TYPE.proofOfInclusion]: { + [Tag.TreesPoi]: { 1: [ ...BASE_TX, ['accounts', FIELD_TYPES.mptrees], @@ -611,7 +611,7 @@ export const TX_SCHEMA = { ['oracles', FIELD_TYPES.mptrees], ], }, - [TX_TYPE.stateTrees]: { + [Tag.StateTrees]: { 1: [ ...BASE_TX, ['contracts', FIELD_TYPES.rlpBinary], @@ -622,56 +622,56 @@ export const TX_SCHEMA = { ['accounts', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.merklePatriciaTree]: { + [Tag.Mtree]: { 1: [ ...BASE_TX, ['values', FIELD_TYPES.rlpBinaries], ], }, - [TX_TYPE.merklePatriciaTreeValue]: { + [Tag.MtreeValue]: { 1: [ ...BASE_TX, ['key', FIELD_TYPES.hex], ['value', FIELD_TYPES.rawBinary], ], }, - [TX_TYPE.contractsTree]: { + [Tag.ContractsMtree]: { 1: [ ...BASE_TX, ['contracts', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.contractCallsTree]: { + [Tag.CallsMtree]: { 1: [ ...BASE_TX, ['calls', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.channelsTree]: { + [Tag.ChannelsMtree]: { 1: [ ...BASE_TX, ['channels', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.nameserviceTree]: { + [Tag.NameserviceMtree]: { 1: [ ...BASE_TX, ['mtree', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.oraclesTree]: { + [Tag.OraclesMtree]: { 1: [ ...BASE_TX, ['otree', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.accountsTree]: { + [Tag.AccountsMtree]: { 1: [ ...BASE_TX, ['accounts', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.gaAttach]: { + [Tag.GaAttachTx]: { 1: [ ...BASE_TX, ['ownerId', FIELD_TYPES.id, 'ak'], @@ -686,7 +686,7 @@ export const TX_SCHEMA = { ['callData', FIELD_TYPES.binary, 'cb'], ], }, - [TX_TYPE.gaMeta]: { + [Tag.GaMetaTx]: { 2: [ ...BASE_TX, ['gaId', FIELD_TYPES.id, 'ak'], @@ -698,7 +698,7 @@ export const TX_SCHEMA = { ['tx', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.payingFor]: { + [Tag.PayingForTx]: { 1: [ ...BASE_TX, ['payerId', FIELD_TYPES.id, 'ak'], @@ -707,7 +707,7 @@ export const TX_SCHEMA = { ['tx', FIELD_TYPES.rlpBinary], ], }, - [TX_TYPE.sophiaByteCode]: { + [Tag.CompilerSophia]: { 3: [ ...BASE_TX, ['sourceCodeHash', FIELD_TYPES.rawBinary], @@ -720,10 +720,10 @@ export const TX_SCHEMA = { } as const; export type TxTypeSchemas = { - [key in TX_TYPE]: BuildTxArgBySchema< + [key in Tag]: BuildTxArgBySchema< typeof TX_SCHEMA[key][keyof typeof TX_SCHEMA[key]] > }; -export type TxSchema = TxTypeSchemas[TX_TYPE]; +export type TxSchema = TxTypeSchemas[Tag]; export type TxParamsCommon = Partial>; diff --git a/src/tx/index.ts b/src/tx/index.ts index 6697d08112..a70be0fe9d 100644 --- a/src/tx/index.ts +++ b/src/tx/index.ts @@ -24,7 +24,7 @@ */ import BigNumber from 'bignumber.js'; import { - ABI_VERSIONS, CtVersion, PROTOCOL_VM_ABI, TX_TYPE, TX_TTL, TxParamsCommon, + ABI_VERSIONS, CtVersion, PROTOCOL_VM_ABI, Tag, TX_TTL, TxParamsCommon, } from './builder/schema'; import { ArgumentError, UnsupportedProtocolError, UnknownTxError, InvalidTxParamsError, @@ -37,7 +37,7 @@ import { AE_AMOUNT_FORMATS } from '../utils/amount-formatter'; type Int = number | string | BigNumber; -export type BuildTxOptions = +export type BuildTxOptions = Omit>[1], OmitFields>; /** @@ -48,13 +48,13 @@ export type BuildTxOptions = * @returns Object with vm/abi version */ export async function getVmVersion( - txType: TX_TYPE.contractCreate, ctVersion: Partial & { onNode: Node } + txType: Tag.ContractCreateTx, ctVersion: Partial & { onNode: Node } ): Promise; export async function getVmVersion( - txType: TX_TYPE, ctVersion: Partial> & { onNode: Node } + txType: Tag, ctVersion: Partial> & { onNode: Node } ): Promise>; export async function getVmVersion( - txType: TX_TYPE, + txType: Tag, { vmVersion, abiVersion, onNode }: Partial & { onNode: Node }, ): Promise> { const { consensusProtocolVersion } = await onNode.getNodeInfo(); @@ -79,7 +79,7 @@ export async function getVmVersion( * @returns Object with account nonce, absolute ttl and transaction fee */ export async function prepareTxParams( - txType: TX_TYPE, + txType: Tag, { senderId, nonce, @@ -112,7 +112,7 @@ interface PrepareTxParamsOptions extends Pick { /** * @category transaction builder */ -export async function _buildTx( +export async function _buildTx( txType: TxType, { denomination, absoluteTtl, ..._params }: Omit>[0], 'fee' | 'nonce' | 'ttl' | 'ctVersion' | 'abiVersion'> @@ -124,62 +124,62 @@ export async function _buildTx( denomination?: AE_AMOUNT_FORMATS; absoluteTtl?: boolean; } - & (TxType extends TX_TYPE.oracleExtend | TX_TYPE.oracleResponse ? { callerId: EncodedData<'ak'> } : {}) - & (TxType extends TX_TYPE.contractCreate | TX_TYPE.gaAttach ? { ctVersion?: CtVersion } : {}) - & (TxType extends TX_TYPE.contractCall | TX_TYPE.oracleRegister + & (TxType extends Tag.OracleExtendTx | Tag.OracleResponseTx ? { callerId: EncodedData<'ak'> } : {}) + & (TxType extends Tag.ContractCreateTx | Tag.GaAttachTx ? { ctVersion?: CtVersion } : {}) + & (TxType extends Tag.ContractCallTx | Tag.OracleRegisterTx ? { abiVersion?: ABI_VERSIONS } : {}), ): Promise> { // TODO: avoid this assertion const params = _params as unknown as TxParamsCommon & { onNode: Node }; let senderKey: keyof TxParamsCommon | ''; switch (txType) { - case TX_TYPE.spend: - case TX_TYPE.oracleQuery: + case Tag.SpendTx: + case Tag.OracleQueryTx: senderKey = 'senderId'; break; - case TX_TYPE.nameClaim: - case TX_TYPE.nameUpdate: - case TX_TYPE.nameRevoke: - case TX_TYPE.nameTransfer: - case TX_TYPE.namePreClaim: - case TX_TYPE.oracleRegister: + case Tag.NameClaimTx: + case Tag.NameUpdateTx: + case Tag.NameRevokeTx: + case Tag.NameTransferTx: + case Tag.NamePreclaimTx: + case Tag.OracleRegisterTx: senderKey = 'accountId'; break; - case TX_TYPE.contractCreate: - case TX_TYPE.gaAttach: + case Tag.ContractCreateTx: + case Tag.GaAttachTx: senderKey = 'ownerId'; break; - case TX_TYPE.contractCall: - case TX_TYPE.oracleExtend: - case TX_TYPE.oracleResponse: + case Tag.ContractCallTx: + case Tag.OracleExtendTx: + case Tag.OracleResponseTx: senderKey = 'callerId'; break; - case TX_TYPE.channelCloseSolo: - case TX_TYPE.channelSlash: - case TX_TYPE.channelSettle: - case TX_TYPE.channelSnapshotSolo: + case Tag.ChannelCloseSoloTx: + case Tag.ChannelSlashTx: + case Tag.ChannelSettleTx: + case Tag.ChannelSnapshotSoloTx: senderKey = 'fromId'; break; - case TX_TYPE.payingFor: + case Tag.PayingForTx: senderKey = 'payerId'; break; default: throw new ArgumentError('txType', 'valid transaction type', txType); } // TODO: move specific cases to field-types - if ([TX_TYPE.contractCreate, TX_TYPE.gaAttach].includes(txType)) { + if ([Tag.ContractCreateTx, Tag.GaAttachTx].includes(txType)) { params.ctVersion = await getVmVersion( - TX_TYPE.contractCreate, + Tag.ContractCreateTx, { ...params, ...params.ctVersion }, ); } - if (txType === TX_TYPE.contractCall) { - params.abiVersion = (await getVmVersion(TX_TYPE.contractCall, params)).abiVersion; + if (txType === Tag.ContractCallTx) { + params.abiVersion = (await getVmVersion(Tag.ContractCallTx, params)).abiVersion; } - if (txType === TX_TYPE.oracleRegister) { + if (txType === Tag.OracleRegisterTx) { params.abiVersion ??= ABI_VERSIONS.NO_ABI; } - if (txType === TX_TYPE.payingFor) { + if (txType === Tag.PayingForTx) { params.tx = unpackTx(params.tx); } const senderId = params[senderKey]; diff --git a/src/tx/validator.ts b/src/tx/validator.ts index c48cea76c7..4e3c1450f6 100644 --- a/src/tx/validator.ts +++ b/src/tx/validator.ts @@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'; import { verify, hash } from '../utils/crypto'; import { encode, decode } from './builder/helpers'; import { - PROTOCOL_VM_ABI, RawTxObject, TxSchema, TxParamsCommon, TX_TYPE, TxTypeSchemas, CtVersion, + PROTOCOL_VM_ABI, RawTxObject, TxSchema, TxParamsCommon, Tag, TxTypeSchemas, CtVersion, } from './builder/schema'; import { TxUnpacked, unpackTx } from './builder'; import { UnsupportedProtocolError } from '../utils/errors'; @@ -27,7 +27,7 @@ type Validator = ( encodedTx: TxUnpacked; signatures: Buffer[]; tx: TxUnpacked & { - tx: TxTypeSchemas[TX_TYPE.signed]; + tx: TxTypeSchemas[Tag.SignedTx]; }; nonce?: number; ttl?: number; @@ -41,9 +41,9 @@ type Validator = ( options: { account?: Account; nodeNetworkId: string; - parentTxTypes: TX_TYPE[]; + parentTxTypes: Tag[]; node: Node; - txType: TX_TYPE; + txType: Tag; height: number; consensusProtocolVersion: number; } @@ -75,11 +75,11 @@ const getSenderAddress = ( export default async function verifyTransaction( transaction: EncodedData<'tx' | 'pi'>, node: Node, - parentTxTypes: TX_TYPE[] = [], + parentTxTypes: Tag[] = [], ): Promise { - const { tx, txType } = unpackTx(transaction); + const { tx, txType } = unpackTx(transaction); const address = getSenderAddress(tx) - ?? (txType === TX_TYPE.signed ? getSenderAddress(tx.encodedTx.tx) : undefined); + ?? (txType === Tag.SignedTx ? getSenderAddress(tx.encodedTx.tx) : undefined); const [account, { height }, { consensusProtocolVersion, nodeNetworkId }] = await Promise.all([ address == null ? undefined @@ -108,7 +108,7 @@ validators.push( if (signatures.length !== 1) return []; // TODO: Support multisignature? const prefix = Buffer.from([ nodeNetworkId, - ...parentTxTypes.includes(TX_TYPE.payingFor) ? ['inner_tx'] : [], + ...parentTxTypes.includes(Tag.PayingForTx) ? ['inner_tx'] : [], ].join('-')); const txWithNetworkId = concatBuffers([prefix, encodedTx.rlpEncoded]); const txHashWithNetworkId = concatBuffers([prefix, hash(encodedTx.rlpEncoded)]); @@ -147,8 +147,8 @@ validators.push( if ((amount ?? fee ?? nameFee) === undefined) return []; fee ??= 0; const cost = new BigNumber(fee).plus(nameFee ?? 0).plus(amount ?? 0) - .plus(txType === TX_TYPE.payingFor ? (tx.tx.encodedTx.tx).fee : 0) - .minus(parentTxTypes.includes(TX_TYPE.payingFor) ? fee : 0); + .plus(txType === Tag.PayingForTx ? (tx.tx.encodedTx.tx).fee : 0) + .minus(parentTxTypes.includes(Tag.PayingForTx) ? fee : 0); if (cost.lte(account.balance.toString())) return []; return [{ message: `Account balance ${account.balance.toString()} is not enough to execute the transaction that costs ${cost.toFixed()}`, @@ -157,7 +157,7 @@ validators.push( }]; }, ({ nonce }, { account, parentTxTypes }) => { - if (nonce == null || account == null || parentTxTypes.includes(TX_TYPE.gaMeta)) return []; + if (nonce == null || account == null || parentTxTypes.includes(Tag.GaMetaTx)) return []; nonce = +nonce; const validNonce = account.nonce + 1; if (nonce === validNonce) return []; @@ -201,7 +201,7 @@ validators.push( return []; }, async ({ contractId }, { txType, node }) => { - if (TX_TYPE.contractCall !== txType) return []; + if (Tag.ContractCallTx !== txType) return []; contractId = contractId as EncodedData<'ct'>; try { const { active } = await node.getContract(contractId); diff --git a/test/integration/chain.ts b/test/integration/chain.ts index 23a9dc040c..20c24e5c76 100644 --- a/test/integration/chain.ts +++ b/test/integration/chain.ts @@ -20,7 +20,7 @@ import { spy } from 'sinon'; import http from 'http'; import { getSdk } from '.'; import { - generateKeyPair, AeSdk, TX_TYPE, UnexpectedTsError, + generateKeyPair, AeSdk, Tag, UnexpectedTsError, } from '../../src'; import { EncodedData } from '../../src/utils/encoder'; @@ -84,7 +84,7 @@ describe('Node Chain', () => { it('polls for transactions', async () => { const senderId = await aeSdk.address(); - const tx = await aeSdk.buildTx(TX_TYPE.spend, { + const tx = await aeSdk.buildTx(Tag.SpendTx, { amount: 1, senderId, recipientId: publicKey, diff --git a/test/integration/channel.ts b/test/integration/channel.ts index c82fae1f99..900261294b 100644 --- a/test/integration/channel.ts +++ b/test/integration/channel.ts @@ -22,7 +22,7 @@ import * as sinon from 'sinon'; import BigNumber from 'bignumber.js'; import { getSdk } from '.'; import { - generateKeyPair, unpackTx, buildTx, buildTxHash, encode, decode, TX_TYPE, + generateKeyPair, unpackTx, buildTx, buildTxHash, encode, decode, Tag, IllegalArgumentError, InsufficientBalanceError, ChannelConnectionError, encodeContractAddress, } from '../../src'; import { pause } from '../../src/utils/other'; @@ -147,9 +147,9 @@ describe('Channel', () => { }; const { txType: initiatorTxType, tx: initiatorTx } = unpackTx(initiatorSign.firstCall.args[1]); const { txType: responderTxType, tx: responderTx } = unpackTx(responderSign.firstCall.args[1]); - initiatorTxType.should.equal(TX_TYPE.channelCreate); + initiatorTxType.should.equal(Tag.ChannelCreateTx); initiatorTx.should.eql({ ...initiatorTx, ...expectedTxParams }); - responderTxType.should.equal(TX_TYPE.channelCreate); + responderTxType.should.equal(Tag.ChannelCreateTx); responderTx.should.eql({ ...responderTx, ...expectedTxParams }); }); @@ -216,7 +216,7 @@ describe('Channel', () => { }), ); const { txType } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); - txType.should.equal(TX_TYPE.channelOffChain); + txType.should.equal(Tag.ChannelOffChainTx); expect(sign.firstCall.args[1]).to.eql({ updates: [ @@ -272,7 +272,7 @@ describe('Channel', () => { }), ); const { txType } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); - txType.should.equal(TX_TYPE.channelOffChain); + txType.should.equal(Tag.ChannelOffChainTx); expect(sign.firstCall.args[1]).to.eql({ updates: [ { @@ -339,14 +339,14 @@ describe('Channel', () => { const initiatorPoi: EncodedData<'pi'> = await initiatorCh.poi(params); expect(initiatorPoi).to.be.equal(await responderCh.poi(params)); initiatorPoi.should.be.a('string'); - const unpackedInitiatorPoi = unpackTx(initiatorPoi, TX_TYPE.proofOfInclusion); + const unpackedInitiatorPoi = unpackTx(initiatorPoi, Tag.TreesPoi); // TODO: move to `unpackTx`/`MPTree` function getAccountBalance(address: EncodedData<'ak'>): string { const addressHex = decode(address).toString('hex'); const treeNode = unpackedInitiatorPoi.tx.accounts[0].get(addressHex); assertNotNull(treeNode); - const { balance, ...account } = unpackTx(encode(treeNode, 'tx'), TX_TYPE.account).tx; + const { balance, ...account } = unpackTx(encode(treeNode, 'tx'), Tag.Account).tx; expect(account).to.eql({ tag: 10, VSN: 1, nonce: 0 }); return balance.toString(); } @@ -420,7 +420,7 @@ describe('Channel', () => { }), ); const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); - txType.should.equal(TX_TYPE.channelWithdraw); + txType.should.equal(Tag.ChannelWithdrawTx); tx.should.eql({ ...tx, toId: await aeSdkInitiatior.address(), @@ -473,7 +473,7 @@ describe('Channel', () => { }), ); const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); - txType.should.equal(TX_TYPE.channelWithdraw); + txType.should.equal(Tag.ChannelWithdrawTx); tx.should.eql({ ...tx, toId: await aeSdkInitiatior.address(), @@ -550,7 +550,7 @@ describe('Channel', () => { }), ); const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); - txType.should.equal(TX_TYPE.channelDeposit); + txType.should.equal(Tag.ChannelDepositTx); tx.should.eql({ ...tx, fromId: await aeSdkInitiatior.address(), @@ -591,7 +591,7 @@ describe('Channel', () => { }), ); const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); - txType.should.equal(TX_TYPE.channelDeposit); + txType.should.equal(Tag.ChannelDepositTx); tx.should.eql({ ...tx, fromId: await aeSdkInitiatior.address(), @@ -636,7 +636,7 @@ describe('Channel', () => { sinon.assert.calledOnce(sign); sinon.assert.calledWithExactly(sign, sinon.match.string); const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); - txType.should.equal(TX_TYPE.channelCloseMutual); + txType.should.equal(Tag.ChannelCloseMutualTx); tx.should.eql({ ...tx, fromId: await aeSdkInitiatior.address(), @@ -714,21 +714,21 @@ describe('Channel', () => { const balances = await initiatorCh.balances([initiatorAddr, responderAddr]); const initiatorBalanceBeforeClose = await aeSdkInitiatior.getBalance(initiatorAddr); const responderBalanceBeforeClose = await aeSdkResponder.getBalance(responderAddr); - const closeSoloTx = await aeSdkInitiatior.buildTx(TX_TYPE.channelCloseSolo, { + const closeSoloTx = await aeSdkInitiatior.buildTx(Tag.ChannelCloseSoloTx, { channelId: await initiatorCh.id(), fromId: initiatorAddr, poi, payload: signedTx, }); - const closeSoloTxFee = unpackTx(closeSoloTx, TX_TYPE.channelCloseSolo).tx.fee; + const closeSoloTxFee = unpackTx(closeSoloTx, Tag.ChannelCloseSoloTx).tx.fee; await aeSdkInitiatior.sendTransaction(await aeSdkInitiatior.signTransaction(closeSoloTx)); - const settleTx = await aeSdkInitiatior.buildTx(TX_TYPE.channelSettle, { + const settleTx = await aeSdkInitiatior.buildTx(Tag.ChannelSettleTx, { channelId: await initiatorCh.id(), fromId: initiatorAddr, initiatorAmountFinal: balances[initiatorAddr], responderAmountFinal: balances[responderAddr], }); - const settleTxFee = unpackTx(settleTx, TX_TYPE.channelSettle).tx.fee; + const settleTxFee = unpackTx(settleTx, Tag.ChannelSettleTx).tx.fee; await aeSdkInitiatior.sendTransaction(await aeSdkInitiatior.signTransaction(settleTx)); const initiatorBalanceAfterClose = await aeSdkInitiatior.getBalance(initiatorAddr); const responderBalanceAfterClose = await aeSdkResponder.getBalance(responderAddr); @@ -777,29 +777,29 @@ describe('Channel', () => { accounts: [initiatorAddr, responderAddr], }); const recentBalances = await responderCh.balances([initiatorAddr, responderAddr]); - const closeSoloTx = await aeSdkInitiatior.buildTx(TX_TYPE.channelCloseSolo, { + const closeSoloTx = await aeSdkInitiatior.buildTx(Tag.ChannelCloseSoloTx, { channelId: initiatorCh.id(), fromId: initiatorAddr, poi: oldPoi, payload: oldUpdate.signedTx, }); - const closeSoloTxFee = unpackTx(closeSoloTx, TX_TYPE.channelCloseSolo).tx.fee; + const closeSoloTxFee = unpackTx(closeSoloTx, Tag.ChannelCloseSoloTx).tx.fee; await aeSdkInitiatior.sendTransaction(await aeSdkInitiatior.signTransaction(closeSoloTx)); - const slashTx = await aeSdkResponder.buildTx(TX_TYPE.channelSlash, { + const slashTx = await aeSdkResponder.buildTx(Tag.ChannelSlashTx, { channelId: responderCh.id(), fromId: responderAddr, poi: recentPoi, payload: recentUpdate.signedTx, }); - const slashTxFee = unpackTx(slashTx, TX_TYPE.channelSlash).tx.fee; + const slashTxFee = unpackTx(slashTx, Tag.ChannelSlashTx).tx.fee; await aeSdkResponder.sendTransaction(await aeSdkResponder.signTransaction(slashTx)); - const settleTx = await aeSdkResponder.buildTx(TX_TYPE.channelSettle, { + const settleTx = await aeSdkResponder.buildTx(Tag.ChannelSettleTx, { channelId: responderCh.id(), fromId: responderAddr, initiatorAmountFinal: recentBalances[initiatorAddr], responderAmountFinal: recentBalances[responderAddr], }); - const settleTxFee = unpackTx(settleTx, TX_TYPE.channelSettle).tx.fee; + const settleTxFee = unpackTx(settleTx, Tag.ChannelSettleTx).tx.fee; await aeSdkResponder.sendTransaction(await aeSdkResponder.signTransaction(settleTx)); const initiatorBalanceAfterClose = await aeSdkInitiatior.getBalance(initiatorAddr); const responderBalanceAfterClose = await aeSdkResponder.getBalance(responderAddr); @@ -1069,7 +1069,7 @@ describe('Channel', () => { }); // TODO fix this it.skip('can post snapshot solo transaction', async () => { - const snapshotSoloTx = await aeSdkInitiatior.buildTx(TX_TYPE.channelSnapshotSolo, { + const snapshotSoloTx = await aeSdkInitiatior.buildTx(Tag.ChannelSnapshotSoloTx, { channelId: initiatorCh.id(), fromId: await aeSdkInitiatior.address(), payload: (await initiatorCh.state()).signedTx, diff --git a/test/integration/ga.ts b/test/integration/ga.ts index 7c81544633..09bf4da4b2 100644 --- a/test/integration/ga.ts +++ b/test/integration/ga.ts @@ -19,7 +19,7 @@ import { describe, it, before } from 'mocha'; import { expect } from 'chai'; import { getSdk } from '.'; import { - AeSdk, TX_TYPE, genSalt, generateKeyPair, unpackTx, + AeSdk, Tag, genSalt, generateKeyPair, unpackTx, } from '../../src'; import { encode, EncodedData } from '../../src/utils/encoder'; import MemoryAccount from '../../src/account/Memory'; @@ -79,7 +79,7 @@ describe('Generalized Account', () => { it('buildAuthTxHash generates a proper hash', async () => { const { rawTx } = await aeSdk .spend(10000, publicKey, { authData: { source: authContractSource, args: [genSalt()] } }); - const spendTx = encode(unpackTx(rawTx, TX_TYPE.signed).tx.encodedTx.tx.tx.tx.encodedTx.rlpEncoded, 'tx'); + const spendTx = encode(unpackTx(rawTx, Tag.SignedTx).tx.encodedTx.tx.tx.tx.encodedTx.rlpEncoded, 'tx'); expect(await aeSdk.buildAuthTxHash(spendTx)).to.be .eql((await authContract.methods.getTxHash()).decodedResult); }); diff --git a/test/integration/paying-for.ts b/test/integration/paying-for.ts index d91090b3ef..cc0732690c 100644 --- a/test/integration/paying-for.ts +++ b/test/integration/paying-for.ts @@ -20,7 +20,7 @@ import { expect } from 'chai'; import BigNumber from 'bignumber.js'; import { getSdk } from '.'; import { - AeSdk, generateKeyPair, MemoryAccount, TX_TYPE, UnexpectedTsError, + AeSdk, generateKeyPair, MemoryAccount, Tag, UnexpectedTsError, } from '../../src'; import { ContractInstance } from '../../src/contract/aci'; import { EncodedData } from '../../src/utils/encoder'; @@ -36,7 +36,7 @@ describe('Paying for transaction of another account', () => { const sender = new MemoryAccount({ keypair: generateKeyPair() }); const receiver = new MemoryAccount({ keypair: generateKeyPair() }); await aeSdk.spend(1e4, await sender.address()); - const spendTx = await aeSdk.buildTx(TX_TYPE.spend, { + const spendTx = await aeSdk.buildTx(Tag.SpendTx, { senderId: await sender.address(), recipientId: await receiver.address(), amount: 1e4, diff --git a/test/integration/rpc.ts b/test/integration/rpc.ts index ac7cde48f8..af36c0b24e 100644 --- a/test/integration/rpc.ts +++ b/test/integration/rpc.ts @@ -28,7 +28,7 @@ import { RpcConnectionDenyError, RpcRejectedByUserError, SUBSCRIPTION_TYPES, - TX_TYPE, + Tag, WALLET_TYPE, unpackTx, decode, @@ -253,7 +253,7 @@ describe('Aepp<->Wallet', function aeppWallet() { throw new RpcRejectedByUserError(); }; const address = await aepp.address(); - const tx = await aepp.buildTx(TX_TYPE.spend, { + const tx = await aepp.buildTx(Tag.SpendTx, { senderId: address, recipientId: address, amount: 0, @@ -265,7 +265,7 @@ describe('Aepp<->Wallet', function aeppWallet() { it('Sign transaction: invalid account object in action', async () => { wallet.onSign = async () => ({ onAccount: {} }); - const tx = await aepp.buildTx(TX_TYPE.spend, { + const tx = await aepp.buildTx(Tag.SpendTx, { senderId: keypair.publicKey, recipientId: keypair.publicKey, amount: 0, @@ -278,7 +278,7 @@ describe('Aepp<->Wallet', function aeppWallet() { it('Sign transaction: wallet allow', async () => { wallet.onSign = async () => {}; const address = await aepp.address(); - const tx = await aepp.buildTx(TX_TYPE.spend, { + const tx = await aepp.buildTx(Tag.SpendTx, { senderId: address, recipientId: address, amount: 0, @@ -286,7 +286,7 @@ describe('Aepp<->Wallet', function aeppWallet() { }); const signedTx = await aepp.signTransaction(tx); - const unpackedTx = unpackTx(signedTx, TX_TYPE.signed); + const unpackedTx = unpackTx(signedTx, Tag.SignedTx); const { tx: { signatures: [signature], encodedTx: { rlpEncoded } } } = unpackedTx; const txWithNetwork = concatBuffers([Buffer.from(networkId), hash(rlpEncoded)]); const valid = verify(txWithNetwork, signature, decode(address)); @@ -306,14 +306,14 @@ describe('Aepp<->Wallet', function aeppWallet() { it('Sign by wallet and broadcast transaction by aepp ', async () => { const address = await aepp.address(); - const tx2 = await aepp.buildTx(TX_TYPE.spend, { + const tx2 = await aepp.buildTx(Tag.SpendTx, { senderId: address, recipientId: address, amount: 0, payload: 'zerospend2', }); wallet.onSign = async () => ({ tx: tx2 }); - const tx = await aepp.buildTx(TX_TYPE.spend, { + const tx = await aepp.buildTx(Tag.SpendTx, { senderId: address, recipientId: address, amount: 0, @@ -358,7 +358,7 @@ describe('Aepp<->Wallet', function aeppWallet() { it('Sign and broadcast invalid transaction', async () => { wallet.onSign = async () => {}; const address = await aepp.address(); - const tx = await aepp.buildTx(TX_TYPE.spend, { + const tx = await aepp.buildTx(Tag.SpendTx, { senderId: address, recipientId: address, amount: 0, @@ -519,14 +519,14 @@ describe('Aepp<->Wallet', function aeppWallet() { it('Sign by wallet and broadcast transaction by aepp ', async () => { const address = await aepp.address(); - const tx2 = await aepp.buildTx(TX_TYPE.spend, { + const tx2 = await aepp.buildTx(Tag.SpendTx, { senderId: address, recipientId: address, amount: 0, payload: 'zerospend2', }); wallet.onSign = async () => ({ tx: tx2 }); - const tx = await aepp.buildTx(TX_TYPE.spend, { + const tx = await aepp.buildTx(Tag.SpendTx, { senderId: address, recipientId: address, amount: 0, diff --git a/test/integration/transaction.ts b/test/integration/transaction.ts index 14e430aafd..be5c21e408 100644 --- a/test/integration/transaction.ts +++ b/test/integration/transaction.ts @@ -21,7 +21,7 @@ import { getSdk } from './index'; import { AeSdk, commitmentHash, oracleQueryId, decode, encode, - ORACLE_TTL_TYPES, TX_TYPE, AE_AMOUNT_FORMATS, + ORACLE_TTL_TYPES, Tag, AE_AMOUNT_FORMATS, } from '../../src'; import { EncodedData } from '../../src/utils/encoder'; @@ -73,10 +73,10 @@ describe('Transaction', () => { senderId, recipientId, nonce, payload: 'test', }; const spendAe = await aeSdk.buildTx( - TX_TYPE.spend, + Tag.SpendTx, { ...params, amount: 1, denomination: AE_AMOUNT_FORMATS.AE }, ); - const spendAettos = await aeSdk.buildTx(TX_TYPE.spend, { ...params, amount: 1e18 }); + const spendAettos = await aeSdk.buildTx(Tag.SpendTx, { ...params, amount: 1e18 }); spendAe.should.be.equal(spendAettos); }); @@ -84,41 +84,41 @@ describe('Transaction', () => { const transactions: Array<[string, string, () => Promise]> = [[ 'spend', 'tx_+F0MAaEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7ChAeEyuPHdt6BOY7f6lkeaVLvFJaUxp/G8DOSSEhkDBn+wiBvBbWdOyAAAhg9e1n8oAAABhHRlc3QLK3OW', - async () => aeSdk.buildTx(TX_TYPE.spend, { + async () => aeSdk.buildTx(Tag.SpendTx, { senderId, recipientId, nonce, payload: 'test', amount: 2, denomination: AE_AMOUNT_FORMATS.AE, }), ], [ 'name pre-claim', 'tx_+E8hAaEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7ABoQOvDVCf43V7alNbsUvTarXaCf7rjtWX36YLS4+JTa4jn4YPHaUyOAAAxRZ6Sg==', - async () => aeSdk.buildTx(TX_TYPE.namePreClaim, { + async () => aeSdk.buildTx(Tag.NamePreclaimTx, { accountId: senderId, nonce, commitmentId, }), ], [ 'name claim', 'tx_+FEgAqEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7ABkXRlc3QxMjN0ZXN0LmNoYWluhw7wBz3KlPuJNjXJrcXeoAAAhg8m9WHIAABl9JBX', - async () => aeSdk.buildTx(TX_TYPE.nameClaim, { + async () => aeSdk.buildTx(Tag.NameClaimTx, { accountId: senderId, nonce, name, nameSalt, nameFee, }), ], [ 'name update', 'tx_+IQiAaEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7ABoQL1zlEz+3+D5h4MF9POub3zp5zJ2fj6VUWGMNOhCyMYPAHy8Y5hY2NvdW50X3B1YmtleaEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7ABhhAUch6gAADR52s+', - async () => aeSdk.buildTx(TX_TYPE.nameUpdate, { + async () => aeSdk.buildTx(Tag.NameUpdateTx, { accountId: senderId, nonce, nameId, nameTtl, pointers, clientTtl, }), ], [ 'name revoke', 'tx_+E8jAaEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7ABoQL1zlEz+3+D5h4MF9POub3zp5zJ2fj6VUWGMNOhCyMYPIYPHaUyOAAA94BVgw==', - async () => aeSdk.buildTx(TX_TYPE.nameRevoke, { accountId: senderId, nonce, nameId }), + async () => aeSdk.buildTx(Tag.NameRevokeTx, { accountId: senderId, nonce, nameId }), ], [ 'name transfer', 'tx_+HEkAaEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7ABoQL1zlEz+3+D5h4MF9POub3zp5zJ2fj6VUWGMNOhCyMYPKEB4TK48d23oE5jt/qWR5pUu8UlpTGn8bwM5JISGQMGf7CGD7v4WsgAAL1d+NM=', - async () => aeSdk.buildTx(TX_TYPE.nameTransfer, { + async () => aeSdk.buildTx(Tag.NameTransferTx, { accountId: senderId, nonce, nameId, recipientId, }), ], [ 'contract create', 'tx_+LAqAaEB1c8IQA6YgiLybrSwLI+JB3RXRnIRpubZVe23B0nGozsBuGr4aEYDoKEijZbj/w2AeiWwAbldusME5pm3ZgPuomnZ3TbUbYgrwLg7nv5E1kQfADcANwAaDoI/AQM//oB4IJIANwEHBwEBAJgvAhFE1kQfEWluaXQRgHggkhlnZXRBcmeCLwCFNi4xLjAAgwcAA4ZHcyzkwAAAAACDTEtAhDuaygCHKxFE1kQfPxOlnVo=', - async () => aeSdk.buildTx(TX_TYPE.contractCreate, { + async () => aeSdk.buildTx(Tag.ContractCreateTx, { nonce, ownerId: address, code: await contract.compile(), @@ -129,7 +129,7 @@ describe('Transaction', () => { ], [ 'contract call', 'tx_+GMrAaEB1c8IQA6YgiLybrSwLI+JB3RXRnIRpubZVe23B0nGozsBoQU7e5ChtHAGM1Nh0MVEV74SbrYb1b5FQ3WBd7OBpwALyQOGpYvVcSgAAACDTEtAhDuaygCIKxGAeCCSGwT8YkzY', - async () => aeSdk.buildTx(TX_TYPE.contractCall, { + async () => aeSdk.buildTx(Tag.ContractCallTx, { nonce, callerId: address, contractId, @@ -140,25 +140,25 @@ describe('Transaction', () => { ], [ 'oracle register', 'tx_+FAWAaEB1c8IQA6YgiLybrSwLI+JB3RXRnIRpubZVe23B0nGozsBjXsnY2l0eSc6IHN0cn2Meyd0bXAnOiBudW19gnUwAIIB9IYPN7jqmAAAAGsRIcw=', - async () => aeSdk.buildTx(TX_TYPE.oracleRegister, { + async () => aeSdk.buildTx(Tag.OracleRegisterTx, { nonce, accountId: address, queryFormat, responseFormat, queryFee, ...oracleTtl, }), ], [ 'oracle extend', 'tx_8RkBoQTVzwhADpiCIvJutLAsj4kHdFdGchGm5tlV7bcHScajOwEAggH0hg6itfGYAADwE/X7', - async () => aeSdk.buildTx(TX_TYPE.oracleExtend, { + async () => aeSdk.buildTx(Tag.OracleExtendTx, { nonce, oracleId, callerId: address, ...oracleTtl, }), ], [ 'oracle post query', 'tx_+GkXAaEB1c8IQA6YgiLybrSwLI+JB3RXRnIRpubZVe23B0nGozsBoQTVzwhADpiCIvJutLAsj4kHdFdGchGm5tlV7bcHScajO5J7J2NpdHknOiAnQmVybGluJ32CdTAAZABkhg+bJBmGAAAtn7nr', - async () => aeSdk.buildTx(TX_TYPE.oracleQuery, { + async () => aeSdk.buildTx(Tag.OracleQueryTx, { nonce, oracleId, ...responseTtl, query, ...queryTtl, queryFee, senderId: address, }), ], [ 'oracle respond query', 'tx_+F0YAaEE1c8IQA6YgiLybrSwLI+JB3RXRnIRpubZVe23B0nGozsBoClgM30zCmbxGvUfzRbIZXGzOT8KCzYAUMRdnxbBX2Q9jHsndG1wJzogMTAxfQBkhg9jQvwmAADfRUs7', - async () => aeSdk.buildTx(TX_TYPE.oracleResponse, { + async () => aeSdk.buildTx(Tag.OracleResponseTx, { nonce, oracleId, callerId: address, diff --git a/test/integration/txVerification.ts b/test/integration/txVerification.ts index 2b1bdc6c3f..0d8d648820 100644 --- a/test/integration/txVerification.ts +++ b/test/integration/txVerification.ts @@ -2,7 +2,7 @@ import { before, describe, it } from 'mocha'; import { expect } from 'chai'; import { getSdk } from '.'; import { - AeSdk, Node, InvalidTxError, InvalidTxParamsError, generateKeyPair, TX_TYPE, + AeSdk, Node, InvalidTxError, InvalidTxParamsError, generateKeyPair, Tag, } from '../../src'; import MemoryAccount from '../../src/account/Memory'; import verifyTransaction from '../../src/tx/validator'; @@ -18,12 +18,12 @@ describe('Verify Transaction', () => { }); it('validates params in buildRawTx', async () => { - await expect(aeSdk.buildTx(TX_TYPE.spend, {} as any)).to.eventually.be + await expect(aeSdk.buildTx(Tag.SpendTx, {} as any)).to.eventually.be .rejectedWith(InvalidTxParamsError, 'Transaction field senderId is missed'); }); it('returns errors', async () => { - const spendTx = await aeSdk.buildTx(TX_TYPE.spend, { + const spendTx = await aeSdk.buildTx(Tag.SpendTx, { senderId: await aeSdk.address(), recipientId: await aeSdk.address(), amount: 1e50, @@ -42,7 +42,7 @@ describe('Verify Transaction', () => { }); it('returns NonceHigh error', async () => { - const spendTx = await aeSdk.buildTx(TX_TYPE.spend, { + const spendTx = await aeSdk.buildTx(Tag.SpendTx, { senderId: await aeSdk.address(), recipientId: await aeSdk.address(), amount: 100, @@ -53,7 +53,7 @@ describe('Verify Transaction', () => { }); it('verifies transactions before broadcasting', async () => { - const spendTx = await aeSdk.buildTx(TX_TYPE.spend, { + const spendTx = await aeSdk.buildTx(Tag.SpendTx, { senderId: await aeSdk.address(), recipientId: await aeSdk.address(), amount: 1, diff --git a/test/unit/tx.ts b/test/unit/tx.ts index dba74a0b5b..b4ee58a9dd 100644 --- a/test/unit/tx.ts +++ b/test/unit/tx.ts @@ -27,7 +27,7 @@ import { getDefaultPointerKey, commitmentHash, getMinimumNameFee, isNameValid, produceNameId, toBytes, buildTx, unpackTx, - NAME_BID_RANGES, TX_TYPE, + NAME_BID_RANGES, Tag, SchemaNotFoundError, } from '../../src'; @@ -109,7 +109,7 @@ describe('Tx', () => { }); it('Serialize tx: invalid tx VSN', () => { - expect(() => buildTx({} as any, TX_TYPE.spend, { vsn: 5 })) - .to.throw(SchemaNotFoundError, 'Transaction serialization not implemented for spend version 5'); + expect(() => buildTx({} as any, Tag.SpendTx, { vsn: 5 })) + .to.throw(SchemaNotFoundError, 'Transaction serialization not implemented for SpendTx version 5'); }); }); From a2c3167bdab66f6b96ab92cd0d6c0085df84fcb1 Mon Sep 17 00:00:00 2001 From: Denis Davidyuk Date: Mon, 25 Jul 2022 20:41:52 +0300 Subject: [PATCH 2/6] refactor(tx-builder): deprecate ID_TAG constants --- src/deprecated/index.ts | 37 +++++++++++++++++++++ src/index.ts | 1 + src/tx/builder/address.ts | 48 +++++++++++++++++++++++++++ src/tx/builder/constants.ts | 34 ------------------- src/tx/builder/field-types/name-id.ts | 5 ++- src/tx/builder/helpers.ts | 43 +++--------------------- src/tx/builder/index.ts | 3 +- 7 files changed, 93 insertions(+), 78 deletions(-) create mode 100644 src/tx/builder/address.ts diff --git a/src/deprecated/index.ts b/src/deprecated/index.ts index 219def8018..871d29df73 100644 --- a/src/deprecated/index.ts +++ b/src/deprecated/index.ts @@ -4,6 +4,8 @@ import { Tag } from '../tx/builder/constants'; import { calculateMinFee as calculateMinFeeInternal } from '../tx/builder/field-types/fee'; import { TxParamsCommon } from '../tx/builder/schema'; import { AE_AMOUNT_FORMATS } from '../utils/amount-formatter'; +import { mapObject } from '../utils/other'; +import { EncodingType } from '../utils/encoder'; export * from './methods'; @@ -120,3 +122,38 @@ export enum TX_TYPE { oraclesTree = 625, accountsTree = 626, } + +/** + * @deprecated use `readId`, `writeId` + * @hidden + */ +export const ID_TAG = { + account: 1, + name: 2, + commitment: 3, + oracle: 4, + contract: 5, + channel: 6, +} as const; + +/** + * @deprecated use `readId`, `writeId` + * @hidden + */ +export const PREFIX_ID_TAG = { + ak: ID_TAG.account, + nm: ID_TAG.name, + cm: ID_TAG.commitment, + ok: ID_TAG.oracle, + ct: ID_TAG.contract, + ch: ID_TAG.channel, +} as const; + +/** + * @deprecated use `readId`, `writeId` + * @hidden + */ +export const ID_TAG_PREFIX = mapObject( + PREFIX_ID_TAG, + ([key, value]: [EncodingType, number]) => [value, key], +); diff --git a/src/index.ts b/src/index.ts index 11df37cb83..ba2d956210 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,6 +23,7 @@ export * from './tx'; export * from './tx/builder'; export * from './tx/builder/helpers'; export * from './tx/builder/schema'; +export * from './tx/builder/address'; export * from './utils/amount-formatter'; export * from './utils/hd-wallet'; export * from './aens'; diff --git a/src/tx/builder/address.ts b/src/tx/builder/address.ts new file mode 100644 index 0000000000..7bd528b38c --- /dev/null +++ b/src/tx/builder/address.ts @@ -0,0 +1,48 @@ +import { ArgumentError, PrefixNotFoundError, TagNotFoundError } from '../../utils/errors'; +import { isKeyOfObject } from '../../utils/other'; +import { toBytes } from '../../utils/bytes'; +import { decode, encode, EncodedData } from '../../utils/encoder'; + +/** + * Map of prefix to ID tag constant + * @see {@link https://github.com/aeternity/protocol/blob/master/serializations.md#the-id-type} + * @see {@link https://github.com/aeternity/aeserialization/blob/eb68fe331bd476910394966b7f5ede7a74d37e35/src/aeser_id.erl#L97-L102} + * @see {@link https://github.com/aeternity/aeserialization/blob/eb68fe331bd476910394966b7f5ede7a74d37e35/src/aeser_api_encoder.erl#L163-L168} + */ +enum PrefixToIdTag { + ak = 1, + nm = 2, + cm = 3, + ok = 4, + ct = 5, + ch = 6, +} + +type AddressPrefixes = keyof typeof PrefixToIdTag; + +/** + * Utility function to create and _id type + * @category transaction builder + * @param hashId - Encoded hash + * @returns Buffer Buffer with ID tag and decoded HASh + */ +export function writeId(hashId: EncodedData): Buffer { + if (typeof hashId !== 'string') throw new ArgumentError('hashId', 'a string', hashId); + const prefix = hashId.slice(0, 2); + if (!isKeyOfObject(prefix, PrefixToIdTag)) throw new TagNotFoundError(prefix); + const idTag = PrefixToIdTag[prefix]; + return Buffer.from([...toBytes(idTag), ...decode(hashId)]); +} + +/** + * Utility function to read and _id type + * @category transaction builder + * @param buf - Data + * @returns Encoided hash string with prefix + */ +export function readId(buf: Buffer): EncodedData { + const idTag = Buffer.from(buf).readUIntBE(0, 1); + const prefix = PrefixToIdTag[idTag] as AddressPrefixes; + if (prefix == null) throw new PrefixNotFoundError(idTag); + return encode(buf.slice(1, buf.length), prefix); +} diff --git a/src/tx/builder/constants.ts b/src/tx/builder/constants.ts index 3911145597..7457b95deb 100644 --- a/src/tx/builder/constants.ts +++ b/src/tx/builder/constants.ts @@ -1,5 +1,4 @@ import BigNumber from 'bignumber.js'; -import { EncodingType } from '../../utils/encoder'; import { mapObject } from '../../utils/other'; // # AENS @@ -63,39 +62,6 @@ export const NAME_BID_RANGES = mapObject({ 1: 5702887, }, ([key, value]) => [key, new BigNumber(value).times(NAME_FEE_MULTIPLIER)]); -// # Tag constant for ids (type uint8) -// # see https://github.com/aeternity/protocol/blob/master/serializations.md#the-id-type -// # <> -const ID_TAG_ACCOUNT = 1; -const ID_TAG_NAME = 2; -const ID_TAG_COMMITMENT = 3; -const ID_TAG_ORACLE = 4; -const ID_TAG_CONTRACT = 5; -const ID_TAG_CHANNEL = 6; - -export const ID_TAG = { - account: ID_TAG_ACCOUNT, - name: ID_TAG_NAME, - commitment: ID_TAG_COMMITMENT, - oracle: ID_TAG_ORACLE, - contract: ID_TAG_CONTRACT, - channel: ID_TAG_CHANNEL, -}; - -export const PREFIX_ID_TAG = { - ak: ID_TAG.account, - nm: ID_TAG.name, - cm: ID_TAG.commitment, - ok: ID_TAG.oracle, - ct: ID_TAG.contract, - ch: ID_TAG.channel, -} as const; - -export const ID_TAG_PREFIX = mapObject( - PREFIX_ID_TAG, - ([key, value]: [EncodingType, number]) => [value, key], -); - /** * Enum with tag types * @category transaction builder diff --git a/src/tx/builder/field-types/name-id.ts b/src/tx/builder/field-types/name-id.ts index 74f794cfd0..6d0149562f 100644 --- a/src/tx/builder/field-types/name-id.ts +++ b/src/tx/builder/field-types/name-id.ts @@ -1,7 +1,6 @@ import { AensName } from '../constants'; -import { - writeId, readId, produceNameId, isNameValid, -} from '../helpers'; +import { produceNameId, isNameValid } from '../helpers'; +import { writeId, readId } from '../address'; import { EncodedData } from '../../../utils/encoder'; export default { diff --git a/src/tx/builder/helpers.ts b/src/tx/builder/helpers.ts index 57b52a4904..d099415e08 100644 --- a/src/tx/builder/helpers.ts +++ b/src/tx/builder/helpers.ts @@ -1,13 +1,9 @@ import BigNumber from 'bignumber.js'; import { hash, genSalt } from '../../utils/crypto'; -import { - encode, decode, EncodedData, EncodingType, -} from '../../utils/encoder'; +import { encode, decode, EncodedData } from '../../utils/encoder'; import { toBytes } from '../../utils/bytes'; import { concatBuffers } from '../../utils/other'; import { - ID_TAG_PREFIX, - PREFIX_ID_TAG, NAME_BID_RANGES, NAME_FEE_BID_INCREMENT, NAME_BID_TIMEOUT_BLOCKS, @@ -16,13 +12,9 @@ import { AensName, } from './constants'; import { ceil } from '../../utils/bignumber'; -import { - TagNotFoundError, - PrefixNotFoundError, - IllegalBidFeeError, - ArgumentError, -} from '../../utils/errors'; +import { IllegalBidFeeError } from '../../utils/errors'; import { NamePointer } from '../../apis/node'; +import { readId, writeId } from './address'; /** * JavaScript-based Transaction builder helper function's @@ -102,33 +94,6 @@ export function commitmentHash(name: AensName, salt: number = genSalt()): Encode return encode(hash(concatBuffers([Buffer.from(name.toLowerCase()), formatSalt(salt)])), 'cm'); } -/** - * Utility function to create and _id type - * @category transaction builder - * @param hashId - Encoded hash - * @returns Buffer Buffer with ID tag and decoded HASh - */ -export function writeId(hashId: string): Buffer { - if (typeof hashId !== 'string') throw new ArgumentError('hashId', 'a string', hashId); - const prefix = hashId.slice(0, 2) as keyof typeof PREFIX_ID_TAG; - const idTag = PREFIX_ID_TAG[prefix]; - if (idTag == null) throw new TagNotFoundError(prefix); - return Buffer.from([...toBytes(idTag), ...decode(hashId as EncodedData)]); -} - -/** - * Utility function to read and _id type - * @category transaction builder - * @param buf - Data - * @returns Encoided hash string with prefix - */ -export function readId(buf: Buffer): EncodedData { - const tag = Buffer.from(buf).readUIntBE(0, 1); - const prefix = ID_TAG_PREFIX[tag]; - if (prefix == null) throw new PrefixNotFoundError(tag); - return encode(buf.slice(1, buf.length), prefix); -} - /** * Utility function to convert int to bytes * @category transaction builder @@ -160,7 +125,7 @@ export function buildPointers(pointers: NamePointer[]): Buffer[][] { return pointers.map( (p) => [ toBytes(p.key), - writeId(p.id), + writeId(p.id as Parameters[0]), ], ); } diff --git a/src/tx/builder/index.ts b/src/tx/builder/index.ts index ae772bbd80..c6a8aae581 100644 --- a/src/tx/builder/index.ts +++ b/src/tx/builder/index.ts @@ -16,15 +16,14 @@ import { } from './schema'; import { readInt, - readId, readPointers, - writeId, writeInt, buildPointers, encode, decode, buildContractId, } from './helpers'; +import { readId, writeId } from './address'; import { toBytes } from '../../utils/bytes'; import MPTree, { MPTreeBinary } from '../../utils/mptree'; import { From ac8152f9d6854c379d902fb076a2d7b078d21673 Mon Sep 17 00:00:00 2001 From: Denis Davidyuk Date: Mon, 25 Jul 2022 20:28:16 +0300 Subject: [PATCH 3/6] refactor(tx-builder): copy byteSizeForType from aeserialization --- src/utils/encoder.ts | 23 +++++++++++++++++++---- test/unit/tx.ts | 4 ++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/utils/encoder.ts b/src/utils/encoder.ts index 59249c8400..2a0eca5d10 100644 --- a/src/utils/encoder.ts +++ b/src/utils/encoder.ts @@ -23,15 +23,30 @@ const base58Types = ['ak', 'bf', 'bs', 'bx', 'ch', 'cm', 'ct', 'kh', 'mh', 'nm', export type EncodingType = typeof base64Types[number] | typeof base58Types[number]; export type EncodedData = `${Type}_${string}`; -// TODO: add all types with a fixed length -const typesLength: { [name in EncodingType]?: number } = { - ak: 32, + +/** + * @see {@link https://github.com/aeternity/aeserialization/blob/eb68fe331bd476910394966b7f5ede7a74d37e35/src/aeser_api_encoder.erl#L261-L286} + */ +const byteSizeForType: { [name in EncodingType]?: number } = { + kh: 32, + mh: 32, + bf: 32, + bx: 32, + bs: 32, + ch: 32, ct: 32, + th: 32, ok: 32, + oq: 32, + ak: 32, + sg: 64, + cm: 32, + pp: 32, + st: 32, } as const; function ensureValidLength(data: Uint8Array, type: EncodingType): void { - const reqLen = typesLength[type]; + const reqLen = byteSizeForType[type]; if (reqLen == null || data.length === reqLen) return; throw new PayloadLengthError(`Payload should be ${reqLen} bytes, got ${data.length} instead`); } diff --git a/test/unit/tx.ts b/test/unit/tx.ts index b4ee58a9dd..583d9a1af5 100644 --- a/test/unit/tx.ts +++ b/test/unit/tx.ts @@ -82,7 +82,7 @@ describe('Tx', () => { describe('decode', () => { it('decodes base64check', () => expect(decode('ba_AQIq9Y55kw==')).to.be.eql(payload)); - it('decodes base58check', () => expect(decode('bf_3DZUwMat2')).to.be.eql(payload)); + it('decodes base58check', () => expect(decode('nm_3DZUwMat2')).to.be.eql(payload)); it('throws if invalid checksum', () => expect(() => decode('ak_23aaaaa')) .to.throw('Invalid checksum')); @@ -94,7 +94,7 @@ describe('Tx', () => { describe('encode', () => { it('encodes base64check', () => expect(encode(payload, 'ba')).to.be.equal('ba_AQIq9Y55kw==')); - it('encodes base58check', () => expect(encode(payload, 'bf')).to.be.equal('bf_3DZUwMat2')); + it('encodes base58check', () => expect(encode(payload, 'nm')).to.be.equal('nm_3DZUwMat2')); }); describe('getDefaultPointerKey', () => { From 9f8ffc42cbab8810928ef14a1f99142e16a009d1 Mon Sep 17 00:00:00 2001 From: Denis Davidyuk Date: Mon, 25 Jul 2022 20:45:27 +0300 Subject: [PATCH 4/6] refactor: remove unnecessary reexports --- src/AeSdkBase.ts | 3 ++- src/account/Base.ts | 2 +- src/account/Memory.ts | 3 +-- src/aens.ts | 2 +- src/chain.ts | 7 ++++--- src/channel/handlers.ts | 5 ++--- src/channel/index.ts | 2 +- src/contract/aci.ts | 5 ++--- src/contract/ga.ts | 3 ++- src/contract/methods.ts | 6 +++--- src/index.ts | 2 ++ src/oracle.ts | 6 +++--- src/spend.ts | 2 +- src/tx/builder/helpers.ts | 2 -- src/tx/builder/index.ts | 9 ++++----- src/tx/builder/schema.ts | 2 -- src/tx/index.ts | 3 ++- src/tx/validator.ts | 6 +++--- src/utils/crypto.ts | 2 -- 19 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/AeSdkBase.ts b/src/AeSdkBase.ts index 1af4659355..8363b8b55e 100644 --- a/src/AeSdkBase.ts +++ b/src/AeSdkBase.ts @@ -10,7 +10,8 @@ import { _buildTx } from './tx'; import { mapObject } from './utils/other'; import Node, { getNetworkId } from './Node'; import { AE_AMOUNT_FORMATS } from './utils/amount-formatter'; -import { AMOUNT, Tag } from './tx/builder/schema'; +import { AMOUNT } from './tx/builder/schema'; +import { Tag } from './tx/builder/constants'; import MemoryAccount, { Keypair } from './account/Memory'; import AccountBase, { isAccountBase } from './account/Base'; import { diff --git a/src/account/Base.ts b/src/account/Base.ts index 12b0234ac8..d2046af04d 100644 --- a/src/account/Base.ts +++ b/src/account/Base.ts @@ -17,7 +17,7 @@ import { messageToHash, verifyMessage as verifyMessageCrypto, hash } from '../utils/crypto'; import { buildTx } from '../tx/builder'; import { decode, EncodedData } from '../utils/encoder'; -import { Tag } from '../tx/builder/schema'; +import { Tag } from '../tx/builder/constants'; import { getNetworkId } from '../Node'; import { concatBuffers } from '../utils/other'; import type { createMetaTx } from '../contract/ga'; diff --git a/src/account/Memory.ts b/src/account/Memory.ts index d97856852a..48ab3d3f9f 100644 --- a/src/account/Memory.ts +++ b/src/account/Memory.ts @@ -17,9 +17,8 @@ import AccountBase from './Base'; import { sign, isValidKeypair } from '../utils/crypto'; import { isHex } from '../utils/string'; -import { decode } from '../tx/builder/helpers'; import { ArgumentError, InvalidKeypairError, MissingParamError } from '../utils/errors'; -import { EncodedData } from '../utils/encoder'; +import { decode, EncodedData } from '../utils/encoder'; import { createMetaTx } from '../contract/ga'; const secrets = new WeakMap(); diff --git a/src/aens.ts b/src/aens.ts index 323822392a..725b53f325 100644 --- a/src/aens.ts +++ b/src/aens.ts @@ -28,7 +28,7 @@ import { genSalt } from './utils/crypto'; import { commitmentHash, isAuctionName } from './tx/builder/helpers'; import { CLIENT_TTL, NAME_TTL, Tag, AensName, -} from './tx/builder/schema'; +} from './tx/builder/constants'; import { ArgumentError } from './utils/errors'; import { EncodedData } from './utils/encoder'; import { send, SendOptions } from './spend'; diff --git a/src/chain.ts b/src/chain.ts index adc596149a..8518169bfa 100644 --- a/src/chain.ts +++ b/src/chain.ts @@ -17,8 +17,9 @@ import { AE_AMOUNT_FORMATS, formatAmount } from './utils/amount-formatter'; import verifyTransaction, { ValidatorResult } from './tx/validator'; import { pause } from './utils/other'; -import { isNameValid, produceNameId, decode } from './tx/builder/helpers'; -import { DRY_RUN_ACCOUNT, AensName } from './tx/builder/schema'; +import { isNameValid, produceNameId } from './tx/builder/helpers'; +import { DRY_RUN_ACCOUNT } from './tx/builder/schema'; +import { AensName } from './tx/builder/constants'; import { AensPointerContextError, DryRunError, InvalidAensNameError, TransactionError, TxTimedOutError, TxNotInChainError, InternalError, @@ -28,7 +29,7 @@ import { Account as AccountNode, ByteCode, ContractObject, DryRunResult, DryRunResults, Generation, KeyBlock, MicroBlockHeader, NameEntry, SignedTx, } from './apis/node'; -import { EncodedData } from './utils/encoder'; +import { decode, EncodedData } from './utils/encoder'; import AccountBase from './account/Base'; /** diff --git a/src/channel/handlers.ts b/src/channel/handlers.ts index db260383aa..78a462393b 100644 --- a/src/channel/handlers.ts +++ b/src/channel/handlers.ts @@ -34,8 +34,7 @@ import { SignTx, } from './internal'; import { unpackTx, buildTx } from '../tx/builder'; -import { encode } from '../tx/builder/helpers'; -import { EncodedData } from '../utils/encoder'; +import { encode, EncodedData } from '../utils/encoder'; import { IllegalArgumentError, InsufficientBalanceError, @@ -43,7 +42,7 @@ import { UnexpectedChannelMessageError, } from '../utils/errors'; import type Channel from '.'; -import { Tag } from '../tx/builder/schema'; +import { Tag } from '../tx/builder/constants'; export async function appendSignature( tx: EncodedData<'tx'>, diff --git a/src/channel/index.ts b/src/channel/index.ts index 1180183d42..f9be3c2224 100644 --- a/src/channel/index.ts +++ b/src/channel/index.ts @@ -17,7 +17,7 @@ import BigNumber from 'bignumber.js'; import { snakeToPascal } from '../utils/string'; import { buildTx, unpackTx } from '../tx/builder'; -import { MIN_GAS_PRICE, Tag } from '../tx/builder/schema'; +import { MIN_GAS_PRICE, Tag } from '../tx/builder/constants'; import * as handlers from './handlers'; import { eventEmitters, diff --git a/src/contract/aci.ts b/src/contract/aci.ts index 8d0d6dd516..cc697dba76 100644 --- a/src/contract/aci.ts +++ b/src/contract/aci.ts @@ -16,9 +16,8 @@ */ // @ts-expect-error TODO remove import { Encoder as Calldata } from '@aeternity/aepp-calldata'; -import { - DRY_RUN_ACCOUNT, Tag, AMOUNT, AensName, -} from '../tx/builder/schema'; +import { DRY_RUN_ACCOUNT, AMOUNT } from '../tx/builder/schema'; +import { Tag, AensName } from '../tx/builder/constants'; import { buildContractIdByContractTx, unpackTx } from '../tx/builder'; import { _buildTx } from '../tx'; import { send } from '../spend'; diff --git a/src/contract/ga.ts b/src/contract/ga.ts index 9ed2b55741..d0ed0186a1 100644 --- a/src/contract/ga.ts +++ b/src/contract/ga.ts @@ -19,7 +19,8 @@ * Generalized Account module - routines to use generalized account */ -import { Tag, MAX_AUTH_FUN_GAS, TxSchema } from '../tx/builder/schema'; +import { MAX_AUTH_FUN_GAS, TxSchema } from '../tx/builder/schema'; +import { Tag } from '../tx/builder/constants'; import { buildContractIdByContractTx, buildTx, BuiltTx, TxUnpacked, unpackTx, } from '../tx/builder'; diff --git a/src/contract/methods.ts b/src/contract/methods.ts index 21dcd2115b..ce7430da87 100644 --- a/src/contract/methods.ts +++ b/src/contract/methods.ts @@ -22,10 +22,10 @@ * https://github.com/aeternity/protocol/tree/master/contracts and */ -import { AensName } from '../tx/builder/schema'; -import { decode, produceNameId } from '../tx/builder/helpers'; +import { AensName } from '../tx/builder/constants'; +import { produceNameId } from '../tx/builder/helpers'; import { concatBuffers } from '../utils/other'; -import { EncodedData, EncodingType } from '../utils/encoder'; +import { decode, EncodedData, EncodingType } from '../utils/encoder'; import AccountBase from '../account/Base'; import Node from '../Node'; diff --git a/src/index.ts b/src/index.ts index ba2d956210..855a2d3fe5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,10 +22,12 @@ export * from './utils/bytes'; export * from './tx'; export * from './tx/builder'; export * from './tx/builder/helpers'; +export * from './tx/builder/constants'; export * from './tx/builder/schema'; export * from './tx/builder/address'; export * from './utils/amount-formatter'; export * from './utils/hd-wallet'; +export { sha256hash, encode, decode } from './utils/encoder'; export * from './aens'; export * from './contract/methods'; export * from './oracle'; diff --git a/src/oracle.ts b/src/oracle.ts index b2ff66829f..35075c0d11 100644 --- a/src/oracle.ts +++ b/src/oracle.ts @@ -26,7 +26,7 @@ import BigNumber from 'bignumber.js'; import { send, SendOptions } from './spend'; import { mapObject, pause } from './utils/other'; -import { oracleQueryId, decode, encode } from './tx/builder/helpers'; +import { oracleQueryId } from './tx/builder/helpers'; import { unpackTx } from './tx/builder'; import { ORACLE_TTL, @@ -34,10 +34,10 @@ import { QUERY_FEE, QUERY_TTL, RESPONSE_TTL, - Tag, } from './tx/builder/schema'; +import { Tag } from './tx/builder/constants'; import { RequestTimedOutError } from './utils/errors'; -import { EncodedData } from './utils/encoder'; +import { decode, encode, EncodedData } from './utils/encoder'; import { _getPollInterval } from './chain'; import { _buildTx, BuildTxOptions } from './tx'; import Node from './Node'; diff --git a/src/spend.ts b/src/spend.ts index a0f939b264..1078eb1ab5 100644 --- a/src/spend.ts +++ b/src/spend.ts @@ -22,7 +22,7 @@ import { _buildTx, BuildTxOptions } from './tx'; import { buildTxHash, unpackTx } from './tx/builder'; import { ArgumentError } from './utils/errors'; import { EncodedData } from './utils/encoder'; -import { Tag, AensName } from './tx/builder/schema'; +import { Tag, AensName } from './tx/builder/constants'; import AccountBase from './account/Base'; /** diff --git a/src/tx/builder/helpers.ts b/src/tx/builder/helpers.ts index d099415e08..0e35e7bed3 100644 --- a/src/tx/builder/helpers.ts +++ b/src/tx/builder/helpers.ts @@ -20,8 +20,6 @@ import { readId, writeId } from './address'; * JavaScript-based Transaction builder helper function's */ -export { encode, decode }; - /** * Build a contract public key * @category contract diff --git a/src/tx/builder/index.ts b/src/tx/builder/index.ts index c6a8aae581..8c411d8168 100644 --- a/src/tx/builder/index.ts +++ b/src/tx/builder/index.ts @@ -1,9 +1,10 @@ import { decode as rlpDecode, encode as rlpEncode, NestedUint8Array } from 'rlp'; -import { EncodedData, EncodingType } from '../../utils/encoder'; +import { + encode, decode, EncodedData, EncodingType, +} from '../../utils/encoder'; import { AE_AMOUNT_FORMATS } from '../../utils/amount-formatter'; import { hash } from '../../utils/crypto'; import { Field } from './field-types'; - import { FIELD_TYPES, RawTxObject, @@ -11,16 +12,14 @@ import { TxTypeSchemas, TxParamsCommon, TX_SCHEMA, - Tag, TxSchema, } from './schema'; +import { Tag } from './constants'; import { readInt, readPointers, writeInt, buildPointers, - encode, - decode, buildContractId, } from './helpers'; import { readId, writeId } from './address'; diff --git a/src/tx/builder/schema.ts b/src/tx/builder/schema.ts index a05bf72ac4..9f6493bb49 100644 --- a/src/tx/builder/schema.ts +++ b/src/tx/builder/schema.ts @@ -13,8 +13,6 @@ import { EncodedData, EncodingType } from '../../utils/encoder'; import MPTree from '../../utils/mptree'; import { NamePointer } from '../../apis/node'; -export * from './constants'; - export enum ORACLE_TTL_TYPES { delta = 0, block = 1, diff --git a/src/tx/index.ts b/src/tx/index.ts index a70be0fe9d..ede99a75e8 100644 --- a/src/tx/index.ts +++ b/src/tx/index.ts @@ -24,8 +24,9 @@ */ import BigNumber from 'bignumber.js'; import { - ABI_VERSIONS, CtVersion, PROTOCOL_VM_ABI, Tag, TX_TTL, TxParamsCommon, + ABI_VERSIONS, CtVersion, PROTOCOL_VM_ABI, TX_TTL, TxParamsCommon, } from './builder/schema'; +import { Tag } from './builder/constants'; import { ArgumentError, UnsupportedProtocolError, UnknownTxError, InvalidTxParamsError, } from '../utils/errors'; diff --git a/src/tx/validator.ts b/src/tx/validator.ts index 4e3c1450f6..9ef0fca8fd 100644 --- a/src/tx/validator.ts +++ b/src/tx/validator.ts @@ -1,13 +1,13 @@ import BigNumber from 'bignumber.js'; import { verify, hash } from '../utils/crypto'; -import { encode, decode } from './builder/helpers'; import { - PROTOCOL_VM_ABI, RawTxObject, TxSchema, TxParamsCommon, Tag, TxTypeSchemas, CtVersion, + PROTOCOL_VM_ABI, RawTxObject, TxSchema, TxParamsCommon, TxTypeSchemas, CtVersion, } from './builder/schema'; +import { Tag } from './builder/constants'; import { TxUnpacked, unpackTx } from './builder'; import { UnsupportedProtocolError } from '../utils/errors'; import { concatBuffers, isKeyOfObject } from '../utils/other'; -import { EncodedData } from '../utils/encoder'; +import { encode, decode, EncodedData } from '../utils/encoder'; import Node from '../Node'; interface Account { diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts index 0d15c75365..f4b5cbe0d1 100644 --- a/src/utils/crypto.ts +++ b/src/utils/crypto.ts @@ -25,8 +25,6 @@ import { encode, decode, sha256hash, EncodedData, EncodingType, } from './encoder'; -export { sha256hash }; - const Ecb = aesjs.ModeOfOperation.ecb; /** From 5ec9e5667121ed7b85f1d171bcb76be8fcf5fd15 Mon Sep 17 00:00:00 2001 From: Denis Davidyuk Date: Tue, 26 Jul 2022 15:02:47 +0300 Subject: [PATCH 5/6] refactor(tx-builder): deprecate POINTER_KEY_BY_PREFIX --- src/deprecated/index.ts | 11 +++++++++++ src/tx/builder/constants.ts | 6 ------ src/tx/builder/helpers.ts | 16 +++++++++++----- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/deprecated/index.ts b/src/deprecated/index.ts index 871d29df73..3b2043ee45 100644 --- a/src/deprecated/index.ts +++ b/src/deprecated/index.ts @@ -157,3 +157,14 @@ export const ID_TAG_PREFIX = mapObject( PREFIX_ID_TAG, ([key, value]: [EncodingType, number]) => [value, key], ); + +/** + * @deprecated use `getDefaultPointerKey` + * @hidden + */ +export enum POINTER_KEY_BY_PREFIX { + ak = 'account_pubkey', + ok = 'oracle_pubkey', + ct = 'contract_pubkey', + ch = 'channel', +} diff --git a/src/tx/builder/constants.ts b/src/tx/builder/constants.ts index 7457b95deb..e142d29bd6 100644 --- a/src/tx/builder/constants.ts +++ b/src/tx/builder/constants.ts @@ -19,12 +19,6 @@ export const NAME_BID_TIMEOUT_BLOCKS = 480; // # ~1 day // # this is the max length for a domain that requires a base fee to be paid export const NAME_MAX_LENGTH_FEE = 31; export const NAME_BID_MAX_LENGTH = 12; // # this is the max length for a domain to be part of a bid -export enum POINTER_KEY_BY_PREFIX { - ak = 'account_pubkey', - ok = 'oracle_pubkey', - ct = 'contract_pubkey', - ch = 'channel', -} // # https://github.com/aeternity/aeternity/blob/72e440b8731422e335f879a31ecbbee7ac23a1cf/apps/aecore/src/aec_governance.erl#L290 // # https://github.com/aeternity/protocol/blob/master/AENS.md#protocol-fees-and-protection-times // # bid ranges: diff --git a/src/tx/builder/helpers.ts b/src/tx/builder/helpers.ts index 0e35e7bed3..cdafb3f8f4 100644 --- a/src/tx/builder/helpers.ts +++ b/src/tx/builder/helpers.ts @@ -8,7 +8,6 @@ import { NAME_FEE_BID_INCREMENT, NAME_BID_TIMEOUT_BLOCKS, NAME_MAX_LENGTH_FEE, - POINTER_KEY_BY_PREFIX, AensName, } from './constants'; import { ceil } from '../../utils/bignumber'; @@ -155,17 +154,24 @@ export function isNameValid(name: string): name is AensName { return name.endsWith(AENS_SUFFIX); } +enum PointerKeyByPrefix { + ak = 'account_pubkey', + ok = 'oracle_pubkey', + ct = 'contract_pubkey', + ch = 'channel', +} + /** * @category AENS * @param identifier - account/oracle/contract address, or channel * @returns default AENS pointer key */ export function getDefaultPointerKey( - identifier: EncodedData, -): POINTER_KEY_BY_PREFIX { + identifier: EncodedData, +): PointerKeyByPrefix { decode(identifier); - const prefix = identifier.substring(0, 2) as keyof typeof POINTER_KEY_BY_PREFIX; - return POINTER_KEY_BY_PREFIX[prefix]; + const prefix = identifier.substring(0, 2) as keyof typeof PointerKeyByPrefix; + return PointerKeyByPrefix[prefix]; } /** From 2b25e5dc944eb338fb1ad01d73649879ed4362cf Mon Sep 17 00:00:00 2001 From: Denis Davidyuk Date: Tue, 26 Jul 2022 15:06:34 +0300 Subject: [PATCH 6/6] refactor(tx-builder): make EncodedData based on enum --- src/AeSdk.ts | 20 +-- src/AeSdkAepp.ts | 10 +- src/AeSdkBase.ts | 18 ++- src/AeSdkWallet.ts | 6 +- src/Node.ts | 4 +- src/account/Base.ts | 8 +- src/account/Memory.ts | 13 +- src/account/Rpc.ts | 14 +- src/aens.ts | 14 +- src/aepp-wallet-communication/rpc/types.ts | 14 +- src/aepp-wallet-communication/schema.ts | 4 +- src/chain.ts | 58 +++---- src/channel/handlers.ts | 8 +- src/channel/index.ts | 65 ++++---- src/channel/internal.ts | 20 +-- src/contract/aci.ts | 44 +++--- src/contract/ga.ts | 26 ++-- src/contract/methods.ts | 10 +- src/deprecated/index.ts | 4 +- src/deprecated/methods.ts | 6 +- src/oracle.ts | 34 +++-- src/spend.ts | 25 +++- src/tx/builder/address.ts | 40 ++--- src/tx/builder/field-types/name-id.ts | 8 +- src/tx/builder/helpers.ts | 58 +++---- src/tx/builder/index.ts | 62 ++++---- src/tx/builder/schema.ts | 166 ++++++++++----------- src/tx/index.ts | 9 +- src/tx/validator.ts | 29 ++-- src/utils/crypto.ts | 28 ++-- src/utils/encoder-types.ts | 63 ++++++++ src/utils/encoder.ts | 89 +++++++---- src/utils/hd-wallet.ts | 4 +- src/utils/other.ts | 18 ++- test/integration/accounts.ts | 4 +- test/integration/chain.ts | 12 +- test/integration/channel.ts | 40 ++--- test/integration/contract-aci.ts | 12 +- test/integration/contract.ts | 25 ++-- test/integration/ga.ts | 9 +- test/integration/index.ts | 4 +- test/integration/oracle.ts | 4 +- test/integration/paying-for.ts | 4 +- test/integration/rpc.ts | 11 +- test/integration/transaction.ts | 8 +- test/unit/crypto.ts | 4 +- test/unit/hd-wallet.ts | 8 +- test/unit/memory-account.ts | 4 +- test/unit/tx.ts | 9 +- 49 files changed, 675 insertions(+), 482 deletions(-) create mode 100644 src/utils/encoder-types.ts diff --git a/src/AeSdk.ts b/src/AeSdk.ts index 7883b48a7b..e5061842a6 100644 --- a/src/AeSdk.ts +++ b/src/AeSdk.ts @@ -1,16 +1,18 @@ import AeSdkBase, { Account } from './AeSdkBase'; import AccountBase from './account/Base'; -import { decode, EncodedData } from './utils/encoder'; +import { decode, Encoded } from './utils/encoder'; import { UnavailableAccountError } from './utils/errors'; export default class AeSdk extends AeSdkBase { - accounts: { [key: EncodedData<'ak'>]: AccountBase } = {}; + accounts: { [key: Encoded.AccountAddress]: AccountBase } = {}; - selectedAddress?: EncodedData<'ak'>; + selectedAddress?: Encoded.AccountAddress; - _resolveAccount(account: Account | EncodedData<'ak'> = this.selectedAddress): AccountBase { + _resolveAccount( + account: Account | Encoded.AccountAddress = this.selectedAddress, + ): AccountBase { if (typeof account === 'string') { - const address = account as EncodedData<'ak'>; + const address = account as Encoded.AccountAddress; decode(address); if (this.accounts[address] == null) throw new UnavailableAccountError(account); account = this.accounts[address]; @@ -22,8 +24,8 @@ export default class AeSdk extends AeSdkBase { * Get accounts addresses * @example addresses() */ - addresses(): Array> { - return Object.keys(this.accounts) as Array>; + addresses(): Encoded.AccountAddress[] { + return Object.keys(this.accounts) as Encoded.AccountAddress[]; } /** @@ -44,7 +46,7 @@ export default class AeSdk extends AeSdkBase { * @param address - Address of account to remove * @example removeAccount(address) */ - removeAccount(address: EncodedData<'ak'>): void { + removeAccount(address: Encoded.AccountAddress): void { if (this.accounts[address] == null) { console.warn(`removeAccount: Account for ${address} not available`); return; @@ -58,7 +60,7 @@ export default class AeSdk extends AeSdkBase { * @param address - Address of account to select * @example selectAccount('ak_xxxxxxxx') */ - selectAccount(address: EncodedData<'ak'>): void { + selectAccount(address: Encoded.AccountAddress): void { decode(address); if (this.accounts[address] == null) throw new UnavailableAccountError(address); this.selectedAddress = address; diff --git a/src/AeSdkAepp.ts b/src/AeSdkAepp.ts index ca2064c714..b4857e745f 100644 --- a/src/AeSdkAepp.ts +++ b/src/AeSdkAepp.ts @@ -1,7 +1,7 @@ import AeSdkBase, { Account } from './AeSdkBase'; import AccountBase from './account/Base'; import AccountRpc from './account/Rpc'; -import { decode, EncodedData } from './utils/encoder'; +import { decode, Encoded } from './utils/encoder'; import { Accounts, RPC_VERSION, WalletInfo, Network, WalletApi, AeppApi, } from './aepp-wallet-communication/rpc/types'; @@ -63,7 +63,7 @@ export default class AeSdkAepp extends AeSdkBase { _resolveAccount(account: Account = this.addresses()[0]): AccountBase { if (typeof account === 'string') { - const address = account as EncodedData<'ak'>; + const address = account as Encoded.AccountAddress; decode(address); if (!this.addresses().includes(address)) throw new UnAuthorizedAccountError(address); account = new AccountRpc({ rpcClient: this.rpcClient, address }); @@ -72,12 +72,12 @@ export default class AeSdkAepp extends AeSdkBase { return super._resolveAccount(account); } - addresses(): Array> { + addresses(): Encoded.AccountAddress[] { if (this._accounts == null) return []; const current = Object.keys(this._accounts.current)[0]; return [ ...current != null ? [current] : [], ...Object.keys(this._accounts.connected), - ] as Array>; + ] as Encoded.AccountAddress[]; } /** @@ -142,7 +142,7 @@ export default class AeSdkAepp extends AeSdkBase { * Ask addresses from wallet * @returns Addresses from wallet */ - async askAddresses(): Promise>> { + async askAddresses(): Promise { this._ensureAccountAccess(); return this.rpcClient.request(METHODS.address, undefined); } diff --git a/src/AeSdkBase.ts b/src/AeSdkBase.ts index 8363b8b55e..ed5f936899 100644 --- a/src/AeSdkBase.ts +++ b/src/AeSdkBase.ts @@ -21,7 +21,7 @@ import { NotImplementedError, TypeError, } from './utils/errors'; -import { EncodedData } from './utils/encoder'; +import { Encoded } from './utils/encoder'; import Compiler from './contract/Compiler'; export type Account = Keypair | AccountBase | any; @@ -190,11 +190,11 @@ class AeSdkBase { } // eslint-disable-next-line class-methods-use-this - addresses(): Array> { + addresses(): Encoded.AccountAddress[] { return []; } - async address({ onAccount }: { onAccount?: Account } = {}): Promise> { + async address({ onAccount }: { onAccount?: Account } = {}): Promise { return this._resolveAccount(onAccount).address(); } @@ -206,9 +206,9 @@ class AeSdkBase { } async signTransaction( - tx: EncodedData<'tx'>, + tx: Encoded.Transaction, { onAccount, ...options }: { onAccount?: Account } & Parameters[1] = {}, - ): Promise> { + ): Promise { return this._resolveAccount(onAccount) .signTransaction(tx, { ...options, networkId: await this.getNetworkId(options) }); } @@ -263,7 +263,7 @@ class AeSdkBase { async buildTx( txType: TxType, options: Omit>[1], 'onNode'> & { onNode?: Node }, - ): Promise> { + ): Promise { // @ts-expect-error TODO: need to figure out what's wrong here return _buildTx(txType, { ...this._getOptions(), @@ -296,7 +296,11 @@ type MakeOptional = Args extends [infer Head, ...infer Tail] ? Tail extends [] ? Head extends object ? OptionalIfNotRequired<[Omit - & { onNode?: Node; onCompiler?: Compiler; onAccount?: AccountBase | EncodedData<'ak'> | Keypair }]> + & { + onNode?: Node; + onCompiler?: Compiler; + onAccount?: AccountBase | Encoded.AccountAddress | Keypair; + }]> : [Head] : [Head, ...MakeOptional] : never; diff --git a/src/AeSdkWallet.ts b/src/AeSdkWallet.ts index 334f942fd4..0117d7b38c 100644 --- a/src/AeSdkWallet.ts +++ b/src/AeSdkWallet.ts @@ -19,7 +19,7 @@ import { WalletApi, WalletInfo, } from './aepp-wallet-communication/rpc/types'; -import { EncodedData } from './utils/encoder'; +import { Encoded } from './utils/encoder'; import jsonBig from './utils/json-big'; type RpcClientWallet = RpcClient; @@ -34,7 +34,7 @@ type OnSubscription = ( type OnSign = ( clientId: string, params: Parameters[0], origin: string -) => Promise<{ tx?: EncodedData<'tx'>; onAccount?: Account } | undefined> | Promise; +) => Promise<{ tx?: Encoded.Transaction; onAccount?: Account } | undefined> | Promise; type OnDisconnect = ( clientId: string, params: Parameters[0] @@ -135,7 +135,7 @@ export default class AeSdkWallet extends AeSdk { .forEach((client) => client.notify(METHODS.updateAddress, this.getAccounts())); } - selectAccount(address: EncodedData<'ak'>): void { + selectAccount(address: Encoded.AccountAddress): void { super.selectAccount(address); this._pushAccountsToApps(); } diff --git a/src/Node.ts b/src/Node.ts index 3698d10965..b2f2d44400 100644 --- a/src/Node.ts +++ b/src/Node.ts @@ -6,7 +6,7 @@ import { } from './utils/autorest'; import { Node as NodeApi, NodeOptionalParams, ErrorModel } from './apis/node'; import { mapObject } from './utils/other'; -import { EncodedData } from './utils/encoder'; +import { Encoded } from './utils/encoder'; import { MissingParamError } from './utils/errors'; /** @@ -53,7 +53,7 @@ export type TransformNodeType = : Property extends NumberPropertyNames ? PreserveOptional : Property extends 'txHash' - ? PreserveOptional, Type[Property]> + ? PreserveOptional : TransformNodeType } : Type; diff --git a/src/account/Base.ts b/src/account/Base.ts index d2046af04d..57c5a64d2b 100644 --- a/src/account/Base.ts +++ b/src/account/Base.ts @@ -16,7 +16,7 @@ */ import { messageToHash, verifyMessage as verifyMessageCrypto, hash } from '../utils/crypto'; import { buildTx } from '../tx/builder'; -import { decode, EncodedData } from '../utils/encoder'; +import { decode, Encoded } from '../utils/encoder'; import { Tag } from '../tx/builder/constants'; import { getNetworkId } from '../Node'; import { concatBuffers } from '../utils/other'; @@ -53,14 +53,14 @@ export default abstract class AccountBase { * @returns Signed transaction */ async signTransaction( - tx: EncodedData<'tx'>, + tx: Encoded.Transaction, { innerTx, networkId, ...options }: { innerTx?: boolean; networkId?: string; authData?: Parameters[1]; authFun?: Parameters[2]; } & Omit[3]>, 'onAccount'> = {}, - ): Promise> { + ): Promise { const prefixes = [await this.getNetworkId({ networkId })]; if (innerTx === true) prefixes.push('inner_tx'); const rlpBinaryTx = decode(tx); @@ -120,5 +120,5 @@ export default abstract class AccountBase { * Obtain account address * @returns Public account address */ - abstract address(opt?: object): Promise>; + abstract address(opt?: object): Promise; } diff --git a/src/account/Memory.ts b/src/account/Memory.ts index 48ab3d3f9f..82456e227a 100644 --- a/src/account/Memory.ts +++ b/src/account/Memory.ts @@ -18,13 +18,13 @@ import AccountBase from './Base'; import { sign, isValidKeypair } from '../utils/crypto'; import { isHex } from '../utils/string'; import { ArgumentError, InvalidKeypairError, MissingParamError } from '../utils/errors'; -import { decode, EncodedData } from '../utils/encoder'; +import { decode, Encoded } from '../utils/encoder'; import { createMetaTx } from '../contract/ga'; const secrets = new WeakMap(); export interface Keypair { - publicKey: EncodedData<'ak'>; + publicKey: Encoded.AccountAddress; secretKey: string | Uint8Array; } @@ -42,7 +42,8 @@ export default class AccountMemory extends AccountBase { * @param options.gaId - Address of generalized account */ constructor( - { keypair, gaId, ...options }: { keypair?: Keypair; gaId?: EncodedData<'ak'> } & ConstructorParameters[0], + { keypair, gaId, ...options }: { keypair?: Keypair; gaId?: Encoded.AccountAddress } + & ConstructorParameters[0], ) { super(options); @@ -78,9 +79,9 @@ export default class AccountMemory extends AccountBase { } async signTransaction( - tx: EncodedData<'tx'>, + tx: Encoded.Transaction, options: Parameters[1] = {}, - ): Promise> { + ): Promise { if (!this.isGa || options.innerTx === true) return super.signTransaction(tx, options); const { authData, authFun, onCompiler, onNode, @@ -91,7 +92,7 @@ export default class AccountMemory extends AccountBase { return createMetaTx(tx, authData, authFun, { onCompiler, onNode, onAccount: this }); } - async address(): Promise> { + async address(): Promise { return secrets.get(this).publicKey; } } diff --git a/src/account/Rpc.ts b/src/account/Rpc.ts index 14d2983b93..7b4b75abcc 100644 --- a/src/account/Rpc.ts +++ b/src/account/Rpc.ts @@ -1,7 +1,7 @@ import AccountBase from './Base'; import { METHODS } from '../aepp-wallet-communication/schema'; import { NotImplementedError } from '../utils/errors'; -import { EncodedData } from '../utils/encoder'; +import { Encoded } from '../utils/encoder'; /** * Account provided by wallet @@ -13,11 +13,11 @@ import { EncodedData } from '../utils/encoder'; export default class AccountRpc extends AccountBase { _rpcClient: any; - _address: EncodedData<'ak'>; + _address: Encoded.AccountAddress; constructor( - { rpcClient, address, ...options }: - { rpcClient: any; address: EncodedData<'ak'> } & ConstructorParameters[0], + { rpcClient, address, ...options }: { rpcClient: any; address: Encoded.AccountAddress } + & ConstructorParameters[0], ) { super(options); this._rpcClient = rpcClient; @@ -29,7 +29,7 @@ export default class AccountRpc extends AccountBase { throw new NotImplementedError('RAW signing using wallet'); } - async address(): Promise> { + async address(): Promise { return this._address; } @@ -37,9 +37,9 @@ export default class AccountRpc extends AccountBase { * @returns Signed transaction */ async signTransaction( - tx: EncodedData<'tx'>, + tx: Encoded.Transaction, { innerTx, networkId }: Parameters[1] = {}, - ): Promise> { + ): Promise { if (innerTx != null) throw new NotImplementedError('innerTx option in AccountRpc'); const res = await this._rpcClient.request(METHODS.sign, { onAccount: this._address, diff --git a/src/aens.ts b/src/aens.ts index 725b53f325..1f1658b267 100644 --- a/src/aens.ts +++ b/src/aens.ts @@ -30,7 +30,7 @@ import { CLIENT_TTL, NAME_TTL, Tag, AensName, } from './tx/builder/constants'; import { ArgumentError } from './utils/errors'; -import { EncodedData } from './utils/encoder'; +import { Encoded } from './utils/encoder'; import { send, SendOptions } from './spend'; import { getName, getHeight } from './chain'; import { _buildTx, BuildTxOptions } from './tx'; @@ -166,7 +166,7 @@ interface AensUpdateOptions extends */ export async function aensTransfer( name: AensName, - account: EncodedData<'ak'>, + account: Encoded.AccountAddress, options: AensTransferOptions, ): ReturnType { const nameTransferTx = await _buildTx(Tag.NameTransferTx, { @@ -210,8 +210,8 @@ export async function aensQuery( & Parameters[2], ): Promise & { - id: EncodedData<'nm'>; - owner: EncodedData<'ak'>; + id: Encoded.Name; + owner: Encoded.AccountAddress; pointers: KeyPointers | NamePointer[]; ttl: number; update: ( @@ -221,7 +221,7 @@ export async function aensQuery( } ) => ReturnType & ReturnType; transfer: ( - account: EncodedData<'ak'>, + account: Encoded.AccountAddress, options?: Parameters[1] ) => ReturnType & ReturnType; revoke: (options?: Omit[1], 'onNode' | 'onCompiler' | 'onAccount'> & { @@ -237,8 +237,8 @@ export async function aensQuery( const nameEntry = await getName(name, opt); return Object.freeze({ ...nameEntry, - id: nameEntry.id as EncodedData<'nm'>, - owner: nameEntry.owner as EncodedData<'ak'>, + id: nameEntry.id as Encoded.Name, + owner: nameEntry.owner as Encoded.AccountAddress, async update(pointers, options) { return { ...await aensUpdate(name, pointers, { ...opt, ...options }), diff --git a/src/aepp-wallet-communication/rpc/types.ts b/src/aepp-wallet-communication/rpc/types.ts index fe7157616a..1a57a42c96 100644 --- a/src/aepp-wallet-communication/rpc/types.ts +++ b/src/aepp-wallet-communication/rpc/types.ts @@ -1,5 +1,5 @@ import { send } from '../../spend'; -import { EncodedData } from '../../utils/encoder'; +import { Encoded } from '../../utils/encoder'; import { METHODS, SUBSCRIPTION_TYPES, WALLET_TYPE } from '../schema'; export interface WalletInfo { @@ -11,8 +11,8 @@ export interface WalletInfo { } export interface Accounts { - connected: { [pub: EncodedData<'ak'>]: {} }; - current: { [pub: EncodedData<'ak'>]: {} }; + connected: { [pub: Encoded.AccountAddress]: {} }; + current: { [pub: Encoded.AccountAddress]: {} }; } export interface Node { @@ -40,21 +40,21 @@ export interface WalletApi { p: { type: SUBSCRIPTION_TYPES; value: 'connected' | 'current' } ) => Promise<{ subscription: Array<'connected' | 'current'>; address: Accounts }>; - [METHODS.address]: () => Promise>>; + [METHODS.address]: () => Promise; [METHODS.sign]: (( - p: { tx: EncodedData<'tx'>; onAccount: EncodedData<'ak'>; returnSigned: boolean } + p: { tx: Encoded.Transaction; onAccount: Encoded.AccountAddress; returnSigned: boolean } ) => Promise<{ /** * @deprecated this is not a hash at all, will be removed later at the same time * as dropping ability to broadcast transaction by wallet */ transactionHash?: Awaited>; - signedTransaction?: EncodedData<'tx'>; + signedTransaction?: Encoded.Transaction; }>); [METHODS.signMessage]: ( - p: { message: string; onAccount: EncodedData<'ak'> } + p: { message: string; onAccount: Encoded.AccountAddress } ) => Promise<{ signature: string }>; } diff --git a/src/aepp-wallet-communication/schema.ts b/src/aepp-wallet-communication/schema.ts index b3cac4d238..4c76c4bbe8 100644 --- a/src/aepp-wallet-communication/schema.ts +++ b/src/aepp-wallet-communication/schema.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line max-classes-per-file -import { EncodedData } from '../utils/encoder'; +import { Encoded } from '../utils/encoder'; import { BaseError, InternalError } from '../utils/errors'; /** @@ -185,7 +185,7 @@ export class RpcPermissionDenyError extends RpcError { code = 11; - constructor(address: EncodedData<'ak'>) { + constructor(address: Encoded.AccountAddress) { super(`You are not subscribed for account ${address}`); this.data = address; this.name = 'RpcPermissionDenyError'; diff --git a/src/chain.ts b/src/chain.ts index 8518169bfa..8697ffa995 100644 --- a/src/chain.ts +++ b/src/chain.ts @@ -29,7 +29,7 @@ import { Account as AccountNode, ByteCode, ContractObject, DryRunResult, DryRunResults, Generation, KeyBlock, MicroBlockHeader, NameEntry, SignedTx, } from './apis/node'; -import { decode, EncodedData } from './utils/encoder'; +import { decode, Encoded, Encoding } from './utils/encoder'; import AccountBase from './account/Base'; /** @@ -53,9 +53,13 @@ export function _getPollInterval( export class InvalidTxError extends TransactionError { validation: ValidatorResult[]; - transaction: EncodedData<'tx'>; + transaction: Encoded.Transaction; - constructor(message: string, validation: ValidatorResult[], transaction: EncodedData<'tx'>) { + constructor( + message: string, + validation: ValidatorResult[], + transaction: Encoded.Transaction, + ) { super(message); this.name = 'InvalidTxError'; this.validation = validation; @@ -83,7 +87,7 @@ export async function getHeight({ onNode }: { onNode: Node }): Promise { * @returns The transaction as it was mined */ export async function poll( - th: EncodedData<'th'>, + th: Encoded.TxHash, { blocks = 10, interval, onNode, ...options }: @@ -132,7 +136,7 @@ export async function awaitHeight( * @returns Current Height */ export async function waitForTxConfirm( - txHash: EncodedData<'th'>, + txHash: Encoded.TxHash, { confirm = 3, onNode, ...options }: { confirm?: number; onNode: Node } & Parameters[1], ): Promise { @@ -162,7 +166,7 @@ export async function waitForTxConfirm( * @returns Transaction details */ export async function sendTransaction( - tx: EncodedData<'tx'>, + tx: Encoded.Transaction, { onNode, onAccount, verify = true, waitMined = true, confirm, ...options }: @@ -193,7 +197,7 @@ export async function sendTransaction( const pollResult = await poll(txHash, { onNode, ...options }); const txData = { ...pollResult, - hash: pollResult.hash as EncodedData<'th'>, + hash: pollResult.hash as Encoded.TxHash, rawTx: tx, }; // wait for transaction confirmation @@ -224,8 +228,8 @@ type SendTransactionOptionsType = { } & Parameters[1] & Omit[1], 'confirm'>; interface SendTransactionOptions extends SendTransactionOptionsType {} interface SendTransactionReturnType extends Partial> { - hash: EncodedData<'th'>; - rawTx: EncodedData<'tx'>; + hash: Encoded.TxHash; + rawTx: Encoded.Transaction; confirmationHeight?: number; } @@ -239,9 +243,9 @@ interface SendTransactionReturnType extends Partial> * @param options.onNode - Node to use */ export async function getAccount( - address: EncodedData<'ak' | 'ct'>, + address: Encoded.AccountAddress | Encoded.ContractAddress, { height, hash, onNode }: - { height?: number; hash?: EncodedData<'kh' | 'mh'>; onNode: Node }, + { height?: number; hash?: Encoded.KeyBlockHash | Encoded.MicroBlockHash; onNode: Node }, ): Promise> { if (height != null) return onNode.getAccountByPubkeyAndHeight(address, height); if (hash != null) return onNode.getAccountByPubkeyAndHash(address, hash); @@ -259,7 +263,7 @@ export async function getAccount( * @param options.hash - The block hash on which to obtain the balance for (default: top of chain) */ export async function getBalance( - address: EncodedData<'ak' | 'ct'>, + address: Encoded.AccountAddress | Encoded.ContractAddress, { format = AE_AMOUNT_FORMATS.AETTOS, ...options }: { format?: AE_AMOUNT_FORMATS } & Parameters[1], ): Promise { @@ -290,7 +294,7 @@ export async function getCurrentGeneration( * @returns Generation */ export async function getGeneration( - hashOrHeight: EncodedData<'kh'> | number, + hashOrHeight: Encoded.KeyBlockHash | number, { onNode }: { onNode: Node }, ): Promise> { if (typeof hashOrHeight === 'number') return onNode.getGenerationByHeight(hashOrHeight); @@ -306,7 +310,7 @@ export async function getGeneration( * @returns Transactions */ export async function getMicroBlockTransactions( - hash: EncodedData<'mh'>, + hash: Encoded.MicroBlockHash, { onNode }: { onNode: Node }, ): Promise> { return (await onNode.getMicroBlockTransactionsByHash(hash)).transactions; @@ -321,7 +325,7 @@ export async function getMicroBlockTransactions( * @returns Key Block */ export async function getKeyBlock( - hashOrHeight: EncodedData<'kh'> | number, + hashOrHeight: Encoded.KeyBlockHash | number, { onNode }: { onNode: Node }, ): Promise> { if (typeof hashOrHeight === 'number') return onNode.getKeyBlockByHeight(hashOrHeight); @@ -337,15 +341,15 @@ export async function getKeyBlock( * @returns Micro block header */ export async function getMicroBlockHeader( - hash: EncodedData<'mh'>, + hash: Encoded.MicroBlockHash, { onNode }: { onNode: Node }, ): Promise> { return onNode.getMicroBlockHeaderByHash(hash); } interface TxDryRunArguments { - tx: EncodedData<'tx'>; - accountAddress: EncodedData<'ak'>; + tx: Encoded.Transaction; + accountAddress: Encoded.AccountAddress; top?: number; txEvents?: any; resolve: Function; @@ -394,8 +398,8 @@ async function txDryRunHandler(key: string, onNode: Node): Promise { * @param options.onNode - Node to use */ export async function txDryRun( - tx: EncodedData<'tx'>, - accountAddress: EncodedData<'ak'>, + tx: Encoded.Transaction, + accountAddress: Encoded.AccountAddress, { top, txEvents, combine, onNode, }: @@ -426,7 +430,7 @@ export async function txDryRun( * @param options.onNode - Node to use */ export async function getContractByteCode( - contractId: EncodedData<'ct'>, + contractId: Encoded.ContractAddress, { onNode }: { onNode: Node }, ): Promise> { return onNode.getContractCode(contractId); @@ -440,7 +444,7 @@ export async function getContractByteCode( * @param options.onNode - Node to use */ export async function getContract( - contractId: EncodedData<'ct'>, + contractId: Encoded.ContractAddress, { onNode }: { onNode: Node }, ): Promise> { return onNode.getContract(contractId); @@ -472,18 +476,20 @@ export async function getName( * @param options.onNode - Node to use * @returns Address or AENS name hash */ -export async function resolveName ( - nameOrId: AensName | EncodedData, +export async function resolveName < + Type extends Encoding.AccountAddress | Encoding.ContractAddress, +>( + nameOrId: AensName | Encoded.Generic, key: string, { verify = true, resolveByNode = false, onNode }: { verify?: boolean; resolveByNode?: boolean; onNode: Node }, -): Promise> { +): Promise> { if (isNameValid(nameOrId)) { if (verify || resolveByNode) { const name = await onNode.getNameEntryByName(nameOrId); const pointer = name.pointers.find((p) => p.key === key); if (pointer == null) throw new AensPointerContextError(nameOrId, key); - if (resolveByNode) return pointer.id as EncodedData; + if (resolveByNode) return pointer.id as Encoded.Generic; } return produceNameId(nameOrId); } diff --git a/src/channel/handlers.ts b/src/channel/handlers.ts index 78a462393b..cb137675f3 100644 --- a/src/channel/handlers.ts +++ b/src/channel/handlers.ts @@ -34,7 +34,7 @@ import { SignTx, } from './internal'; import { unpackTx, buildTx } from '../tx/builder'; -import { encode, EncodedData } from '../utils/encoder'; +import { encode, Encoded, Encoding } from '../utils/encoder'; import { IllegalArgumentError, InsufficientBalanceError, @@ -45,11 +45,11 @@ import type Channel from '.'; import { Tag } from '../tx/builder/constants'; export async function appendSignature( - tx: EncodedData<'tx'>, + tx: Encoded.Transaction, signFn: SignTx, -): Promise | number | null> { +): Promise { const { signatures, encodedTx } = unpackTx(tx, Tag.SignedTx).tx; - const result = await signFn(encode(encodedTx.rlpEncoded, 'tx')); + const result = await signFn(encode(encodedTx.rlpEncoded, Encoding.Transaction)); if (typeof result === 'string') { const { tx: signedTx } = unpackTx(result, Tag.SignedTx); return buildTx({ diff --git a/src/channel/index.ts b/src/channel/index.ts index f9be3c2224..143483a6a2 100644 --- a/src/channel/index.ts +++ b/src/channel/index.ts @@ -35,7 +35,7 @@ import { ChannelState, } from './internal'; import { UnknownChannelStateError, ChannelError } from '../utils/errors'; -import { EncodedData } from '../utils/encoder'; +import { Encoded } from '../utils/encoder'; import { ContractCallReturnType } from '../apis/node'; import { pause } from '../utils/other'; @@ -50,9 +50,9 @@ type EventCallback = (...args: any[]) => void; interface CallContractOptions { amount?: number | BigNumber; - callData?: EncodedData<'cb'>; + callData?: Encoded.ContractBytearray; abiVersion?: number; - contract?: EncodedData<'ct'>; + contract?: Encoded.ContractAddress; returnValue?: any; gasUsed?: number | BigNumber; gasPrice?: number | BigNumber; @@ -285,14 +285,14 @@ export default class Channel { * ``` */ async update( - from: EncodedData<'ak'>, - to: EncodedData<'ak'>, + from: Encoded.AccountAddress, + to: Encoded.AccountAddress, amount: number | BigNumber, sign: SignTx, metadata: string[] = [], ): Promise<{ accepted: boolean; - signedTx?: EncodedData<'tx'>; + signedTx?: Encoded.Transaction; errorCode?: number; errorMessage?: string; }> { @@ -343,10 +343,10 @@ export default class Channel { */ async poi( { accounts, contracts }: { - accounts: Array>; - contracts?: Array>; + accounts: Encoded.AccountAddress[]; + contracts?: Encoded.ContractAddress[]; }, - ): Promise> { + ): Promise { return (await call(this, 'channels.get.poi', { accounts, contracts })).poi; } @@ -371,11 +371,13 @@ export default class Channel { * ) * ``` */ - async balances(accounts: Array>): Promise<{ [key: EncodedData<'ak'>]: string }> { + async balances( + accounts: Encoded.AccountAddress[], + ): Promise<{ [key: Encoded.AccountAddress]: string }> { return Object.fromEntries( (await call(this, 'channels.get.balances', { accounts })) .map((item: { - account: EncodedData<'ak'>; + account: Encoded.AccountAddress; balance: string; }) => [item.account, item.balance]), ); @@ -401,7 +403,7 @@ export default class Channel { * }) * ``` */ - async leave(): Promise<{ channelId: string; signedTx: EncodedData<'tx'> }> { + async leave(): Promise<{ channelId: string; signedTx: Encoded.Transaction }> { return new Promise((resolve, reject) => { enqueueAction( this, @@ -431,7 +433,7 @@ export default class Channel { * ).then(tx => console.log('on_chain_tx', tx)) * ``` */ - async shutdown(sign: Function): Promise> { + async shutdown(sign: Function): Promise { return new Promise((resolve, reject) => { enqueueAction( this, @@ -505,7 +507,7 @@ export default class Channel { sign: SignTx, { onOnChainTx, onOwnWithdrawLocked, onWithdrawLocked }: Pick = {}, - ): Promise<{ accepted: boolean; signedTx: EncodedData<'tx'> }> { + ): Promise<{ accepted: boolean; signedTx: Encoded.Transaction }> { return new Promise((resolve, reject) => { enqueueAction( this, @@ -644,14 +646,16 @@ export default class Channel { { code, callData, deposit, vmVersion, abiVersion, }: { - code: EncodedData<'cb'>; - callData: EncodedData<'cb'>; + code: Encoded.ContractBytearray; + callData: Encoded.ContractBytearray; deposit: number | BigNumber; vmVersion: number; abiVersion: number; }, sign: SignTx, - ): Promise<{ accepted: boolean; signedTx: EncodedData<'tx'>; address: EncodedData<'ct'> }> { + ): Promise<{ + accepted: boolean; signedTx: Encoded.Transaction; address: Encoded.ContractAddress; + }> { return new Promise((resolve, reject) => { enqueueAction( this, @@ -724,7 +728,7 @@ export default class Channel { amount, callData, contract, abiVersion, }: CallContractOptions, sign: SignTx, - ): Promise<{ accepted: boolean; signedTx: EncodedData<'tx'> }> { + ): Promise<{ accepted: boolean; signedTx: Encoded.Transaction }> { return new Promise((resolve, reject) => { enqueueAction( this, @@ -783,15 +787,19 @@ export default class Channel { amount, callData, contract, abiVersion, gasLimit = 1000000, gasPrice = MIN_GAS_PRICE, }: { amount: number; - callData: EncodedData<'cb'>; - contract: EncodedData<'ct'>; + callData: Encoded.ContractBytearray; + contract: Encoded.ContractAddress; abiVersion: number; gasLimit?: number; gasPrice?: number; }, sign: SignTx, { onOnChainTx }: Pick = {}, - ): Promise<{ accepted: boolean; signedTx: EncodedData<'tx'>; tx: EncodedData<'tx'> | Uint8Array }> { + ): Promise<{ + accepted: boolean; + signedTx: Encoded.Transaction; + tx: Encoded.Transaction | Uint8Array; + }> { return new Promise((resolve, reject) => { enqueueAction( this, @@ -854,8 +862,8 @@ export default class Channel { amount, callData, contract, abiVersion, }: { amount: number; - callData: EncodedData<'cb'>; - contract: EncodedData<'ct'>; + callData: Encoded.ContractBytearray; + contract: Encoded.ContractAddress; abiVersion: number; }, ): Promise { @@ -890,8 +898,8 @@ export default class Channel { */ async getContractCall( { caller, contract, round }: { - caller: EncodedData<'ak'>; - contract: EncodedData<'ct'>; + caller: Encoded.AccountAddress; + contract: Encoded.ContractAddress; round: number; }, ): Promise<{ @@ -925,7 +933,7 @@ export default class Channel { * ``` */ async getContractState( - contract: EncodedData<'ct'>, + contract: Encoded.ContractAddress, ): Promise<{ contract: Contract; contractState: object }> { const result = await call(this, 'channels.get.contract', { pubkey: contract }); return snakeToPascalObjKeys({ @@ -981,7 +989,10 @@ export default class Channel { * ) * ``` */ - async sendMessage(message: string | object, recipient: EncodedData<'ak'>): Promise { + async sendMessage( + message: string | object, + recipient: Encoded.AccountAddress, + ): Promise { const info = typeof message === 'object' ? JSON.stringify(message) : message; if (this.status() === 'connecting') { await new Promise((resolve) => { diff --git a/src/channel/internal.ts b/src/channel/internal.ts index 452f43ed8a..0fdd17ae5d 100644 --- a/src/channel/internal.ts +++ b/src/channel/internal.ts @@ -21,7 +21,7 @@ import BigNumber from 'bignumber.js'; import type Channel from '.'; import JsonBig from '../utils/json-big'; import { pascalToSnake } from '../utils/string'; -import { EncodedData } from '../utils/encoder'; +import { Encoded } from '../utils/encoder'; import { BaseError, ChannelCallError, ChannelPingTimedOutError, UnknownChannelStateError, } from '../utils/errors'; @@ -31,20 +31,20 @@ interface ChannelAction { action: (channel: Channel, state?: ChannelFsm) => ChannelFsm; } -export type SignTxWithTag = (tag: string, tx: EncodedData<'tx'>, options?: object) => ( - Promise> +export type SignTxWithTag = (tag: string, tx: Encoded.Transaction, options?: object) => ( + Promise ); // TODO: SignTx shouldn't return number or null -export type SignTx = (tx: EncodedData<'tx'>, options?: object) => ( - Promise | number | null> +export type SignTx = (tx: Encoded.Transaction, options?: object) => ( + Promise ); export interface ChannelOptions { existingFsmId?: string; url: string; role: 'initiator' | 'responder'; - initiatorId: EncodedData<'ak'>; - responderId: EncodedData<'ak'>; + initiatorId: Encoded.AccountAddress; + responderId: Encoded.AccountAddress; pushAmount: number; initiatorAmount: BigNumber; responderAmount: BigNumber; @@ -81,7 +81,7 @@ export interface ChannelState { reject: (e: BaseError) => void; sign: SignTx; handler?: ChannelHandler; - onOnChainTx?: (tx: EncodedData<'tx'>) => void; + onOnChainTx?: (tx: Encoded.Transaction) => void; onOwnWithdrawLocked?: () => void; onWithdrawLocked?: () => void; onOwnDepositLocked?: () => void; @@ -125,7 +125,7 @@ const PONG_TIMEOUT_MS = 5000; // TODO: move to Channel instance to avoid is-null checks and for easier debugging export const options = new WeakMap(); export const status = new WeakMap(); -export const state = new WeakMap>(); +export const state = new WeakMap(); const fsm = new WeakMap(); const websockets = new WeakMap(); export const eventEmitters = new WeakMap(); @@ -165,7 +165,7 @@ export function changeStatus(channel: Channel, newStatus: string): void { } } -export function changeState(channel: Channel, newState: EncodedData<'tx'>): void { +export function changeState(channel: Channel, newState: Encoded.Transaction): void { state.set(channel, newState); emit(channel, 'stateChanged', newState); } diff --git a/src/contract/aci.ts b/src/contract/aci.ts index cc697dba76..f6a1c00deb 100644 --- a/src/contract/aci.ts +++ b/src/contract/aci.ts @@ -21,7 +21,7 @@ import { Tag, AensName } from '../tx/builder/constants'; import { buildContractIdByContractTx, unpackTx } from '../tx/builder'; import { _buildTx } from '../tx'; import { send } from '../spend'; -import { decode, EncodedData, EncodingType } from '../utils/encoder'; +import { decode, Encoded } from '../utils/encoder'; import { MissingContractDefError, MissingContractAddressError, @@ -73,7 +73,7 @@ interface Aci extends BaseAci { } interface Event { - address: EncodedData<'ct'>; + address: Encoded.ContractAddress; data: string; topics: Array; } @@ -83,7 +83,7 @@ interface DecodedEvent { args: unknown; contract: { name: string; - address: EncodedData<'ct'>; + address: Encoded.ContractAddress; }; } @@ -94,9 +94,9 @@ export interface ContractInstance { _name: string; calldata: any; source?: string; - bytecode?: EncodedData<'cb'>; + bytecode?: Encoded.ContractBytearray; deployInfo: { - address?: EncodedData<'ct'>; + address?: Encoded.ContractAddress; result?: { callerId: string; callerNonce: string; @@ -108,13 +108,13 @@ export interface ContractInstance { returnType: ContractCallReturnType; returnValue: string; }; - owner?: EncodedData<'ak'>; + owner?: Encoded.AccountAddress; transaction?: string; rawTx?: string; txData?: TxData; }; options: any; - compile: (options?: {}) => Promise>; + compile: (options?: {}) => Promise; _estimateGas: (name: string, params: any[], options: object) => Promise; deploy: (params?: any[], options?: object) => Promise; call: (fn: string, params?: any[], options?: {}) => Promise<{ @@ -123,9 +123,9 @@ export interface ContractInstance { txData: TxData; rawTx: string; result: { - callerId: EncodedData<'ak'>; + callerId: Encoded.AccountAddress; callerNonce: number; - contractId: EncodedData<'ct'>; + contractId: Encoded.ContractAddress; gasPrice: number; gasUsed: number; height: number; @@ -177,9 +177,9 @@ export default async function getContractInstance({ onCompiler: Compiler; onNode: Node; source?: string; - bytecode?: EncodedData<'cb'>; + bytecode?: Encoded.ContractBytearray; aci?: Aci; - contractAddress?: EncodedData<'ct'> | AensName; + contractAddress?: Encoded.ContractAddress | AensName; fileSystem?: Record; validateBytecode?: boolean; [key: string]: any; @@ -195,7 +195,7 @@ export default async function getContractInstance({ contractAddress, 'contract_pubkey', { resolveByNode: true, onNode }, - ) as EncodedData<'ct'>; + ) as Encoded.ContractAddress; } if (contractAddress == null && source == null && bytecode == null) { @@ -250,19 +250,19 @@ export default async function getContractInstance({ * Compile contract * @returns bytecode */ - instance.compile = async (options = {}): Promise> => { + instance.compile = async (options = {}): Promise => { if (instance.bytecode != null) throw new IllegalArgumentError('Contract already compiled'); if (instance.source == null) throw new IllegalArgumentError('Can\'t compile without source code'); instance.bytecode = (await onCompiler.compileContract({ code: instance.source, options: { ...instance.options, ...options }, - })).bytecode as EncodedData<'cb'>; + })).bytecode as Encoded.ContractBytearray; return instance.bytecode; }; const handleCallError = ( { returnType, returnValue }: { returnType: ContractCallReturnType; - returnValue: EncodedData; + returnValue: Encoded.ContractBytearray; }, transaction: string, ): void => { @@ -281,12 +281,12 @@ export default async function getContractInstance({ throw new NodeInvocationError(message, transaction); }; - const sendAndProcess = async (tx: EncodedData<'tx'>, options: any): Promise<{ + const sendAndProcess = async (tx: Encoded.Transaction, options: any): Promise<{ result?: ContractInstance['deployInfo']['result']; hash: TxData['hash']; tx: Awaited>>; txData: TxData; - rawTx: EncodedData<'tx'>; + rawTx: Encoded.Transaction; }> => { options = { ...instance.options, ...options }; const txData = await send(tx, options); @@ -393,7 +393,7 @@ export default async function getContractInstance({ .catch((error: any) => { if (opt.callStatic === true) return DRY_RUN_ACCOUNT.pub; throw error; - }) as EncodedData<'ak'>; + }) as Encoded.AccountAddress; const callData = instance.calldata.encode(instance._name, fn, params); let res: any; @@ -413,7 +413,7 @@ export default async function getContractInstance({ if (callObj == null) throw new UnexpectedTsError(); handleCallError({ returnType: callObj.returnType as ContractCallReturnType, - returnValue: callObj.returnValue as EncodedData, + returnValue: callObj.returnValue as Encoded.ContractBytearray, }, tx); res = { ...dryRunOther, tx: unpackTx(tx), result: callObj }; } else { @@ -444,9 +444,11 @@ export default async function getContractInstance({ * @throws {@link AmbiguousEventDefinitionError} */ function getContractNameByEvent( - address: EncodedData<'ct'>, + address: Encoded.ContractAddress, nameHash: BigInt, - { contractAddressToName }: { contractAddressToName?: { [key: EncodedData<'ct'>]: string } }, + { contractAddressToName }: { + contractAddressToName?: { [key: Encoded.ContractAddress]: string }; + }, ): string { const addressToName = { ...instance.options.contractAddressToName, ...contractAddressToName }; if (addressToName[address] != null) return addressToName[address]; diff --git a/src/contract/ga.ts b/src/contract/ga.ts index d0ed0186a1..0bdcb8314d 100644 --- a/src/contract/ga.ts +++ b/src/contract/ga.ts @@ -28,7 +28,7 @@ import { _buildTx, BuildTxOptions, getVmVersion, prepareTxParams, } from '../tx'; import { hash } from '../utils/crypto'; -import { decode, EncodedData } from '../utils/encoder'; +import { decode, Encoded, Encoding } from '../utils/encoder'; import { IllegalArgumentError, MissingParamError, InvalidAuthDataError } from '../utils/errors'; import { concatBuffers } from '../utils/other'; import AccountBase from '../account/Base'; @@ -46,7 +46,7 @@ import Compiler from './Compiler'; * @returns if account is GA */ export async function isGA( - address: EncodedData<'ak'>, + address: Encoded.AccountAddress, options: Parameters[1], ): Promise { const { contractId } = await getAccount(address, options); @@ -70,10 +70,10 @@ export async function createGeneralizedAccount( onAccount, onCompiler, onNode, ...options }: CreateGeneralizedAccountOptions, ): Promise; - transaction: EncodedData<'th'>; - rawTx: EncodedData<'tx'>; - gaContractId: EncodedData<'ct'>; + owner: Encoded.AccountAddress; + transaction: Encoded.TxHash; + rawTx: Encoded.Transaction; + gaContractId: Encoded.ContractAddress; }>> { const ownerId = await onAccount.address(options); if (await isGA(ownerId, { onNode })) throw new IllegalArgumentError(`Account ${ownerId} is already GA`); @@ -124,10 +124,10 @@ interface CreateGeneralizedAccountOptions extends * @returns Transaction string */ export async function createMetaTx( - rawTransaction: EncodedData<'tx'>, + rawTransaction: Encoded.Transaction, authData: { gasLimit?: number; - callData?: EncodedData<'cb'>; + callData?: Encoded.ContractBytearray; source?: string; args?: any[]; }, @@ -137,10 +137,12 @@ export async function createMetaTx( }: { onAccount: AccountBase; onCompiler: Compiler; onNode: Node } & Parameters[0], -): Promise> { +): Promise { const wrapInEmptySignedTx = ( - tx: EncodedData<'tx'> | Uint8Array | TxUnpacked, - ): BuiltTx => buildTx({ encodedTx: tx, signatures: [] }, Tag.SignedTx); + tx: Encoded.Transaction | Uint8Array | TxUnpacked, + ): BuiltTx => ( + buildTx({ encodedTx: tx, signatures: [] }, Tag.SignedTx) + ); if (Object.keys(authData).length <= 0) throw new MissingParamError('authData is required'); @@ -187,7 +189,7 @@ export async function createMetaTx( * @returns Transaction hash */ export async function buildAuthTxHash( - transaction: EncodedData<'tx'>, + transaction: Encoded.Transaction, { onNode }: { onNode: Node }, ): Promise { const { networkId } = await onNode.getStatus(); diff --git a/src/contract/methods.ts b/src/contract/methods.ts index ce7430da87..ab10bb1c28 100644 --- a/src/contract/methods.ts +++ b/src/contract/methods.ts @@ -25,7 +25,7 @@ import { AensName } from '../tx/builder/constants'; import { produceNameId } from '../tx/builder/helpers'; import { concatBuffers } from '../utils/other'; -import { decode, EncodedData, EncodingType } from '../utils/encoder'; +import { decode, Encoded } from '../utils/encoder'; import AccountBase from '../account/Base'; import Node from '../Node'; @@ -41,7 +41,7 @@ export { default as getContractInstance } from './aci'; * @returns Signature in hex representation */ async function delegateSignatureCommon( - ids: Array>, + ids: Encoded.Any[], { onAccount, onNode, ...opt }: { onAccount: AccountBase; onNode: Node } & Parameters[1], ): Promise { @@ -78,7 +78,7 @@ async function delegateSignatureCommon( * ``` */ export async function createAensDelegationSignature( - contractId: EncodedData<'ct'>, + contractId: Encoded.ContractAddress, opt: Parameters[0] & Parameters[1] & { name?: AensName }, ): Promise { @@ -112,9 +112,9 @@ export async function createAensDelegationSignature( * ``` */ export async function createOracleDelegationSignature( - contractId: EncodedData<'ct'>, + contractId: Encoded.ContractAddress, opt: Parameters[0] & Parameters[1] & - { queryId?: EncodedData<'oq'> }, + { queryId?: Encoded.OracleQueryId }, ): Promise { return delegateSignatureCommon( [opt.queryId ?? await opt.onAccount.address(opt), contractId], diff --git a/src/deprecated/index.ts b/src/deprecated/index.ts index 3b2043ee45..2e6da7aa39 100644 --- a/src/deprecated/index.ts +++ b/src/deprecated/index.ts @@ -5,7 +5,7 @@ import { calculateMinFee as calculateMinFeeInternal } from '../tx/builder/field- import { TxParamsCommon } from '../tx/builder/schema'; import { AE_AMOUNT_FORMATS } from '../utils/amount-formatter'; import { mapObject } from '../utils/other'; -import { EncodingType } from '../utils/encoder'; +import { Encoding } from '../utils/encoder'; export * from './methods'; @@ -155,7 +155,7 @@ export const PREFIX_ID_TAG = { */ export const ID_TAG_PREFIX = mapObject( PREFIX_ID_TAG, - ([key, value]: [EncodingType, number]) => [value, key], + ([key, value]: [Encoding, number]) => [value, key], ); /** diff --git a/src/deprecated/methods.ts b/src/deprecated/methods.ts index 8d4cd4b4b3..61cfdb8227 100644 --- a/src/deprecated/methods.ts +++ b/src/deprecated/methods.ts @@ -1,4 +1,4 @@ -import { EncodedData } from '../utils/encoder'; +import { Encoded } from '../utils/encoder'; import { createMetaTx } from '../contract/ga'; import Node from '../Node'; @@ -7,12 +7,12 @@ import Node from '../Node'; * @hidden */ export async function signUsingGA( - tx: EncodedData<'tx'>, + tx: Encoded.Transaction, { authData, authFun, ...options }: { authData: Parameters[1]; authFun: Parameters[2]; } & Parameters[3], -): Promise> { +): Promise { return createMetaTx(tx, authData, authFun, options); } diff --git a/src/oracle.ts b/src/oracle.ts index 35075c0d11..84cc9a1569 100644 --- a/src/oracle.ts +++ b/src/oracle.ts @@ -37,7 +37,9 @@ import { } from './tx/builder/schema'; import { Tag } from './tx/builder/constants'; import { RequestTimedOutError } from './utils/errors'; -import { decode, encode, EncodedData } from './utils/encoder'; +import { + decode, encode, Encoded, Encoding, +} from './utils/encoder'; import { _getPollInterval } from './chain'; import { _buildTx, BuildTxOptions } from './tx'; import Node from './Node'; @@ -56,7 +58,7 @@ type OracleQueries = Awaited>['orac * @returns Callback to stop polling function */ export function pollForQueries( - oracleId: EncodedData<'ok'>, + oracleId: Encoded.OracleAddress, onQuery: (queries: OracleQueries) => void, { interval, onNode, ...options }: { interval?: number; onNode: Node } & Parameters[1], @@ -94,8 +96,8 @@ export function pollForQueries( * @returns OracleQuery object */ export async function pollForQueryResponse( - oracleId: EncodedData<'ok'>, - queryId: EncodedData<'oq'>, + oracleId: Encoded.OracleAddress, + queryId: Encoded.OracleQueryId, { interval, onNode, ...options }: { interval?: number; onNode: Node } & Parameters[1], ): Promise { @@ -106,7 +108,7 @@ export async function pollForQueryResponse( do { if (height != null) await pause(interval); ({ response, ttl } = await onNode.getOracleQueryByPubkeyAndQueryId(oracleId, queryId)); - const responseBuffer = decode(response as EncodedData<'or'>); + const responseBuffer = decode(response as Encoded.OracleResponse); if (responseBuffer.length > 0) return responseBuffer.toString(); height = await this.getHeight(); } while (ttl >= height); @@ -122,15 +124,15 @@ export async function pollForQueryResponse( * @returns OracleQuery object */ export async function getQueryObject( - oracleId: EncodedData<'ok'>, - queryId: EncodedData<'oq'>, + oracleId: Encoded.OracleAddress, + queryId: Encoded.OracleQueryId, options: RespondToQueryOptions & Parameters[2], ): Promise { const record = await options.onNode.getOracleQueryByPubkeyAndQueryId(oracleId, queryId); return { ...record, - decodedQuery: decode(record.query as EncodedData<'oq'>).toString(), - decodedResponse: decode(record.response as EncodedData<'or'>).toString(), + decodedQuery: decode(record.query as Encoded.OracleQueryId).toString(), + decodedResponse: decode(record.response as Encoded.OracleResponse).toString(), respond: async (response, opt) => ( // eslint-disable-next-line @typescript-eslint/no-use-before-define respondToQuery(oracleId, queryId, response, { ...options, ...opt }) @@ -162,7 +164,7 @@ interface GetQueryObjectReturnType extends Awaited, + oracleId: Encoded.OracleAddress, query: string, options: PostQueryToOracleOptions, ): Promise> & Awaited>> { @@ -210,7 +212,7 @@ interface PostQueryToOracleOptions extends PostQueryToOracleOptionsType {} * @returns Oracle object */ export async function extendOracleTtl( - oracleId: EncodedData<'ok'>, + oracleId: Encoded.OracleAddress, options: ExtendOracleTtlOptions, ): Promise> & Awaited>> { const oracleExtendTx = await _buildTx(Tag.OracleExtendTx, { @@ -245,8 +247,8 @@ interface ExtendOracleTtlOptions extends ExtendOracleTtlOptionsType {} * @returns Oracle object */ export async function respondToQuery( - oracleId: EncodedData<'ok'>, - queryId: EncodedData<'oq'>, + oracleId: Encoded.OracleAddress, + queryId: Encoded.OracleQueryId, response: string, options: RespondToQueryOptions, ): Promise> & Awaited>> { @@ -279,7 +281,7 @@ interface RespondToQueryOptions extends RespondToQueryOptionsType {} * @returns Oracle object */ export async function getOracleObject( - oracleId: EncodedData<'ok'>, + oracleId: Encoded.OracleAddress, options: { onNode: Node; onAccount: AccountBase }, ): Promise { return { @@ -308,7 +310,7 @@ export async function getOracleObject( } interface GetOracleObjectReturnType extends Awaited> { - id: EncodedData<'ok'>; + id: Encoded.OracleAddress; queries: OracleQueries; // TODO: replace getOracleObject with a class pollQueries: (cb: Parameters[1]) => ReturnType; @@ -348,7 +350,7 @@ export async function registerOracle( }); return { ...await send(oracleRegisterTx, options), - ...await getOracleObject(encode(decode(accountId), 'ok'), options), + ...await getOracleObject(encode(decode(accountId), Encoding.OracleAddress), options), }; } diff --git a/src/spend.ts b/src/spend.ts index 1078eb1ab5..aff6408deb 100644 --- a/src/spend.ts +++ b/src/spend.ts @@ -21,7 +21,7 @@ import { import { _buildTx, BuildTxOptions } from './tx'; import { buildTxHash, unpackTx } from './tx/builder'; import { ArgumentError } from './utils/errors'; -import { EncodedData } from './utils/encoder'; +import { Encoded, Encoding } from './utils/encoder'; import { Tag, AensName } from './tx/builder/constants'; import AccountBase from './account/Base'; @@ -34,7 +34,10 @@ import AccountBase from './account/Base'; * valid * @returns Transaction */ -export async function send(tx: EncodedData<'tx'>, options: SendOptions): Promise { +export async function send( + tx: Encoded.Transaction, + options: SendOptions, +): Promise { // TODO: detect authFun in AccountGa const authFun = options.innerTx === true ? undefined @@ -66,14 +69,18 @@ interface SendReturnType extends Awaited> {} */ export async function spend( amount: number | string, - recipientIdOrName: EncodedData<'ak'> | AensName, + recipientIdOrName: Encoded.AccountAddress | AensName, options: SpendOptions, ): ReturnType { return send( await _buildTx(Tag.SpendTx, { ...options, senderId: await options.onAccount.address(options), - recipientId: await resolveName(recipientIdOrName, 'account_pubkey', options), + recipientId: await resolveName( + recipientIdOrName, + 'account_pubkey', + options, + ), amount, }), options, @@ -95,13 +102,17 @@ interface SpendOptions extends SpendOptionsType {} */ export async function transferFunds( fraction: number | string, - recipientIdOrName: AensName | EncodedData<'ak'>, + recipientIdOrName: AensName | Encoded.AccountAddress, options: TransferFundsOptions, ): ReturnType { if (fraction < 0 || fraction > 1) { throw new ArgumentError('fraction', 'a number between 0 and 1', fraction); } - const recipientId = await resolveName<'ak'>(recipientIdOrName, 'account_pubkey', options); + const recipientId = await resolveName( + recipientIdOrName, + 'account_pubkey', + options, + ); const senderId = await options.onAccount.address(options); const balance = new BigNumber( await getBalance.bind(options.onAccount)(senderId, options), @@ -135,7 +146,7 @@ interface TransferFundsOptions extends TransferFundsOptionsType {} * @returns Object Transaction */ export async function payForTransaction( - transaction: EncodedData<'tx'>, + transaction: Encoded.Transaction, options: PayForTransactionOptions, ): ReturnType { return send( diff --git a/src/tx/builder/address.ts b/src/tx/builder/address.ts index 7bd528b38c..ce18ffe303 100644 --- a/src/tx/builder/address.ts +++ b/src/tx/builder/address.ts @@ -1,7 +1,9 @@ import { ArgumentError, PrefixNotFoundError, TagNotFoundError } from '../../utils/errors'; -import { isKeyOfObject } from '../../utils/other'; import { toBytes } from '../../utils/bytes'; -import { decode, encode, EncodedData } from '../../utils/encoder'; +import { + decode, encode, Encoded, Encoding, +} from '../../utils/encoder'; +import { isItemOfArray } from '../../utils/other'; /** * Map of prefix to ID tag constant @@ -9,16 +11,16 @@ import { decode, encode, EncodedData } from '../../utils/encoder'; * @see {@link https://github.com/aeternity/aeserialization/blob/eb68fe331bd476910394966b7f5ede7a74d37e35/src/aeser_id.erl#L97-L102} * @see {@link https://github.com/aeternity/aeserialization/blob/eb68fe331bd476910394966b7f5ede7a74d37e35/src/aeser_api_encoder.erl#L163-L168} */ -enum PrefixToIdTag { - ak = 1, - nm = 2, - cm = 3, - ok = 4, - ct = 5, - ch = 6, -} +const idTagToEncoding = [ + Encoding.AccountAddress, + Encoding.Name, + Encoding.Commitment, + Encoding.OracleAddress, + Encoding.ContractAddress, + Encoding.Channel, +] as const; -type AddressPrefixes = keyof typeof PrefixToIdTag; +type AddressEncodings = typeof idTagToEncoding[number]; /** * Utility function to create and _id type @@ -26,11 +28,11 @@ type AddressPrefixes = keyof typeof PrefixToIdTag; * @param hashId - Encoded hash * @returns Buffer Buffer with ID tag and decoded HASh */ -export function writeId(hashId: EncodedData): Buffer { +export function writeId(hashId: Encoded.Generic): Buffer { if (typeof hashId !== 'string') throw new ArgumentError('hashId', 'a string', hashId); - const prefix = hashId.slice(0, 2); - if (!isKeyOfObject(prefix, PrefixToIdTag)) throw new TagNotFoundError(prefix); - const idTag = PrefixToIdTag[prefix]; + const encoding = hashId.slice(0, 2); + if (!isItemOfArray(encoding, idTagToEncoding)) throw new TagNotFoundError(encoding); + const idTag = idTagToEncoding.indexOf(encoding) + 1; return Buffer.from([...toBytes(idTag), ...decode(hashId)]); } @@ -40,9 +42,9 @@ export function writeId(hashId: EncodedData): Buffer { * @param buf - Data * @returns Encoided hash string with prefix */ -export function readId(buf: Buffer): EncodedData { +export function readId(buf: Buffer): Encoded.Generic { const idTag = Buffer.from(buf).readUIntBE(0, 1); - const prefix = PrefixToIdTag[idTag] as AddressPrefixes; - if (prefix == null) throw new PrefixNotFoundError(idTag); - return encode(buf.slice(1, buf.length), prefix); + const encoding = idTagToEncoding[idTag - 1]; + if (encoding == null) throw new PrefixNotFoundError(idTag); + return encode(buf.slice(1, buf.length), encoding); } diff --git a/src/tx/builder/field-types/name-id.ts b/src/tx/builder/field-types/name-id.ts index 6d0149562f..388546c4b7 100644 --- a/src/tx/builder/field-types/name-id.ts +++ b/src/tx/builder/field-types/name-id.ts @@ -1,20 +1,20 @@ import { AensName } from '../constants'; import { produceNameId, isNameValid } from '../helpers'; import { writeId, readId } from '../address'; -import { EncodedData } from '../../../utils/encoder'; +import { Encoded } from '../../../utils/encoder'; export default { /** * @param value - AENS name ID */ - serialize(value: AensName | EncodedData<'nm'>): Buffer { + serialize(value: AensName | Encoded.Name): Buffer { return writeId(isNameValid(value) ? produceNameId(value) : value); }, /** * @param value - AENS name ID Buffer */ - deserialize(value: Buffer): EncodedData<'nm'> { - return readId(value) as EncodedData<'nm'>; + deserialize(value: Buffer): Encoded.Name { + return readId(value) as Encoded.Name; }, }; diff --git a/src/tx/builder/helpers.ts b/src/tx/builder/helpers.ts index cdafb3f8f4..88104fb5e6 100644 --- a/src/tx/builder/helpers.ts +++ b/src/tx/builder/helpers.ts @@ -1,14 +1,16 @@ import BigNumber from 'bignumber.js'; -import { hash, genSalt } from '../../utils/crypto'; -import { encode, decode, EncodedData } from '../../utils/encoder'; +import { genSalt, hash } from '../../utils/crypto'; +import { + decode, encode, Encoded, Encoding, +} from '../../utils/encoder'; import { toBytes } from '../../utils/bytes'; import { concatBuffers } from '../../utils/other'; import { + AensName, NAME_BID_RANGES, - NAME_FEE_BID_INCREMENT, NAME_BID_TIMEOUT_BLOCKS, + NAME_FEE_BID_INCREMENT, NAME_MAX_LENGTH_FEE, - AensName, } from './constants'; import { ceil } from '../../utils/bignumber'; import { IllegalBidFeeError } from '../../utils/errors'; @@ -27,12 +29,12 @@ import { readId, writeId } from './address'; * @returns Contract public key */ export function buildContractId( - ownerId: EncodedData<'ak'>, + ownerId: Encoded.AccountAddress, nonce: number | BigNumber, -): EncodedData<'ct'> { +): Encoded.ContractAddress { const ownerIdAndNonce = Buffer.from([...decode(ownerId), ...toBytes(nonce)]); const b2bHash = hash(ownerIdAndNonce); - return encode(b2bHash, 'ct'); + return encode(b2bHash, Encoding.ContractAddress); } /** @@ -44,10 +46,10 @@ export function buildContractId( * @returns Contract public key */ export function oracleQueryId( - senderId: EncodedData<'ak'>, + senderId: Encoded.AccountAddress, nonce: number | BigNumber | string, - oracleId: EncodedData<'ok'>, -): EncodedData<'oq'> { + oracleId: Encoded.OracleAddress, +): Encoded.OracleQueryId { function _int32(val: number | string | BigNumber): Buffer { const nonceBE = toBytes(val, true); return concatBuffers([Buffer.alloc(32 - nonceBE.length), nonceBE]); @@ -56,7 +58,7 @@ export function oracleQueryId( const b2bHash = hash( Buffer.from([...decode(senderId), ..._int32(nonce), ...decode(oracleId)]), ); - return encode(b2bHash, 'oq'); + return encode(b2bHash, Encoding.OracleQueryId); } /** @@ -75,8 +77,8 @@ export function formatSalt(salt: number): Buffer { * @param name - Name to encode * @returns `nm_` prefixed encoded AENS name */ -export function produceNameId(name: AensName): EncodedData<'nm'> { - return encode(hash(name.toLowerCase()), 'nm'); +export function produceNameId(name: AensName): Encoded.Name { + return encode(hash(name.toLowerCase()), Encoding.Name); } /** @@ -87,8 +89,14 @@ export function produceNameId(name: AensName): EncodedData<'nm'> { * @param salt - Random salt * @returns Commitment hash */ -export function commitmentHash(name: AensName, salt: number = genSalt()): EncodedData<'cm'> { - return encode(hash(concatBuffers([Buffer.from(name.toLowerCase()), formatSalt(salt)])), 'cm'); +export function commitmentHash( + name: AensName, + salt: number = genSalt(), +): Encoded.Commitment { + return encode( + hash(concatBuffers([Buffer.from(name.toLowerCase()), formatSalt(salt)])), + Encoding.Commitment, + ); } /** @@ -154,12 +162,12 @@ export function isNameValid(name: string): name is AensName { return name.endsWith(AENS_SUFFIX); } -enum PointerKeyByPrefix { - ak = 'account_pubkey', - ok = 'oracle_pubkey', - ct = 'contract_pubkey', - ch = 'channel', -} +const encodingToPointerKey = { + [Encoding.AccountAddress]: 'account_pubkey', + [Encoding.OracleAddress]: 'oracle_pubkey', + [Encoding.ContractAddress]: 'contract_pubkey', + [Encoding.Channel]: 'channel', +} as const; /** * @category AENS @@ -167,11 +175,11 @@ enum PointerKeyByPrefix { * @returns default AENS pointer key */ export function getDefaultPointerKey( - identifier: EncodedData, -): PointerKeyByPrefix { + identifier: Encoded.Generic, +): typeof encodingToPointerKey[keyof typeof encodingToPointerKey] { decode(identifier); - const prefix = identifier.substring(0, 2) as keyof typeof PointerKeyByPrefix; - return PointerKeyByPrefix[prefix]; + const prefix = identifier.substring(0, 2) as keyof typeof encodingToPointerKey; + return encodingToPointerKey[prefix]; } /** diff --git a/src/tx/builder/index.ts b/src/tx/builder/index.ts index 8c411d8168..5418c0f799 100644 --- a/src/tx/builder/index.ts +++ b/src/tx/builder/index.ts @@ -1,6 +1,6 @@ import { decode as rlpDecode, encode as rlpEncode, NestedUint8Array } from 'rlp'; import { - encode, decode, EncodedData, EncodingType, + decode, encode, Encoded, Encoding, } from '../../utils/encoder'; import { AE_AMOUNT_FORMATS } from '../../utils/amount-formatter'; import { hash } from '../../utils/crypto'; @@ -8,25 +8,24 @@ import { Field } from './field-types'; import { FIELD_TYPES, RawTxObject, + TX_SCHEMA, TxField, - TxTypeSchemas, TxParamsCommon, - TX_SCHEMA, TxSchema, + TxTypeSchemas, } from './schema'; import { Tag } from './constants'; import { - readInt, - readPointers, - writeInt, - buildPointers, - buildContractId, + buildContractId, buildPointers, readInt, readPointers, writeInt, } from './helpers'; import { readId, writeId } from './address'; import { toBytes } from '../../utils/bytes'; import MPTree, { MPTreeBinary } from '../../utils/mptree'; import { - ArgumentError, InvalidTxParamsError, SchemaNotFoundError, DecodeError, + ArgumentError, + DecodeError, + InvalidTxParamsError, + SchemaNotFoundError, } from '../../utils/errors'; import { isKeyOfObject } from '../../utils/other'; import { NamePointer } from '../../apis/node'; @@ -39,7 +38,7 @@ import { NamePointer } from '../../apis/node'; function deserializeField( value: any, type: FIELD_TYPES | Field, - prefix?: EncodingType | EncodingType[], + prefix?: Encoding | Encoding[], ): any { if (value == null) return ''; switch (type) { @@ -60,28 +59,28 @@ function deserializeField( case FIELD_TYPES.bool: return value[0] === 1; case FIELD_TYPES.binary: - return encode(value, prefix as EncodingType); + return encode(value, prefix as Encoding); case FIELD_TYPES.stateTree: - return encode(value, 'ss'); + return encode(value, Encoding.StateTrees); case FIELD_TYPES.string: return value.toString(); case FIELD_TYPES.payload: - return encode(value, 'ba'); + return encode(value, Encoding.Bytearray); case FIELD_TYPES.pointers: return readPointers(value); case FIELD_TYPES.rlpBinary: // eslint-disable-next-line @typescript-eslint/no-use-before-define - return unpackTx(encode(value, 'tx')); + return unpackTx(encode(value, Encoding.Transaction)); case FIELD_TYPES.rlpBinaries: // eslint-disable-next-line @typescript-eslint/no-use-before-define - return value.map((v: Buffer) => unpackTx(encode(v, 'tx'))); + return value.map((v: Buffer) => unpackTx(encode(v, Encoding.Transaction))); case FIELD_TYPES.rawBinary: return value; case FIELD_TYPES.hex: return value.toString('hex'); case FIELD_TYPES.offChainUpdates: // eslint-disable-next-line @typescript-eslint/no-use-before-define - return value.map((v: Buffer) => unpackTx(encode(v, 'tx'))); + return value.map((v: Buffer) => unpackTx(encode(v, Encoding.Transaction))); case FIELD_TYPES.callStack: // TODO: fix this return [readInt(value)]; @@ -138,7 +137,7 @@ function serializeField(value: any, type: FIELD_TYPES | Field, params: any): any return value.map(Buffer.from); case FIELD_TYPES.payload: return typeof value === 'string' && value.split('_')[0] === 'ba' - ? decode(value as EncodedData<'ba'>) + ? decode(value as Encoded.Bytearray) : toBytes(value); case FIELD_TYPES.string: return toBytes(value); @@ -167,7 +166,7 @@ function serializeField(value: any, type: FIELD_TYPES | Field, params: any): any function validateField( value: any, type: FIELD_TYPES | Field, - prefix?: EncodingType | EncodingType[], + prefix?: Encoding | Encoding[], ): string | undefined { // All fields are required if (value == null) return 'Field is required'; @@ -249,8 +248,8 @@ export function unpackRawTx( /** * @category transaction builder */ -export interface BuiltTx { - tx: EncodedData; +export interface BuiltTx { + tx: Encoded.Generic; rlpEncoded: Uint8Array; binary: Uint8Array; txObject: RawTxObject; @@ -275,14 +274,17 @@ export function buildTx( _params: Omit & { VSN?: number }, type: TxType, { - excludeKeys = [], prefix = 'tx', vsn, denomination = AE_AMOUNT_FORMATS.AETTOS, + excludeKeys = [], + prefix = Encoding.Transaction, + vsn, + denomination = AE_AMOUNT_FORMATS.AETTOS, }: { excludeKeys?: string[]; - prefix?: EncodingType; + prefix?: Encoding; vsn?: number; denomination?: AE_AMOUNT_FORMATS; } = {}, -): BuiltTx { +): BuiltTx { const schemas = TX_SCHEMA[type]; vsn ??= Math.max(...Object.keys(schemas).map((a) => +a)); @@ -303,7 +305,7 @@ export function buildTx( } const binary = filteredSchema - .map(([key, fieldType]: [keyof TxSchema, FIELD_TYPES, EncodingType]) => ( + .map(([key, fieldType]: [keyof TxSchema, FIELD_TYPES, Encoding]) => ( serializeField( params[key], fieldType, @@ -314,7 +316,7 @@ export function buildTx( { ...params, ...overrideParams }, type, { - excludeKeys, prefix: 'tx', vsn, denomination, + excludeKeys, prefix: Encoding.Transaction, vsn, denomination, }, ), }, @@ -350,7 +352,7 @@ export interface TxUnpacked { * @returns object.txType Transaction type */ export function unpackTx( - encodedTx: EncodedData<'tx' | 'pi'>, + encodedTx: Encoded.Transaction | Encoded.Poi, txType?: TxType, ): TxUnpacked { const rlpEncoded = decode(encodedTx); @@ -374,11 +376,11 @@ export function unpackTx( * @param rawTx - base64 or rlp encoded transaction * @returns Transaction hash */ -export function buildTxHash(rawTx: EncodedData<'tx'> | Uint8Array): EncodedData<'th'> { +export function buildTxHash(rawTx: Encoded.Transaction | Uint8Array): Encoded.TxHash { const data = typeof rawTx === 'string' && rawTx.startsWith('tx_') ? decode(rawTx) : rawTx; - return encode(hash(data), 'th'); + return encode(hash(data), Encoding.TxHash); } /** @@ -387,7 +389,9 @@ export function buildTxHash(rawTx: EncodedData<'tx'> | Uint8Array): EncodedData< * @param contractTx - Transaction * @returns Contract public key */ -export function buildContractIdByContractTx(contractTx: EncodedData<'tx'>): EncodedData<'ct'> { +export function buildContractIdByContractTx( + contractTx: Encoded.Transaction, +): Encoded.ContractAddress { const { txType, tx } = unpackTx(contractTx); if (![Tag.ContractCreateTx, Tag.GaAttachTx].includes(txType)) { throw new ArgumentError('contractCreateTx', 'a contractCreateTx or gaAttach', txType); diff --git a/src/tx/builder/schema.ts b/src/tx/builder/schema.ts index 9f6493bb49..5aa3ed3c8b 100644 --- a/src/tx/builder/schema.ts +++ b/src/tx/builder/schema.ts @@ -9,7 +9,7 @@ import { Tag } from './constants'; import { Field, uInt, shortUInt, coinAmount, name, nameId, nameFee, deposit, gasLimit, gasPrice, fee, } from './field-types'; -import { EncodedData, EncodingType } from '../../utils/encoder'; +import { Encoded, Encoding } from '../../utils/encoder'; import MPTree from '../../utils/mptree'; import { NamePointer } from '../../apis/node'; @@ -36,7 +36,7 @@ export const DRY_RUN_ACCOUNT = { export type TxField = [ name: string, type: FIELD_TYPES | Field, - prefix?: EncodingType | EncodingType[], + prefix?: Encoding | Encoding[], ]; /** @@ -86,11 +86,11 @@ export const PROTOCOL_VM_ABI = { }, } as const; -type PrefixType = Prefix extends EncodingType - ? EncodedData - : Prefix extends readonly EncodingType[] - ? EncodedData - : EncodedData; +type PrefixType = Prefix extends Encoding + ? Encoded.Generic + : Prefix extends readonly Encoding[] + ? Encoded.Generic + : Encoded.Generic; /** * @category transaction builder @@ -128,9 +128,9 @@ export enum FIELD_TYPES { stateTree, } -interface BuildFieldTypes { +interface BuildFieldTypes { [FIELD_TYPES.id]: PrefixType; - [FIELD_TYPES.ids]: Array>; + [FIELD_TYPES.ids]: Array>; [FIELD_TYPES.string]: string; [FIELD_TYPES.binary]: PrefixType; [FIELD_TYPES.bool]: Boolean; @@ -159,11 +159,11 @@ type UnionToIntersection = ? Intersection : never; type TxElem = readonly [string, FIELD_TYPES | Field] -| readonly [string, FIELD_TYPES, EncodingType | readonly EncodingType[]]; +| readonly [string, FIELD_TYPES, Encoding | readonly Encoding[]]; type BuildTxArgBySchemaType< Type extends FIELD_TYPES | Field, - Prefix extends undefined | EncodingType | readonly EncodingType[], + Prefix extends undefined | Encoding | readonly Encoding[], > = Type extends Field ? Parameters[0] @@ -206,8 +206,8 @@ export const TX_SCHEMA = { ['flags', uInt], ['nonce', shortUInt], ['balance', uInt], - ['gaContract', FIELD_TYPES.id, ['ct', 'nm']], - ['gaAuthFun', FIELD_TYPES.binary, 'cb'], + ['gaContract', FIELD_TYPES.id, [Encoding.ContractAddress, Encoding.Name]], + ['gaAuthFun', FIELD_TYPES.binary, Encoding.ContractBytearray], ], }, [Tag.SignedTx]: { @@ -220,8 +220,8 @@ export const TX_SCHEMA = { [Tag.SpendTx]: { 1: [ ...BASE_TX, - ['senderId', FIELD_TYPES.id, 'ak'], - ['recipientId', FIELD_TYPES.id, ['ak', 'nm']], + ['senderId', FIELD_TYPES.id, Encoding.AccountAddress], + ['recipientId', FIELD_TYPES.id, [Encoding.AccountAddress, Encoding.Name]], ['amount', coinAmount], ['fee', fee], ['ttl', shortUInt], @@ -232,9 +232,9 @@ export const TX_SCHEMA = { [Tag.NamePreclaimTx]: { 1: [ ...BASE_TX, - ['accountId', FIELD_TYPES.id, 'ak'], + ['accountId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], - ['commitmentId', FIELD_TYPES.id, 'cm'], + ['commitmentId', FIELD_TYPES.id, Encoding.Commitment], ['fee', fee], ['ttl', shortUInt], ], @@ -242,7 +242,7 @@ export const TX_SCHEMA = { [Tag.NameClaimTx]: { 2: [ ...BASE_TX, - ['accountId', FIELD_TYPES.id, 'ak'], + ['accountId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], ['name', name], ['nameSalt', uInt], @@ -254,7 +254,7 @@ export const TX_SCHEMA = { [Tag.NameUpdateTx]: { 1: [ ...BASE_TX, - ['accountId', FIELD_TYPES.id, 'ak'], + ['accountId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], ['nameId', nameId], ['nameTtl', uInt], @@ -267,10 +267,10 @@ export const TX_SCHEMA = { [Tag.NameTransferTx]: { 1: [ ...BASE_TX, - ['accountId', FIELD_TYPES.id, 'ak'], + ['accountId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], ['nameId', nameId], - ['recipientId', FIELD_TYPES.id, ['ak', 'nm']], + ['recipientId', FIELD_TYPES.id, [Encoding.AccountAddress, Encoding.Name]], ['fee', fee], ['ttl', shortUInt], ], @@ -278,7 +278,7 @@ export const TX_SCHEMA = { [Tag.NameRevokeTx]: { 1: [ ...BASE_TX, - ['accountId', FIELD_TYPES.id, 'ak'], + ['accountId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], ['nameId', nameId], ['fee', fee], @@ -288,21 +288,21 @@ export const TX_SCHEMA = { [Tag.Contract]: { 1: [ ...BASE_TX, - ['owner', FIELD_TYPES.id, 'ak'], + ['owner', FIELD_TYPES.id, Encoding.AccountAddress], ['ctVersion', FIELD_TYPES.ctVersion], - ['code', FIELD_TYPES.binary, 'cb'], - ['log', FIELD_TYPES.binary, 'cb'], + ['code', FIELD_TYPES.binary, Encoding.ContractBytearray], + ['log', FIELD_TYPES.binary, Encoding.ContractBytearray], ['active', FIELD_TYPES.bool], - ['referers', FIELD_TYPES.ids, 'ak'], + ['referers', FIELD_TYPES.ids, Encoding.AccountAddress], ['deposit', deposit], ], }, [Tag.ContractCreateTx]: { 1: [ ...BASE_TX, - ['ownerId', FIELD_TYPES.id, 'ak'], + ['ownerId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], - ['code', FIELD_TYPES.binary, 'cb'], + ['code', FIELD_TYPES.binary, Encoding.ContractBytearray], ['ctVersion', FIELD_TYPES.ctVersion], ['fee', fee], ['ttl', shortUInt], @@ -310,34 +310,34 @@ export const TX_SCHEMA = { ['amount', coinAmount], ['gasLimit', gasLimit], ['gasPrice', gasPrice], - ['callData', FIELD_TYPES.binary, 'cb'], + ['callData', FIELD_TYPES.binary, Encoding.ContractBytearray], ], }, [Tag.ContractCallTx]: { 1: [ ...BASE_TX, - ['callerId', FIELD_TYPES.id, 'ak'], + ['callerId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], - ['contractId', FIELD_TYPES.id, ['ct', 'nm']], + ['contractId', FIELD_TYPES.id, [Encoding.ContractAddress, Encoding.Name]], ['abiVersion', FIELD_TYPES.abiVersion], ['fee', fee], ['ttl', shortUInt], ['amount', coinAmount], ['gasLimit', gasLimit], ['gasPrice', gasPrice], - ['callData', FIELD_TYPES.binary, 'cb'], + ['callData', FIELD_TYPES.binary, Encoding.ContractBytearray], ], }, [Tag.ContractCall]: { 1: [ ...BASE_TX, - ['callerId', FIELD_TYPES.id, 'ak'], + ['callerId', FIELD_TYPES.id, Encoding.AccountAddress], ['callerNonce', shortUInt], ['height', shortUInt], - ['contractId', FIELD_TYPES.id, 'ct'], + ['contractId', FIELD_TYPES.id, Encoding.ContractAddress], ['gasPrice', gasPrice], ['gasUsed', shortUInt], - ['returnValue', FIELD_TYPES.binary, 'cb'], + ['returnValue', FIELD_TYPES.binary, Encoding.ContractBytearray], ['returnType', FIELD_TYPES.callReturnType], // TODO: add serialization for // :: [ {
:: id, [ :: binary() ], :: binary() } ] @@ -347,7 +347,7 @@ export const TX_SCHEMA = { [Tag.OracleRegisterTx]: { 1: [ ...BASE_TX, - ['accountId', FIELD_TYPES.id, 'ak'], + ['accountId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], ['queryFormat', FIELD_TYPES.string], ['responseFormat', FIELD_TYPES.string], @@ -362,7 +362,7 @@ export const TX_SCHEMA = { [Tag.OracleExtendTx]: { 1: [ ...BASE_TX, - ['oracleId', FIELD_TYPES.id, ['ok', 'nm']], + ['oracleId', FIELD_TYPES.id, [Encoding.OracleAddress, Encoding.Name]], ['nonce', shortUInt], ['oracleTtlType', FIELD_TYPES.ttlType], ['oracleTtlValue', shortUInt], @@ -373,9 +373,9 @@ export const TX_SCHEMA = { [Tag.OracleQueryTx]: { 1: [ ...BASE_TX, - ['senderId', FIELD_TYPES.id, 'ak'], + ['senderId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], - ['oracleId', FIELD_TYPES.id, ['ok', 'nm']], + ['oracleId', FIELD_TYPES.id, [Encoding.OracleAddress, Encoding.Name]], ['query', FIELD_TYPES.string], ['queryFee', coinAmount], ['queryTtlType', FIELD_TYPES.ttlType], @@ -389,9 +389,9 @@ export const TX_SCHEMA = { [Tag.OracleResponseTx]: { 1: [ ...BASE_TX, - ['oracleId', FIELD_TYPES.id, 'ok'], + ['oracleId', FIELD_TYPES.id, Encoding.OracleAddress], ['nonce', shortUInt], - ['queryId', FIELD_TYPES.binary, 'oq'], + ['queryId', FIELD_TYPES.binary, Encoding.OracleQueryId], ['response', FIELD_TYPES.string], ['responseTtlType', FIELD_TYPES.ttlType], ['responseTtlValue', shortUInt], @@ -402,9 +402,9 @@ export const TX_SCHEMA = { [Tag.ChannelCreateTx]: { 2: [ ...BASE_TX, - ['initiator', FIELD_TYPES.id, 'ak'], + ['initiator', FIELD_TYPES.id, Encoding.AccountAddress], ['initiatorAmount', uInt], - ['responder', FIELD_TYPES.id, 'ak'], + ['responder', FIELD_TYPES.id, Encoding.AccountAddress], ['responderAmount', uInt], ['channelReserve', uInt], ['lockPeriod', uInt], @@ -419,8 +419,8 @@ export const TX_SCHEMA = { [Tag.ChannelCloseMutualTx]: { 1: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], - ['fromId', FIELD_TYPES.id, 'ak'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], + ['fromId', FIELD_TYPES.id, Encoding.AccountAddress], ['initiatorAmountFinal', uInt], ['responderAmountFinal', uInt], ['ttl', shortUInt], @@ -431,8 +431,8 @@ export const TX_SCHEMA = { [Tag.ChannelCloseSoloTx]: { 1: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], - ['fromId', FIELD_TYPES.id, 'ak'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], + ['fromId', FIELD_TYPES.id, Encoding.AccountAddress], ['payload', FIELD_TYPES.binary, 'tx'], ['poi', FIELD_TYPES.binary, 'pi'], ['ttl', shortUInt], @@ -443,8 +443,8 @@ export const TX_SCHEMA = { [Tag.ChannelSlashTx]: { 1: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], - ['fromId', FIELD_TYPES.id, 'ak'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], + ['fromId', FIELD_TYPES.id, Encoding.AccountAddress], ['payload', FIELD_TYPES.binary, 'tx'], ['poi', FIELD_TYPES.binary, 'pi'], ['ttl', shortUInt], @@ -455,8 +455,8 @@ export const TX_SCHEMA = { [Tag.ChannelDepositTx]: { 1: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], - ['fromId', FIELD_TYPES.id, 'ak'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], + ['fromId', FIELD_TYPES.id, Encoding.AccountAddress], ['amount', uInt], ['ttl', shortUInt], ['fee', fee], @@ -468,8 +468,8 @@ export const TX_SCHEMA = { [Tag.ChannelWithdrawTx]: { 1: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], - ['toId', FIELD_TYPES.id, 'ak'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], + ['toId', FIELD_TYPES.id, Encoding.AccountAddress], ['amount', uInt], ['ttl', shortUInt], ['fee', fee], @@ -481,8 +481,8 @@ export const TX_SCHEMA = { [Tag.ChannelSettleTx]: { 1: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], - ['fromId', FIELD_TYPES.id, 'ak'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], + ['fromId', FIELD_TYPES.id, Encoding.AccountAddress], ['initiatorAmountFinal', uInt], ['responderAmountFinal', uInt], ['ttl', shortUInt], @@ -493,11 +493,11 @@ export const TX_SCHEMA = { [Tag.ChannelForceProgressTx]: { 1: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], - ['fromId', FIELD_TYPES.id, 'ak'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], + ['fromId', FIELD_TYPES.id, Encoding.AccountAddress], ['payload', FIELD_TYPES.binary, 'tx'], ['round', shortUInt], - ['update', FIELD_TYPES.binary, 'cb'], + ['update', FIELD_TYPES.binary, Encoding.ContractBytearray], ['stateHash', FIELD_TYPES.binary, 'st'], ['offChainTrees', FIELD_TYPES.stateTree], ['ttl', shortUInt], @@ -508,7 +508,7 @@ export const TX_SCHEMA = { [Tag.ChannelOffChainTx]: { 2: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], ['round', shortUInt], ['stateHash', FIELD_TYPES.binary, 'st'], ], @@ -516,8 +516,8 @@ export const TX_SCHEMA = { [Tag.Channel]: { 3: [ ...BASE_TX, - ['initiator', FIELD_TYPES.id, 'ak'], - ['responder', FIELD_TYPES.id, 'ak'], + ['initiator', FIELD_TYPES.id, Encoding.AccountAddress], + ['responder', FIELD_TYPES.id, Encoding.AccountAddress], ['channelAmount', uInt], ['initiatorAmount', uInt], ['responderAmount', uInt], @@ -529,15 +529,15 @@ export const TX_SCHEMA = { ['soloRound', uInt], ['lockPeriod', uInt], ['lockedUntil', uInt], - ['initiatorAuth', FIELD_TYPES.binary, 'cb'], - ['responderAuth', FIELD_TYPES.binary, 'cb'], + ['initiatorAuth', FIELD_TYPES.binary, Encoding.ContractBytearray], + ['responderAuth', FIELD_TYPES.binary, Encoding.ContractBytearray], ], }, [Tag.ChannelSnapshotSoloTx]: { 1: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], - ['fromId', FIELD_TYPES.id, 'ak'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], + ['fromId', FIELD_TYPES.id, Encoding.AccountAddress], ['payload', FIELD_TYPES.binary, 'tx'], ['ttl', shortUInt], ['fee', fee], @@ -547,43 +547,43 @@ export const TX_SCHEMA = { [Tag.ChannelOffChainUpdateTransfer]: { 1: [ ...BASE_TX, - ['from', FIELD_TYPES.id, 'ak'], - ['to', FIELD_TYPES.id, 'ak'], + ['from', FIELD_TYPES.id, Encoding.AccountAddress], + ['to', FIELD_TYPES.id, Encoding.AccountAddress], ['amount', uInt], ], }, [Tag.ChannelOffChainUpdateDeposit]: { 1: [ ...BASE_TX, - ['from', FIELD_TYPES.id, 'ak'], + ['from', FIELD_TYPES.id, Encoding.AccountAddress], ['amount', uInt], ], }, [Tag.ChannelOffChainUpdateWithdraw]: { 1: [ ...BASE_TX, - ['from', FIELD_TYPES.id, 'ak'], + ['from', FIELD_TYPES.id, Encoding.AccountAddress], ['amount', uInt], ], }, [Tag.ChannelOffChainUpdateCreateContract]: { 1: [ ...BASE_TX, - ['owner', FIELD_TYPES.id, 'ak'], + ['owner', FIELD_TYPES.id, Encoding.AccountAddress], ['ctVersion', FIELD_TYPES.ctVersion], - ['code', FIELD_TYPES.binary, 'cb'], + ['code', FIELD_TYPES.binary, Encoding.ContractBytearray], ['deposit', uInt], - ['callData', FIELD_TYPES.binary, 'cb'], + ['callData', FIELD_TYPES.binary, Encoding.ContractBytearray], ], }, [Tag.ChannelOffChainUpdateCallContract]: { 1: [ ...BASE_TX, - ['caller', FIELD_TYPES.id, 'ak'], - ['contract', FIELD_TYPES.id, 'ct'], + ['caller', FIELD_TYPES.id, Encoding.AccountAddress], + ['contract', FIELD_TYPES.id, Encoding.ContractAddress], ['abiVersion', FIELD_TYPES.abiVersion], ['amount', uInt], - ['callData', FIELD_TYPES.binary, 'cb'], + ['callData', FIELD_TYPES.binary, Encoding.ContractBytearray], ['callStack', FIELD_TYPES.callStack], ['gasPrice', gasPrice], ['gasLimit', gasLimit], @@ -592,10 +592,10 @@ export const TX_SCHEMA = { [Tag.ChannelClientReconnectTx]: { 1: [ ...BASE_TX, - ['channelId', FIELD_TYPES.id, 'ch'], + ['channelId', FIELD_TYPES.id, Encoding.Channel], ['round', shortUInt], ['role', FIELD_TYPES.string], - ['pubkey', FIELD_TYPES.id, 'ak'], + ['pubkey', FIELD_TYPES.id, Encoding.AccountAddress], ], }, [Tag.TreesPoi]: { @@ -672,23 +672,23 @@ export const TX_SCHEMA = { [Tag.GaAttachTx]: { 1: [ ...BASE_TX, - ['ownerId', FIELD_TYPES.id, 'ak'], + ['ownerId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], - ['code', FIELD_TYPES.binary, 'cb'], + ['code', FIELD_TYPES.binary, Encoding.ContractBytearray], ['authFun', FIELD_TYPES.rawBinary], ['ctVersion', FIELD_TYPES.ctVersion], ['fee', fee], ['ttl', shortUInt], ['gasLimit', gasLimit], ['gasPrice', gasPrice], - ['callData', FIELD_TYPES.binary, 'cb'], + ['callData', FIELD_TYPES.binary, Encoding.ContractBytearray], ], }, [Tag.GaMetaTx]: { 2: [ ...BASE_TX, - ['gaId', FIELD_TYPES.id, 'ak'], - ['authData', FIELD_TYPES.binary, 'cb'], + ['gaId', FIELD_TYPES.id, Encoding.AccountAddress], + ['authData', FIELD_TYPES.binary, Encoding.ContractBytearray], ['abiVersion', FIELD_TYPES.abiVersion], ['fee', fee], ['gasLimit', gasLimit], @@ -699,7 +699,7 @@ export const TX_SCHEMA = { [Tag.PayingForTx]: { 1: [ ...BASE_TX, - ['payerId', FIELD_TYPES.id, 'ak'], + ['payerId', FIELD_TYPES.id, Encoding.AccountAddress], ['nonce', shortUInt], ['fee', fee], ['tx', FIELD_TYPES.rlpBinary], diff --git a/src/tx/index.ts b/src/tx/index.ts index ede99a75e8..bf5221720a 100644 --- a/src/tx/index.ts +++ b/src/tx/index.ts @@ -31,7 +31,7 @@ import { ArgumentError, UnsupportedProtocolError, UnknownTxError, InvalidTxParamsError, } from '../utils/errors'; import Node from '../Node'; -import { EncodedData } from '../utils/encoder'; +import { Encoded } from '../utils/encoder'; import { buildTx as syncBuildTx, unpackTx } from './builder/index'; import { isKeyOfObject } from '../utils/other'; import { AE_AMOUNT_FORMATS } from '../utils/amount-formatter'; @@ -103,7 +103,7 @@ export async function prepareTxParams( } interface PrepareTxParamsOptions extends Pick { - senderId: EncodedData<'ak'>; + senderId: Encoded.AccountAddress; absoluteTtl?: boolean; strategy?: 'continuity' | 'max'; onNode: Node; @@ -125,11 +125,12 @@ export async function _buildTx( denomination?: AE_AMOUNT_FORMATS; absoluteTtl?: boolean; } - & (TxType extends Tag.OracleExtendTx | Tag.OracleResponseTx ? { callerId: EncodedData<'ak'> } : {}) + & (TxType extends Tag.OracleExtendTx | Tag.OracleResponseTx + ? { callerId: Encoded.AccountAddress } : {}) & (TxType extends Tag.ContractCreateTx | Tag.GaAttachTx ? { ctVersion?: CtVersion } : {}) & (TxType extends Tag.ContractCallTx | Tag.OracleRegisterTx ? { abiVersion?: ABI_VERSIONS } : {}), -): Promise> { +): Promise { // TODO: avoid this assertion const params = _params as unknown as TxParamsCommon & { onNode: Node }; let senderKey: keyof TxParamsCommon | ''; diff --git a/src/tx/validator.ts b/src/tx/validator.ts index 9ef0fca8fd..db08521cdd 100644 --- a/src/tx/validator.ts +++ b/src/tx/validator.ts @@ -1,18 +1,25 @@ import BigNumber from 'bignumber.js'; -import { verify, hash } from '../utils/crypto'; +import { hash, verify } from '../utils/crypto'; import { - PROTOCOL_VM_ABI, RawTxObject, TxSchema, TxParamsCommon, TxTypeSchemas, CtVersion, + CtVersion, + PROTOCOL_VM_ABI, + RawTxObject, + TxParamsCommon, + TxSchema, + TxTypeSchemas, } from './builder/schema'; import { Tag } from './builder/constants'; import { TxUnpacked, unpackTx } from './builder'; import { UnsupportedProtocolError } from '../utils/errors'; import { concatBuffers, isKeyOfObject } from '../utils/other'; -import { encode, decode, EncodedData } from '../utils/encoder'; +import { + decode, encode, Encoded, Encoding, +} from '../utils/encoder'; import Node from '../Node'; interface Account { balance: bigint; - id: EncodedData<'ak'>; + id: Encoded.AccountAddress; nonce: number; } @@ -36,7 +43,7 @@ type Validator = ( nameFee?: number; ctVersion?: Partial; abiVersion?: number; - contractId?: EncodedData<'ct'>; + contractId?: Encoded.ContractAddress; }, options: { account?: Account; @@ -53,13 +60,13 @@ const validators: Validator[] = []; const getSenderAddress = ( tx: TxParamsCommon | RawTxObject, -): EncodedData<'ak'> | undefined => [ +): Encoded.AccountAddress | undefined => [ 'senderId', 'accountId', 'ownerId', 'callerId', 'oracleId', 'fromId', 'initiator', 'gaId', 'payerId', ] .map((key: keyof TxSchema) => tx[key]) .filter((a) => a) - .map((a) => a?.toString().replace(/^ok_/, 'ak_'))[0] as EncodedData<'ak'> | undefined; + .map((a) => a?.toString().replace(/^ok_/, 'ak_'))[0] as Encoded.AccountAddress | undefined; /** * Transaction Validator @@ -73,7 +80,7 @@ const getSenderAddress = ( * @example const errors = await verifyTransaction(transaction, node) */ export default async function verifyTransaction( - transaction: EncodedData<'tx' | 'pi'>, + transaction: Encoded.Transaction | Encoded.Poi, node: Node, parentTxTypes: Tag[] = [], ): Promise { @@ -86,7 +93,7 @@ export default async function verifyTransaction( : node.getAccountByPubkey(address) .catch(() => ({ id: address, balance: 0n, nonce: 0 })) // TODO: remove after fixing https://github.com/aeternity/aepp-sdk-js/issues/1537 - .then((acc) => ({ ...acc, id: acc.id as EncodedData<'ak'> })), + .then((acc) => ({ ...acc, id: acc.id as Encoded.AccountAddress })), node.getCurrentKeyBlockHeight(), node.getNodeInfo(), ]); @@ -125,7 +132,7 @@ validators.push( async ({ encodedTx, tx }, { node, parentTxTypes, txType }) => { if ((encodedTx ?? tx) === undefined) return []; return verifyTransaction( - encode((encodedTx ?? tx).rlpEncoded, 'tx'), + encode((encodedTx ?? tx).rlpEncoded, Encoding.Transaction), node, [...parentTxTypes, txType], ); @@ -202,7 +209,7 @@ validators.push( }, async ({ contractId }, { txType, node }) => { if (Tag.ContractCallTx !== txType) return []; - contractId = contractId as EncodedData<'ct'>; + contractId = contractId as Encoded.ContractAddress; try { const { active } = await node.getContract(contractId); if (active) return []; diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts index f4b5cbe0d1..5a34d713aa 100644 --- a/src/utils/crypto.ts +++ b/src/utils/crypto.ts @@ -22,7 +22,7 @@ import { encode as varuintEncode } from 'varuint-bitcoin'; import { str2buf } from './bytes'; import { concatBuffers } from './other'; import { - encode, decode, sha256hash, EncodedData, EncodingType, + decode, encode, Encoded, Encoding, sha256hash, } from './encoder'; const Ecb = aesjs.ModeOfOperation.ecb; @@ -32,10 +32,10 @@ const Ecb = aesjs.ModeOfOperation.ecb; * @param secret - Private key * @returns Public key encoded as address */ -export function getAddressFromPriv(secret: string | Uint8Array): EncodedData<'ak'> { +export function getAddressFromPriv(secret: string | Uint8Array): Encoded.AccountAddress { const secretBuffer = typeof secret === 'string' ? str2buf(secret) : secret; const keys = nacl.sign.keyPair.fromSecretKey(secretBuffer); - return encode(keys.publicKey, 'ak'); + return encode(keys.publicKey, Encoding.AccountAddress); } /** @@ -44,9 +44,12 @@ export function getAddressFromPriv(secret: string | Uint8Array): EncodedData<'ak * @param prefix - Transaction prefix. Default: 'ak' * @returns is valid */ -export function isAddressValid(address: string, prefix: EncodingType = 'ak'): boolean { +export function isAddressValid( + address: string, + prefix: Encoding = Encoding.AccountAddress, +): boolean { try { - decode(address as EncodedData); + decode(address as Encoded.Generic); return true; } catch (e) { return false; @@ -90,10 +93,13 @@ export function hash(input: Data): Buffer { * @param nonce - Round when contract was created * @returns Contract address */ -export function encodeContractAddress(owner: EncodedData<'ak'>, nonce: number): EncodedData<'ct'> { +export function encodeContractAddress( + owner: Encoded.AccountAddress, + nonce: number, +): Encoded.ContractAddress { const publicKey = decode(owner); const binary = concatBuffers([publicKey, encodeUnsigned(nonce)]); - return encode(hash(binary), 'ct'); + return encode(hash(binary), Encoding.ContractAddress); } // KEY-PAIR HELPERS @@ -113,9 +119,11 @@ export function generateKeyPairFromSecret(secret: Uint8Array): SignKeyPair { * @returns Key pair */ export function generateKeyPair(raw: true): { publicKey: Buffer; secretKey: Buffer }; -export function generateKeyPair(raw?: false): { publicKey: EncodedData<'ak'>; secretKey: string }; +export function generateKeyPair(raw?: false): { + publicKey: Encoded.AccountAddress; secretKey: string; +}; export function generateKeyPair(raw = false): { - publicKey: EncodedData<'ak'> | Buffer; + publicKey: Encoded.AccountAddress | Buffer; secretKey: string | Buffer; } { const keyPair = nacl.sign.keyPair(); @@ -129,7 +137,7 @@ export function generateKeyPair(raw = false): { }; } return { - publicKey: encode(publicBuffer, 'ak'), + publicKey: encode(publicBuffer, Encoding.AccountAddress), secretKey: secretBuffer.toString('hex'), }; } diff --git a/src/utils/encoder-types.ts b/src/utils/encoder-types.ts new file mode 100644 index 0000000000..be4c4d99e0 --- /dev/null +++ b/src/utils/encoder-types.ts @@ -0,0 +1,63 @@ +/** + * @category transaction builder + * @see {@link https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md} + * @see {@link https://github.com/aeternity/aeserialization/blob/eb68fe331bd476910394966b7f5ede7a74d37e35/src/aeser_api_encoder.erl#L205-L230} + */ +export enum Encoding { + KeyBlockHash = 'kh', + MicroBlockHash = 'mh', + BlockPofHash = 'bf', + BlockTxHash = 'bx', + BlockStateHash = 'bs', + Channel = 'ch', + ContractAddress = 'ct', + ContractBytearray = 'cb', + ContractStoreKey = 'ck', + ContractStoreValue = 'cv', + Transaction = 'tx', + TxHash = 'th', + OracleAddress = 'ok', + OracleQuery = 'ov', + OracleQueryId = 'oq', + OracleResponse = 'or', + AccountAddress = 'ak', + Signature = 'sg', + Commitment = 'cm', + PeerPubkey = 'pp', + Name = 'nm', + State = 'st', + Poi = 'pi', + StateTrees = 'ss', + CallStateTree = 'cs', + Bytearray = 'ba', +} + +export type KeyBlockHash = `${Encoding.KeyBlockHash}_${string}`; +export type MicroBlockHash = `${Encoding.MicroBlockHash}_${string}`; +export type BlockPofHash = `${Encoding.BlockPofHash}_${string}`; +export type BlockTxHash = `${Encoding.BlockTxHash}_${string}`; +export type BlockStateHash = `${Encoding.BlockStateHash}_${string}`; +export type Channel = `${Encoding.Channel}_${string}`; +export type ContractAddress = `${Encoding.ContractAddress}_${string}`; +export type ContractBytearray = `${Encoding.ContractBytearray}_${string}`; +export type ContractStoreKey = `${Encoding.ContractStoreKey}_${string}`; +export type ContractStoreValue = `${Encoding.ContractStoreValue}_${string}`; +export type Transaction = `${Encoding.Transaction}_${string}`; +export type TxHash = `${Encoding.TxHash}_${string}`; +export type OracleAddress = `${Encoding.OracleAddress}_${string}`; +export type OracleQuery = `${Encoding.OracleQuery}_${string}`; +export type OracleQueryId = `${Encoding.OracleQueryId}_${string}`; +export type OracleResponse = `${Encoding.OracleResponse}_${string}`; +export type AccountAddress = `${Encoding.AccountAddress}_${string}`; +export type Signature = `${Encoding.Signature}_${string}`; +export type Commitment = `${Encoding.Commitment}_${string}`; +export type PeerPubkey = `${Encoding.PeerPubkey}_${string}`; +export type Name = `${Encoding.Name}_${string}`; +export type State = `${Encoding.State}_${string}`; +export type Poi = `${Encoding.Poi}_${string}`; +export type StateTrees = `${Encoding.StateTrees}_${string}`; +export type CallStateTree = `${Encoding.CallStateTree}_${string}`; +export type Bytearray = `${Encoding.Bytearray}_${string}`; + +export type Generic = `${Type}_${string}`; +export type Any = `${Encoding}_${string}`; diff --git a/src/utils/encoder.ts b/src/utils/encoder.ts index 2a0eca5d10..d94fd117f3 100644 --- a/src/utils/encoder.ts +++ b/src/utils/encoder.ts @@ -6,7 +6,11 @@ import { InvalidChecksumError, PayloadLengthError, } from './errors'; -import { concatBuffers } from './other'; +import { concatBuffers, isKeyOfObject } from './other'; +import * as Encoded from './encoder-types'; +import { Encoding } from './encoder-types'; + +export { Encoded, Encoding }; /** * Calculate SHA256 hash of `input` @@ -17,35 +21,63 @@ export function sha256hash(input: Uint8Array | string): Buffer { return new Sha256().update(input).digest(); } -// based on https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md -const base64Types = ['ba', 'cb', 'or', 'ov', 'pi', 'ss', 'cs', 'ck', 'cv', 'st', 'tx'] as const; -const base58Types = ['ak', 'bf', 'bs', 'bx', 'ch', 'cm', 'ct', 'kh', 'mh', 'nm', 'ok', 'oq', 'pp', 'sg', 'th'] as const; - -export type EncodingType = typeof base64Types[number] | typeof base58Types[number]; -export type EncodedData = `${Type}_${string}`; +/** + * @see {@link https://github.com/aeternity/aeserialization/blob/eb68fe331bd476910394966b7f5ede7a74d37e35/src/aeser_api_encoder.erl#L177-L202} + */ +const base64Types = [ + Encoding.ContractBytearray, + Encoding.ContractStoreKey, + Encoding.ContractStoreValue, + Encoding.Transaction, + Encoding.OracleQuery, + Encoding.OracleResponse, + Encoding.State, + Encoding.Poi, + Encoding.StateTrees, + Encoding.CallStateTree, + Encoding.Bytearray, +] as const; +const base58Types = [ + Encoding.KeyBlockHash, + Encoding.MicroBlockHash, + Encoding.BlockPofHash, + Encoding.BlockTxHash, + Encoding.BlockStateHash, + Encoding.Channel, + Encoding.ContractAddress, + Encoding.TxHash, + Encoding.OracleAddress, + Encoding.OracleQueryId, + Encoding.AccountAddress, + Encoding.Signature, + Encoding.Commitment, + Encoding.PeerPubkey, + Encoding.Name, +] as const; /** * @see {@link https://github.com/aeternity/aeserialization/blob/eb68fe331bd476910394966b7f5ede7a74d37e35/src/aeser_api_encoder.erl#L261-L286} */ -const byteSizeForType: { [name in EncodingType]?: number } = { - kh: 32, - mh: 32, - bf: 32, - bx: 32, - bs: 32, - ch: 32, - ct: 32, - th: 32, - ok: 32, - oq: 32, - ak: 32, - sg: 64, - cm: 32, - pp: 32, - st: 32, +const byteSizeForType = { + [Encoding.KeyBlockHash]: 32, + [Encoding.MicroBlockHash]: 32, + [Encoding.BlockPofHash]: 32, + [Encoding.BlockTxHash]: 32, + [Encoding.BlockStateHash]: 32, + [Encoding.Channel]: 32, + [Encoding.ContractAddress]: 32, + [Encoding.TxHash]: 32, + [Encoding.OracleAddress]: 32, + [Encoding.OracleQueryId]: 32, + [Encoding.AccountAddress]: 32, + [Encoding.Signature]: 64, + [Encoding.Commitment]: 32, + [Encoding.PeerPubkey]: 32, + [Encoding.State]: 32, } as const; -function ensureValidLength(data: Uint8Array, type: EncodingType): void { +function ensureValidLength(data: Uint8Array, type: Encoding): void { + if (!isKeyOfObject(type, byteSizeForType)) return; const reqLen = byteSizeForType[type]; if (reqLen == null || data.length === reqLen) return; throw new PayloadLengthError(`Payload should be ${reqLen} bytes, got ${data.length} instead`); @@ -71,7 +103,7 @@ const base58 = { decode: (string: string) => getPayload(Buffer.from(bs58Decode(string))), }; -const parseType = (maybeType: unknown): [EncodingType, typeof base64] => { +const parseType = (maybeType: unknown): [Encoding, typeof base64] => { const base64Type = base64Types.find((t) => t === maybeType); if (base64Type != null) return [base64Type, base64]; const base58Type = base58Types.find((t) => t === maybeType); @@ -85,7 +117,7 @@ const parseType = (maybeType: unknown): [EncodingType, typeof base64] => { * (ex tx_..., sg_..., ak_....) * @returns Decoded data */ -export function decode(data: EncodedData): Buffer { +export function decode(data: Encoded.Any): Buffer { const [prefix, encodedPayload, extra] = data.split('_'); if (encodedPayload == null) throw new DecodeError(`Encoded string missing payload: ${data}`); if (extra != null) throw new DecodeError(`Encoded string have extra parts: ${data}`); @@ -101,7 +133,10 @@ export function decode(data: EncodedData): Buffer { * @param type - Prefix of Transaction * @returns Encoded string Base58check or Base64check data */ -export function encode(data: Uint8Array, type: Type): EncodedData { +export function encode( + data: Uint8Array, + type: Type, +): Encoded.Generic { const [, encoder] = parseType(type); ensureValidLength(data, type); return `${type}_${encoder.encode(data)}`; diff --git a/src/utils/hd-wallet.ts b/src/utils/hd-wallet.ts index a36a14cb55..4e43f93c8b 100644 --- a/src/utils/hd-wallet.ts +++ b/src/utils/hd-wallet.ts @@ -2,7 +2,7 @@ import nacl from 'tweetnacl'; import { full as hmac } from 'tweetnacl-auth'; import { fromString } from 'bip32-path'; import { decryptKey, encryptKey } from './crypto'; -import { encode } from './encoder'; +import { encode, Encoding } from './encoder'; import { CryptographyError } from './errors'; import { bytesToHex } from './bytes'; import { concatBuffers } from './other'; @@ -93,7 +93,7 @@ function formatAccount(keys: nacl.SignKeyPair): Account { const { secretKey, publicKey } = keys; return { secretKey: bytesToHex(secretKey), - publicKey: encode(publicKey, 'ak'), + publicKey: encode(publicKey, Encoding.AccountAddress), }; } diff --git a/src/utils/other.ts b/src/utils/other.ts index 5995346e25..02980eaecc 100644 --- a/src/utils/other.ts +++ b/src/utils/other.ts @@ -27,12 +27,18 @@ export const concatBuffers = isWebpack4Buffer /** * Object key type guard - * @param key - Object key - * @param object - object + * @param key - Maybe object key + * @param object - Object */ -export function isKeyOfObject( - key: string | number | symbol, - object: T, -): key is keyof T { +export function isKeyOfObject(key: string | number | symbol, object: T): key is keyof T { return key in object; } + +/** + * Array item type guard + * @param item - Maybe array item + * @param array - Array + */ +export function isItemOfArray(item: any, array: readonly T[]): item is T { + return array.includes(item); +} diff --git a/test/integration/accounts.ts b/test/integration/accounts.ts index 89b3545f84..ead759afa3 100644 --- a/test/integration/accounts.ts +++ b/test/integration/accounts.ts @@ -24,7 +24,7 @@ import { generateKeyPair, AE_AMOUNT_FORMATS, UnavailableAccountError, TypeError, ArgumentError, InvalidKeypairError, UnexpectedTsError, } from '../../src'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded } from '../../src/utils/encoder'; describe('Accounts', () => { let aeSdk: AeSdk; @@ -152,7 +152,7 @@ describe('Accounts', () => { }); describe('Make operation on specific account without changing of current account', () => { - let address: EncodedData<'ak'>; + let address: Encoded.AccountAddress; before(async () => { address = await aeSdk.address(); diff --git a/test/integration/chain.ts b/test/integration/chain.ts index 20c24e5c76..c28739f085 100644 --- a/test/integration/chain.ts +++ b/test/integration/chain.ts @@ -22,7 +22,7 @@ import { getSdk } from '.'; import { generateKeyPair, AeSdk, Tag, UnexpectedTsError, } from '../../src'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded } from '../../src/utils/encoder'; describe('Node Chain', () => { let aeSdk: AeSdk; @@ -68,7 +68,9 @@ describe('Node Chain', () => { it('Get key block', async () => { const { keyBlock } = await aeSdkWithoutAccount.getCurrentGeneration(); - const keyBlockByHash = await aeSdkWithoutAccount.getKeyBlock(keyBlock.hash as EncodedData<'kh'>); + // TODO type should be corrected in node api + const keyBlockByHash = await aeSdkWithoutAccount + .getKeyBlock(keyBlock.hash as Encoded.KeyBlockHash); const keyBlockByHeight = await aeSdkWithoutAccount.getKeyBlock(keyBlock.height); keyBlockByHash.should.be.an('object'); keyBlockByHeight.should.be.an('object'); @@ -76,7 +78,9 @@ describe('Node Chain', () => { it('Get generation', async () => { const { keyBlock } = await aeSdkWithoutAccount.getCurrentGeneration(); - const genByHash = await aeSdkWithoutAccount.getGeneration(keyBlock.hash as EncodedData<'kh'>); + // TODO type should be corrected in node api + const genByHash = await aeSdkWithoutAccount + .getGeneration(keyBlock.hash as Encoded.KeyBlockHash); const genByHeight = await aeSdkWithoutAccount.getGeneration(keyBlock.height); genByHash.should.be.an('object'); genByHeight.should.be.an('object'); @@ -112,7 +116,7 @@ describe('Node Chain', () => { }); const accounts = new Array(10).fill(undefined).map(() => generateKeyPair()); - const transactions: Array> = []; + const transactions: Encoded.TxHash[] = []; it('multiple spends from one account', async () => { const { nextNonce } = await aeSdk.api.getAccountNextNonce(await aeSdk.address()); diff --git a/test/integration/channel.ts b/test/integration/channel.ts index 900261294b..9a1a37855a 100644 --- a/test/integration/channel.ts +++ b/test/integration/channel.ts @@ -29,7 +29,7 @@ import { pause } from '../../src/utils/other'; import Channel from '../../src/channel'; import { ChannelOptions, send } from '../../src/channel/internal'; import MemoryAccount from '../../src/account/Memory'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded, Encoding } from '../../src/utils/encoder'; import { appendSignature } from '../../src/channel/handlers'; const wsUrl = process.env.TEST_WS_URL ?? 'ws://localhost:3014/channel'; @@ -61,7 +61,7 @@ describe('Channel', () => { let responderShouldRejectUpdate: number | boolean; let existingChannelId: string; let offchainTx: string; - let contractAddress: EncodedData<'ct'>; + let contractAddress: Encoded.ContractAddress; let callerNonce: number; let contract: any; const initiatorSign: sinon.SinonSpy = sinon @@ -215,7 +215,7 @@ describe('Channel', () => { }]), }), ); - const { txType } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); + const { txType } = unpackTx(sign.firstCall.args[0] as Encoded.Transaction); txType.should.equal(Tag.ChannelOffChainTx); expect(sign.firstCall.args[1]).to.eql({ @@ -271,7 +271,7 @@ describe('Channel', () => { }]), }), ); - const { txType } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); + const { txType } = unpackTx(sign.firstCall.args[0] as Encoded.Transaction); txType.should.equal(Tag.ChannelOffChainTx); expect(sign.firstCall.args[1]).to.eql({ updates: [ @@ -333,28 +333,32 @@ describe('Channel', () => { }); it('can get proof of inclusion', async () => { - const initiatorAddr: EncodedData<'ak'> = await aeSdkInitiatior.address(); - const responderAddr: EncodedData<'ak'> = await aeSdkResponder.address(); + const initiatorAddr: Encoded.AccountAddress = await aeSdkInitiatior.address(); + const responderAddr: Encoded.AccountAddress = await aeSdkResponder.address(); const params = { accounts: [initiatorAddr, responderAddr] }; - const initiatorPoi: EncodedData<'pi'> = await initiatorCh.poi(params); + const initiatorPoi: Encoded.Poi = await initiatorCh.poi(params); expect(initiatorPoi).to.be.equal(await responderCh.poi(params)); initiatorPoi.should.be.a('string'); const unpackedInitiatorPoi = unpackTx(initiatorPoi, Tag.TreesPoi); // TODO: move to `unpackTx`/`MPTree` - function getAccountBalance(address: EncodedData<'ak'>): string { + function getAccountBalance(address: Encoded.AccountAddress): string { const addressHex = decode(address).toString('hex'); const treeNode = unpackedInitiatorPoi.tx.accounts[0].get(addressHex); assertNotNull(treeNode); - const { balance, ...account } = unpackTx(encode(treeNode, 'tx'), Tag.Account).tx; + const { balance, ...account } = unpackTx( + encode(treeNode, Encoding.Transaction), + Tag.Account, + ).tx; expect(account).to.eql({ tag: 10, VSN: 1, nonce: 0 }); return balance.toString(); } expect(getAccountBalance(initiatorAddr)).to.eql('89999999999999999997'); expect(getAccountBalance(responderAddr)).to.eql('110000000000000000003'); - expect(buildTx(unpackedInitiatorPoi.tx, unpackedInitiatorPoi.txType, { prefix: 'pi' }).tx) - .to.equal(initiatorPoi); + expect( + buildTx(unpackedInitiatorPoi.tx, unpackedInitiatorPoi.txType, { prefix: Encoding.Poi }).tx, + ).to.equal(initiatorPoi); }); it('can send a message', async () => { @@ -419,7 +423,7 @@ describe('Channel', () => { }], }), ); - const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); + const { txType, tx } = unpackTx(sign.firstCall.args[0] as Encoded.Transaction); txType.should.equal(Tag.ChannelWithdrawTx); tx.should.eql({ ...tx, @@ -472,7 +476,7 @@ describe('Channel', () => { }], }), ); - const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); + const { txType, tx } = unpackTx(sign.firstCall.args[0] as Encoded.Transaction); txType.should.equal(Tag.ChannelWithdrawTx); tx.should.eql({ ...tx, @@ -549,7 +553,7 @@ describe('Channel', () => { }]), }), ); - const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); + const { txType, tx } = unpackTx(sign.firstCall.args[0] as Encoded.Transaction); txType.should.equal(Tag.ChannelDepositTx); tx.should.eql({ ...tx, @@ -590,7 +594,7 @@ describe('Channel', () => { }], }), ); - const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); + const { txType, tx } = unpackTx(sign.firstCall.args[0] as Encoded.Transaction); txType.should.equal(Tag.ChannelDepositTx); tx.should.eql({ ...tx, @@ -635,7 +639,7 @@ describe('Channel', () => { ); sinon.assert.calledOnce(sign); sinon.assert.calledWithExactly(sign, sinon.match.string); - const { txType, tx } = unpackTx(sign.firstCall.args[0] as EncodedData<'tx'>); + const { txType, tx } = unpackTx(sign.firstCall.args[0] as Encoded.Transaction); txType.should.equal(Tag.ChannelCloseMutualTx); tx.should.eql({ ...tx, @@ -919,7 +923,7 @@ describe('Channel', () => { it('can get balances', async () => { const initiatorAddr = await aeSdkInitiatior.address(); const responderAddr = await aeSdkResponder.address(); - const contractAddr = encode(decode(contractAddress), 'ak'); + const contractAddr = encode(decode(contractAddress), Encoding.AccountAddress); const addresses = [initiatorAddr, responderAddr, contractAddr]; const balances = await initiatorCh.balances(addresses); balances.should.be.an('object'); @@ -1160,7 +1164,7 @@ describe('Channel', () => { 100, async (transaction) => appendSignature( await aeSdkResponder.signTransaction(transaction), - async (tx) => (aeSdkInitiatior.signTransaction(tx) as Promise>), + async (tx) => (aeSdkInitiatior.signTransaction(tx) as Promise), ), ); result.accepted.should.equal(true); diff --git a/test/integration/contract-aci.ts b/test/integration/contract-aci.ts index f58b7819b9..ed7c4a35cb 100644 --- a/test/integration/contract-aci.ts +++ b/test/integration/contract-aci.ts @@ -32,7 +32,7 @@ import { IllegalArgumentError, } from '../../src'; import { getSdk } from '.'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded } from '../../src/utils/encoder'; import { ContractInstance } from '../../src/contract/aci'; import { Aci } from '../../src/apis/compiler'; @@ -145,9 +145,9 @@ const notExistingContractAddress = 'ct_ptREMvyDbSh1d38t4WgYgac5oLsa2v9xwYFnG7eUW describe('Contract instance', () => { let aeSdk: AeSdk; let testContract: ContractInstance; - let testContractAddress: EncodedData<'ct'>; + let testContractAddress: Encoded.ContractAddress; let testContractAci: Aci; - let testContractBytecode: EncodedData<'cb'>; + let testContractBytecode: Encoded.ContractBytearray; before(async () => { aeSdk = await getSdk(2); @@ -436,11 +436,11 @@ describe('Contract instance', () => { }); const getDuplicateLog = (): Array<{ - address: EncodedData<'ct'>; - data: EncodedData<'cb'>; + address: Encoded.ContractAddress; + data: Encoded.ContractBytearray; topics: Array; }> => [{ - address: remoteContract.deployInfo.address as EncodedData<'ct'>, + address: remoteContract.deployInfo.address as Encoded.ContractAddress, data: 'cb_Xfbg4g==', topics: [ '28631352549432199952459007654025571262660118571086898449909844428770770966435', diff --git a/test/integration/contract.ts b/test/integration/contract.ts index 7f25bccfbe..62830c34a9 100644 --- a/test/integration/contract.ts +++ b/test/integration/contract.ts @@ -22,7 +22,7 @@ import { IllegalArgumentError, NodeInvocationError, commitmentHash, decode, encode, DRY_RUN_ACCOUNT, messageToHash, genSalt, UnexpectedTsError, AeSdk, } from '../../src'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded, Encoding } from '../../src/utils/encoder'; import { ContractInstance } from '../../src/contract/aci'; const identityContract = ` @@ -117,7 +117,7 @@ contract Sign = describe('Contract', () => { let aeSdk: AeSdk; - let bytecode: EncodedData<'cb'>; + let bytecode: Encoded.ContractBytearray; let contract: ContractInstance; let deployed: ContractInstance['deployInfo']; @@ -128,7 +128,7 @@ describe('Contract', () => { it('compiles Sophia code', async () => { bytecode = (await aeSdk.compilerApi.compileContract({ code: identityContract, options: {}, - })).bytecode as EncodedData<'cb'>; + })).bytecode as Encoded.ContractBytearray; expect(bytecode).to.satisfy((b: string) => b.startsWith('cb_')); }); @@ -271,7 +271,7 @@ describe('Contract', () => { it('compile', async () => { bytecode = (await aeSdk.compilerApi.compileContract({ code: identityContract, options: {}, - })).bytecode as EncodedData<'cb'>; + })).bytecode as Encoded.ContractBytearray; expect(bytecode.split('_')[0]).to.be.equal('cb'); }); @@ -321,11 +321,11 @@ describe('Contract', () => { }); describe('AENS operation delegation', () => { - let contractId: EncodedData<'ct'>; + let contractId: Encoded.ContractAddress; const name = randomName(15); const salt = genSalt(); - let owner: EncodedData<'ak'>; - let newOwner: EncodedData<'ak'>; + let owner: Encoded.AccountAddress; + let newOwner: Encoded.AccountAddress; let delegationSignature: string; before(async () => { @@ -391,10 +391,10 @@ describe('Contract', () => { }); describe('Oracle operation delegation', () => { - let contractId: EncodedData<'ct'>; - let address: EncodedData<'ak'>; + let contractId: Encoded.ContractAddress; + let address: Encoded.AccountAddress; let oracle: Awaited>; - let oracleId: EncodedData<'ok'>; + let oracleId: Encoded.OracleAddress; let queryObject: Awaited>; let delegationSignature: string; const queryFee = 500000; @@ -406,7 +406,7 @@ describe('Contract', () => { if (contract.deployInfo.address == null) throw new UnexpectedTsError(); contractId = contract.deployInfo.address; address = await aeSdk.address(); - oracleId = encode(decode(address), 'ok'); + oracleId = encode(decode(address), Encoding.OracleAddress); }); it('registers', async () => { @@ -446,7 +446,8 @@ describe('Contract', () => { const response = await contract.methods.respond(oracle.id, queryObject.id, respondSig, r); response.result.returnType.should.be.equal('ok'); // TODO type should be corrected in node api - const queryObject2 = await aeSdk.getQueryObject(oracle.id, queryObject.id as EncodedData<'oq'>); + const queryObject2 = await aeSdk + .getQueryObject(oracle.id, queryObject.id as Encoded.OracleQueryId); queryObject2.decodedResponse.should.be.equal(r); }); }); diff --git a/test/integration/ga.ts b/test/integration/ga.ts index 09bf4da4b2..56ebc2bcff 100644 --- a/test/integration/ga.ts +++ b/test/integration/ga.ts @@ -21,7 +21,7 @@ import { getSdk } from '.'; import { AeSdk, Tag, genSalt, generateKeyPair, unpackTx, } from '../../src'; -import { encode, EncodedData } from '../../src/utils/encoder'; +import { encode, Encoded, Encoding } from '../../src/utils/encoder'; import MemoryAccount from '../../src/account/Memory'; import { ContractInstance } from '../../src/contract/aci'; @@ -40,7 +40,7 @@ const authContractSource = `contract BlindAuth = `; describe('Generalized Account', () => { let aeSdk: AeSdk; - let gaAccountAddress: EncodedData<'ak'>; + let gaAccountAddress: Encoded.AccountAddress; let authContract: ContractInstance; before(async () => { @@ -79,7 +79,10 @@ describe('Generalized Account', () => { it('buildAuthTxHash generates a proper hash', async () => { const { rawTx } = await aeSdk .spend(10000, publicKey, { authData: { source: authContractSource, args: [genSalt()] } }); - const spendTx = encode(unpackTx(rawTx, Tag.SignedTx).tx.encodedTx.tx.tx.tx.encodedTx.rlpEncoded, 'tx'); + const spendTx = encode( + unpackTx(rawTx, Tag.SignedTx).tx.encodedTx.tx.tx.tx.encodedTx.rlpEncoded, + Encoding.Transaction, + ); expect(await aeSdk.buildAuthTxHash(spendTx)).to.be .eql((await authContract.methods.getTxHash()).decodedResult); }); diff --git a/test/integration/index.ts b/test/integration/index.ts index ee1c5a52b8..aa4bd8f14a 100644 --- a/test/integration/index.ts +++ b/test/integration/index.ts @@ -19,11 +19,11 @@ import { AeSdk, generateKeyPair, MemoryAccount, Node, } from '../../src'; import '..'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded } from '../../src/utils/encoder'; export const url = process.env.TEST_URL ?? 'http://localhost:3013'; const compilerUrl = process.env.COMPILER_URL ?? 'http://localhost:3080'; -const publicKey = process.env.PUBLIC_KEY as EncodedData<'ak'> ?? 'ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR'; +const publicKey = process.env.PUBLIC_KEY as Encoded.AccountAddress ?? 'ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR'; const secretKey = process.env.SECRET_KEY ?? 'bf66e1c256931870908a649572ed0257876bb84e3cdf71efb12f56c7335fad54d5cf08400e988222f26eb4b02c8f89077457467211a6e6d955edb70749c6a33b'; export const networkId = process.env.TEST_NETWORK_ID ?? 'ae_devnet'; export const ignoreVersion = process.env.IGNORE_VERSION === 'true'; diff --git a/test/integration/oracle.ts b/test/integration/oracle.ts index bc39f2fa4c..3eb351e0c0 100644 --- a/test/integration/oracle.ts +++ b/test/integration/oracle.ts @@ -24,7 +24,7 @@ import { ORACLE_TTL_TYPES, QUERY_FEE, } from '../../src'; import MemoryAccount from '../../src/account/Memory'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded } from '../../src/utils/encoder'; describe('Oracle', () => { let aeSdk: AeSdk; @@ -75,7 +75,7 @@ describe('Oracle', () => { query = await oracle.getQuery(query.id); expect(query.decodedResponse).to.be.equal(queryResponse); - expect(decode(query.response as EncodedData<'or'>).toString()).to.be.equal(queryResponse); + expect(decode(query.response as Encoded.OracleResponse).toString()).to.be.equal(queryResponse); }); it('Poll for response', async () => { diff --git a/test/integration/paying-for.ts b/test/integration/paying-for.ts index cc0732690c..d6ed47c24e 100644 --- a/test/integration/paying-for.ts +++ b/test/integration/paying-for.ts @@ -23,7 +23,7 @@ import { AeSdk, generateKeyPair, MemoryAccount, Tag, UnexpectedTsError, } from '../../src'; import { ContractInstance } from '../../src/contract/aci'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded } from '../../src/utils/encoder'; describe('Paying for transaction of another account', () => { let aeSdk: AeSdk; @@ -63,7 +63,7 @@ describe('Paying for transaction of another account', () => { entrypoint init(x: int): state = { value = x } entrypoint getValue(): int = state.value stateful entrypoint setValue(x: int) = put(state{ value = x })`; - let contractAddress: EncodedData<'ct'>; + let contractAddress: Encoded.ContractAddress; let aeSdkNotPayingFee: any; let payingContract: ContractInstance; diff --git a/test/integration/rpc.ts b/test/integration/rpc.ts index af36c0b24e..3cfeee577d 100644 --- a/test/integration/rpc.ts +++ b/test/integration/rpc.ts @@ -51,7 +51,7 @@ import { getSdk, ignoreVersion, networkId, url, } from '.'; import { Accounts, Network } from '../../src/aepp-wallet-communication/rpc/types'; -import { EncodedData, EncodingType } from '../../src/utils/encoder'; +import { Encoded } from '../../src/utils/encoder'; const WindowPostMessageFake = ( name: string, @@ -99,7 +99,7 @@ describe('Aepp<->Wallet', function aeppWallet() { const handlerReject = (): void => { throw new Error('test reject'); }; const handlerRejectPromise = async (): Promise => { throw new Error('test reject'); }; let account: AccountBase; - let accountAddress: EncodedData<'ak'>; + let accountAddress: Encoded.AccountAddress; describe('New RPC Wallet-AEPP: AEPP node', () => { const keypair = generateKeyPair(); @@ -321,7 +321,7 @@ describe('Aepp<->Wallet', function aeppWallet() { }); const res = await aepp.send(tx); if (res.tx?.payload == null || res.blockHeight == null) throw new UnexpectedTsError(); - decode(res.tx.payload as EncodedData).toString().should.be.equal('zerospend2'); + decode(res.tx.payload as Encoded.Any).toString().should.be.equal('zerospend2'); res.blockHeight.should.be.a('number'); }); @@ -383,7 +383,8 @@ describe('Aepp<->Wallet', function aeppWallet() { it('Receive update for wallet select account', async () => { if (aepp._accounts == null) throw new UnexpectedTsError(); - const connectedAccount = Object.keys(aepp._accounts.connected)[0] as EncodedData<'ak'>; + const connectedAccount = Object + .keys(aepp._accounts.connected)[0] as Encoded.AccountAddress; const accountsPromise = new Promise((resolve) => { aepp.onAddressChange = resolve; }); @@ -534,7 +535,7 @@ describe('Aepp<->Wallet', function aeppWallet() { }); const res = await aepp.send(tx); if (res.tx?.payload == null || res.blockHeight == null) throw new UnexpectedTsError(); - decode(res.tx.payload as EncodedData).toString().should.be.equal('zerospend2'); + decode(res.tx.payload as Encoded.Any).toString().should.be.equal('zerospend2'); res.blockHeight.should.be.a('number'); }); diff --git a/test/integration/transaction.ts b/test/integration/transaction.ts index be5c21e408..5da122633f 100644 --- a/test/integration/transaction.ts +++ b/test/integration/transaction.ts @@ -23,14 +23,14 @@ import { commitmentHash, oracleQueryId, decode, encode, ORACLE_TTL_TYPES, Tag, AE_AMOUNT_FORMATS, } from '../../src'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded, Encoding } from '../../src/utils/encoder'; const nonce = 1; const nameTtl = 1; const clientTtl = 1; const amount = 0; -const senderId: EncodedData<'ak'> = 'ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688'; -const recipientId: EncodedData<'ak'> = 'ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688'; +const senderId: Encoded.AccountAddress = 'ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688'; +const recipientId: Encoded.AccountAddress = 'ak_2iBPH7HUz3cSDVEUWiHg76MZJ6tZooVNBmmxcgVK6VV8KAE688'; const name = 'test123test.chain'; const nameId = 'nm_2sFnPHi5ziAqhdApSpRBsYdomCahtmk3YGNZKYUTtUNpVSMccC'; const nameFee = '1000000000000000000000'; @@ -60,7 +60,7 @@ const commitmentId = commitmentHash(name, nameSalt); describe('Transaction', () => { let aeSdk: AeSdk; const address = 'ak_2dATVcZ9KJU5a8hdsVtTv21pYiGWiPbmVcU1Pz72FFqpk9pSRR'; - const oracleId = encode(decode(address), 'ok'); + const oracleId = encode(decode(address), Encoding.OracleAddress); let contract: any; before(async () => { diff --git a/test/unit/crypto.ts b/test/unit/crypto.ts index 938a64a5c8..6f91faa10f 100644 --- a/test/unit/crypto.ts +++ b/test/unit/crypto.ts @@ -20,13 +20,13 @@ import { describe, it } from 'mocha'; import { assert, expect } from 'chai'; import * as Crypto from '../../src/utils/crypto'; import { buildTxHash, unpackTx, decode } from '../../src'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded } from '../../src/utils/encoder'; // These keys are fixations for the encryption lifecycle tests and will // not be used for signing const privateKeyAsHex = '4d881dd1917036cc231f9881a0db978c8899dd76a817252418606b02bf6ab9d22378f892b7cc82c2d2739e994ec9953aa36461f1eb5a4a49a5b0de17b3d23ae8'; const privateKey = Buffer.from(privateKeyAsHex, 'hex'); -const publicKeyWithPrefix: EncodedData<'ak'> = 'ak_Gd6iMVsoonGuTF8LeswwDDN2NF5wYHAoTRtzwdEcfS32LWoxm'; +const publicKeyWithPrefix: Encoded.AccountAddress = 'ak_Gd6iMVsoonGuTF8LeswwDDN2NF5wYHAoTRtzwdEcfS32LWoxm'; const publicKey = decode(publicKeyWithPrefix); const txBinaryAsArray = [ diff --git a/test/unit/hd-wallet.ts b/test/unit/hd-wallet.ts index da4f3597ef..10fe609c34 100644 --- a/test/unit/hd-wallet.ts +++ b/test/unit/hd-wallet.ts @@ -23,7 +23,9 @@ import { getSaveHDWalletAccounts, DerivationError, } from '../../src'; -import { encode, decode, EncodedData } from '../../src/utils/encoder'; +import { + encode, decode, Encoded, Encoding, +} from '../../src/utils/encoder'; import { concatBuffers } from '../../src/utils/other'; describe('hd wallet', () => { @@ -62,7 +64,7 @@ describe('hd wallet', () => { expect(accounts).to.eql(testAccounts.map((acc) => ({ secretKey: acc.secretKey, - publicKey: encode(Buffer.from(acc.publicKey, 'hex'), 'ak'), + publicKey: encode(Buffer.from(acc.publicKey, 'hex'), Encoding.AccountAddress), }))); })); @@ -165,7 +167,7 @@ describe('hd wallet', () => { it('get HdWalletAccount from seed', () => { const wallet = getHdWalletAccountFromSeed(testMnemonicSeed, 0); - decode(wallet.publicKey as EncodedData<'ak'>); + decode(wallet.publicKey as Encoded.AccountAddress); }); it('Derive child with invalid path', () => { diff --git a/test/unit/memory-account.ts b/test/unit/memory-account.ts index 493a21045c..659ce04020 100644 --- a/test/unit/memory-account.ts +++ b/test/unit/memory-account.ts @@ -20,7 +20,7 @@ import { describe, it } from 'mocha'; import { expect } from 'chai'; import MemoryAccount from '../../src/account/Memory'; import { generateKeyPair, InvalidKeypairError, DecodeError } from '../../src'; -import { EncodedData } from '../../src/utils/encoder'; +import { Encoded } from '../../src/utils/encoder'; const testAcc = generateKeyPair(); @@ -32,7 +32,7 @@ describe('MemoryAccount', () => { }); it('Fail on invalid publicKey', async () => { - expect(() => new MemoryAccount({ keypair: { publicKey: ' ' as EncodedData<'ak'>, secretKey: testAcc.secretKey } })) + expect(() => new MemoryAccount({ keypair: { publicKey: ' ' as Encoded.AccountAddress, secretKey: testAcc.secretKey } })) .to.throw(DecodeError, 'Encoded string missing payload'); }); diff --git a/test/unit/tx.ts b/test/unit/tx.ts index 583d9a1af5..46e5f7c224 100644 --- a/test/unit/tx.ts +++ b/test/unit/tx.ts @@ -30,6 +30,7 @@ import { NAME_BID_RANGES, Tag, SchemaNotFoundError, } from '../../src'; +import { Encoding } from '../../src/utils/encoder'; describe('Tx', () => { it('reproducible commitment hashes can be generated', async () => { @@ -92,9 +93,11 @@ describe('Tx', () => { }); describe('encode', () => { - it('encodes base64check', () => expect(encode(payload, 'ba')).to.be.equal('ba_AQIq9Y55kw==')); + it('encodes base64check', () => expect(encode(payload, Encoding.Bytearray)) + .to.be.equal('ba_AQIq9Y55kw==')); - it('encodes base58check', () => expect(encode(payload, 'nm')).to.be.equal('nm_3DZUwMat2')); + it('encodes base58check', () => expect(encode(payload, Encoding.Name)) + .to.be.equal('nm_3DZUwMat2')); }); describe('getDefaultPointerKey', () => { @@ -103,7 +106,7 @@ describe('Tx', () => { }); it('Deserialize tx: invalid tx VSN', () => { - const tx = encode(rlpEncode([10, 99]), 'tx'); + const tx = encode(rlpEncode([10, 99]), Encoding.Transaction); expect(() => unpackTx(tx)) .to.throw(SchemaNotFoundError, `Transaction deserialization not implemented for tag ${10} version ${99}`); });