https://rpc-testnet0.beatoz.io/
Network integration guide
BEATOZ Wallet Integration Guide
Everything a wallet team needs to support the BEATOZ chain: native transactions, the RPC surface, and the JavaScript SDK.
Wallet Integration Overview
BEATOZ wallet support is a native chain integration. Balance, nonce, transaction construction, signing, broadcasting, and transaction decoding should use the BEATOZ native RPC and the native transaction format documented in this guide.
broadcast_tx_commit
| Wallet feature | BEATOZ integration path | Reference |
|---|---|---|
| Network registration | Use the network settings table. | /network |
| Balance and nonce | Use native account lookup. Ethereum balance and nonce RPC methods are not exposed. | account |
| Network parameters | Read gasPrice and minTrxGas from governance parameters before building any transaction. | gov_params |
| Native transfer | Build and sign a BEATOZ transaction with type 1. | TRX_TRANSFER |
| Contract deploy/call | Use BEATOZ contract transaction wrapping and VM read methods. | TRX_CONTRACT, vm_call, vm_estimate_gas |
| Send signed transaction | Marshal signed TrxProto bytes and broadcast them as a base64 tx parameter. | broadcast_tx_commit |
| Transaction history/detail | Query by tx hash, search by sender/receiver events, and unmarshal the returned TrxProto bytes. | tx, tx_search |
Encoding conventions used by the node
| Value | Convention |
|---|---|
| Addresses in request params | BEATOZ-native methods (account, stakes, vm_call, ...) accept 20-byte hex with or without the 0x prefix. |
| Addresses in responses | Returned as UPPERCASE hex WITHOUT the 0x prefix (e.g. 13BB453C...). Compare addresses case-insensitively after stripping 0x. |
| Byte params on Tendermint methods | Most Tendermint byte params in JSON-RPC POST bodies use base64 (tx.hash, block_by_hash.hash, broadcast_tx_*/check_tx tx bytes). Exception: abci_query.data is Tendermint HexBytes and must be a hex string without 0x; use an empty string when there is no data. |
| 64-bit integers in responses | All int64/uint64 values (heights, counts, gas, most governance parameters) are returned as JSON strings, e.g. "175568". Parse them as strings, not numbers. |
| height / page / per_page params | Send these as strings ("175568"), not JSON numbers. The JSON-RPC decoder rejects bare numbers for 64-bit integer params. |
Accounts and Addresses
BEATOZ accounts use secp256k1 keys and 20-byte addresses. Serialized public keys are compressed 33-byte secp256k1 keys. On the current testnet, Tendermint is replaced by the beatoz/tendermint-ethaddr fork, so secp256k1 PubKey.Address() decompresses the public key, applies Ethereum-style Keccak-256 to the uncompressed key without the 0x04 prefix, and takes the last 20 bytes. Transaction signing hashes with SHA-256.
privateKey = 32 random bytes // secp256k1 scalar
serializedPub = compressed_secp256k1_pubkey(privateKey) // 33 bytes, 0x02/0x03 || X
uncompressed = decompress_secp256k1_pubkey(serializedPub) // 65 bytes, 0x04 || X || Y
address = keccak256(uncompressed[1:])[12:32] // 20 bytes
// Node-equivalent Go path:
// tmsecp256k1.PubKey(serializedPub).Address()
// The testnet replaces Tendermint with beatoz/tendermint-ethaddr, so Address()
// returns the Ethereum-style Keccak address bytes above.
| Field | Type | Description |
|---|---|---|
privateKey |
secp256k1 |
32-byte secp256k1 private key. |
address |
bytes20 |
20-byte address: keccak256(uncompressed_pubkey[1:])[12:32], where uncompressed_pubkey is derived from the compressed secp256k1 public key. Request params accept hex with or without 0x; responses return uppercase hex without 0x. |
chainId |
HexString |
Chain ID string from the network table (e.g. 0xbea701). It is parsed as a number when signing — see Signing. |
signatureHash |
SHA-256 |
BEATOZ native transaction signatures hash the signing preimage with SHA-256. |
hdPath |
TBD |
Mnemonic derivation path and coin type are not finalized in this guide. Confirm with the BEATOZ team before production mnemonic support. |
Wallet Implementation Checklist
Implement these items in order. Each step links to the section that documents it in full — this list intentionally repeats no detail.
1. network
Register the RPC URL, chain ID, currency, and decimals from the Network & Currency table.
2. account
Create or import a secp256k1 account and derive the 20-byte address (Accounts & Addresses).
3. account RPC
Read balance and nonce with the native account RPC.
4. gov_params RPC
Read gasPrice and minTrxGas from gov_params before building a transaction (Fees & Gas).
5. build tx
Build the transaction envelope and the type-specific payload (Data Structure, Transaction Types).
6. sign
Sign with the V1 scheme: RLP preimage + chain ID bytes, SHA-256, secp256k1, v = 27/28 (Signing).
7. marshal
Marshal the signed transaction as proto3 TrxProto bytes and compute the tx hash (Proto Marshal).
8. broadcast
Before broadcasting, persist the exact signed bytes and tx hash. Submit base64 signed bytes, and on timeout retry the SAME bytes or poll the SAME hash; do not re-sign unless you intend to create a different tx.
9. lookup
For deposits, never credit from tx_search alone. Query the tx by hash, require tx_result.code == 0, decode result.tx, and verify type/to/amount against your deposit rule.
Custody Credit Policy
Wallets and exchanges should treat event search as discovery only. Credit, retry, and finality decisions must be based on the decoded transaction and committed block state.
Native deposit credit
Credit only after direct tx lookup confirms tx_result.code == 0 and decoded result.tx is TRX_TRANSFER with to equal to the deposit address and amount equal to the credited fons amount. tx.receiver is only an indexed event and can be emitted by other successful transaction types.
Finality policy
Require status.sync_info.catching_up == false, tx height H, latest_block_height > H, and a successful commit(H) lookup. For custody, set an explicit N-confirmation policy before crediting balances.
Retry and idempotency
Persist signed raw bytes and the SHA-256 tx hash before broadcast. If broadcast_tx_commit times out or the client disconnects, resend the same bytes or poll the same hash. Re-signing changes fields such as time and creates a new hash.
Network & Currency
Use the testnet endpoint for live calls from this guide. Mainnet values will be announced by the BEATOZ team and are listed as TBD until finalized.
| Network | RPC URL | Chain ID (hex) | Chain ID (decimal) | Native currency | Symbol | Currency decimals | Smallest unit |
|---|---|---|---|---|---|---|---|
| Mainnet | TBD |
TBD |
TBD |
TBD |
TBD |
TBD |
TBD |
| Testnet | https://rpc-testnet0.beatoz.io/ |
0xbea701 |
12494593 |
BEATOZ |
BEATOZ |
18 |
fons |
Currency units
| Unit | Value in fons | Note |
|---|---|---|
fons |
1 |
Smallest unit. All RPC amount/balance/gasPrice values are in fons. |
Gfons |
10^9 |
Convenient for gas prices (current testnet gasPrice is 48 Gfons). The SDK also defines kfons/mfons/tfons/pfons. |
BEATOZ |
10^18 |
Display unit. 1 BEATOZ = 10^18 fons ('beatoz' in SDK unit helpers). |
Transaction Flow Overview
Native BEATOZ transactions are protobuf-marshaled application transactions. Wallets build the transaction fields, RLP-encode the signing payload, sign the SHA-256 digest with secp256k1, attach the signature, marshal the signed transaction with proto3, and broadcast the resulting bytes.
1. Read account state
RPC: account({ addr })
Use: value.nonce (string), value.balance (fons, string)
2. Read network parameters
RPC: gov_params({})
Use: value.gasPrice, value.minTrxGas
3. Build TRX_TRANSFER
version = 1
time = current Unix time in NANOSECONDS
nonce = account nonce, used AS-IS (first tx of a new account: 0)
from = sender address
to = receiver address
amount = amount in fons
gas = minTrxGas // for regular transfer to a normal account
gasPrice = gov_params gasPrice // must match the network value EXACTLY
payload = nil
4. Sign with V1 (see Signing)
preimage = rlpBytes || chainIdBytes
digest = SHA-256(preimage)
sig = secp256k1.sign(digest)
if sig.v is 0 or 1: sig.v += 27
5. Marshal and hash (see Proto Marshal)
signedTxBytes = proto3.Marshal(TrxProto)
txHash = SHA-256(signedTxBytes) // 32 bytes
6. Broadcast (see Broadcast)
RPC: broadcast_tx_commit({ tx: base64(signedTxBytes) })
Success: check_tx.code == 0 AND deliver_tx.code == 0
7. Lookup (see Lookup & History)
RPC: tx({ hash: base64(txHash), prove: false })
Decode result.tx (base64) as TrxProto.
Transaction Data Structure
All BEATOZ native transaction types share the same envelope. The type-specific payload has two encodings in the full flow: RLP payload bytes are used only inside the V1 signing preimage, while the final TrxProto _payload field stores protobuf-marshaled payload bytes.
Trx
ctrlers/types/trx.go
| Field | Type | Description |
|---|---|---|
version |
int32 |
Transaction version. Current builders use version 1. |
time |
int64 |
Client-side timestamp in Unix NANOSECONDS. The node does not validate it, but it is part of the signed bytes and therefore of the tx hash. |
nonce |
int64 |
Must EQUAL the next sender nonce. For one tx, read the current nonce from account RPC and use it as-is (no +1). After CheckTx succeeds, the node mempool/check state increments nonce before commit, so wallets/exchanges sending multiple same-sender txs must reserve pending nonces locally instead of rereading only committed account nonce. |
from |
Address |
20-byte sender address. The node recovers the signer address from the signature and rejects the tx if it differs. |
to |
Address |
20-byte address whose meaning depends on type: receiver (transfer), delegatee validator (staking/unstaking), contract address or empty for deploy (contract). For TRX_PROPOSAL and TRX_VOTING it MUST be the zero address. See Transaction Types. |
amount |
uint256 |
Native amount in fons. TRX_WITHDRAW requires amount = 0 (the withdrawal amount goes in the payload). For proposal/voting, set amount = 0; nonzero value is not transferred by governance execution but can still affect upfront balance validation. |
gas |
int64 |
Gas limit. Must be at least minTrxGas and fit within the effective remaining block gas. blockGasLimit is the testnet governance ceiling, but a tx can still fail when earlier txs have consumed block gas. For non-EVM types the FULL limit is charged — see Fees & Gas. |
gasPrice |
uint256 |
Gas price in fons. Must EQUAL the gov_params gasPrice exactly, or the tx is rejected. |
type |
int32 |
One of the supported transaction type constants (1-8). |
payload |
ITrxPayload |
Type-specific payload. Transfer and staking use no payload. |
sig |
bytes65 |
secp256k1 signature as r || s || v. For new V1 wallet signing, v must be 27 or 28. Legacy V0 transactions with v = 0 or 1 may still be accepted by the node for backward compatibility. |
payer |
Address? |
Optional fee payer. When set with payerSig, the payer account is charged the fee instead of the sender. Not part of the sender's RLP signing payload. |
payerSig |
bytes65? |
Optional payer signature. The payer signs AFTER the sender — see Fees & Gas. |
Payload Structures
protos/trx.proto
Proto field numbers are shown because TrxPayloadProposalProto is NOT sequential: applying_height is field 6, opt_type is field 4, options is field 5. Copy the numbers exactly when hand-writing the schema.
| Name | Proto fields (with field numbers) | RLP payload (for signing) |
|---|---|---|
TrxPayloadAssetTransferProto |
Empty message | Omitted — empty byte string |
TrxPayloadStakingProto |
Empty message | Omitted — empty byte string |
TrxPayloadUnstakingProto |
bytes tx_hash = 1 |
rlp(tx_hash) |
TrxPayloadWithdrawProto |
bytes _reqAmt = 1 |
rlp(reqAmt bytes) |
TrxPayloadContractProto |
bytes _data = 1 |
rlp(data) |
TrxPayloadProposalProto |
string message = 1, int64 start_voting_height = 2, int64 voting_blocks = 3, int32 opt_type = 4, repeated bytes options = 5, int64 applying_height = 6 |
rlp([message, startVotingHeight, votingBlocks, applyingHeight, optType, options]) |
TrxPayloadVotingProto |
bytes tx_hash = 1, int32 choice = 2 |
rlp([tx_hash, choice]) |
TrxPayloadSetDocProto |
string name = 1, string url = 2 |
rlp([name, url]) |
Supported Transaction Types
The transaction type number determines which payload schema is used and which validation rules apply. The rules column lists the constraints the node enforces — violating any of them rejects the transaction.
| Type | Name | Purpose | Validation rules & wallet guidance |
|---|---|---|---|
1 |
TRX_TRANSFER |
Moves native BEATOZ value between accounts. | to = receiver address. If the receiver is a contract account, the EVM path executes the transfer, so allocate EVM gas instead of using the native minTrxGas shortcut. vm_estimate_gas is zero-value calldata-only, so value-bearing payable/fallback transfers may need manual gas limits or extra margin. No payload. |
2 |
TRX_STAKING |
Stakes BEATOZ to self-bond as a validator or delegate to an existing delegatee. | to = own address (self-bond) or an EXISTING delegatee address. amount must be a positive multiple of 10^18 fons (1 BEATOZ = 1 voting power). Self-bonding must reach minValidatorPower. Delegation must meet minDelegatorPower, keep the delegatee above minSelfPowerRate after the new stake, and fit the per-block total voting-power change limit (MaxUpdatablePowerRate). No payload. |
3 |
TRX_UNSTAKING |
Requests unstaking for an existing stake identified by its staking transaction hash. | to = the delegatee address of the original stake. Wallets should set amount = 0; execution identifies the stake by payload tx_hash and does not use tx.amount, but upfront balance validation still includes amount. payload tx_hash = the 32-byte hash of the original TRX_STAKING tx. Funds unlock and return to the owner after lazyUnbondingBlocks (gov_params). Unstaking is also subject to the per-block maxUpdatablePowerRate power-change limit, and if validator self-power reaches 0 the validator/delegation state can be frozen or unbonded by node logic. |
4 |
TRX_PROPOSAL |
Creates a governance proposal with a voting window, applying height, proposal type, and options. | to MUST be the zero address. Set amount = 0; governance execution does not transfer value, but a nonzero amount can still affect upfront balance validation. Sender must be a validator. start_voting_height must be greater than the current height, voting_blocks must be within gov_params min/max, applying_height must be at or after endVotingHeight + lazyApplyingBlocks, and options must be non-empty. Gov-param proposals also validate each option as GovParams. |
5 |
TRX_VOTING |
Votes for one option on an active governance proposal. | to MUST be the zero address. The proposal is identified by the proposal tx hash inside the payload, not by 'to'. Sender must be an eligible voter for that proposal, choice must be within the proposal options, and the current height must be inside the voting period. |
6 |
TRX_CONTRACT |
Deploys or calls an EVM contract using BEATOZ native transaction wrapping. | Call: to = contract address. Deploy: use the same deploy address encoding in the final proto and RLP preimage; this guide uses empty/nil to. Do not mix empty and zero-address encodings because the signed bytes/hash differ. The deployed contract address = Ethereum CreateAddress(from, nonce); confirm it with evm.contractAddress only when tx type/to/nonce indicate a deploy, because evm.contractAddress is also emitted as the log emitter address for normal contract events. Do not use tx_result.data for the address. vm_estimate_gas is zero-value calldata-only, so value-bearing payable calls may need manual gas limits or extra margin. |
7 |
TRX_SETDOC |
Deprecated transaction type. It set the sender account's name and document URL. | Deprecated. Wallets should decode historical TRX_SETDOC transactions but should not create new ones. If a legacy flow must be handled, wallets should use to = sender and amount = 0; execution writes the document to the sender account, while generic transaction validation still checks that to is a valid 20-byte address and includes amount in upfront balance checks. |
8 |
TRX_WITHDRAW |
Withdraws accumulated staking reward. | amount MUST be 0; the requested amount goes in payload reqAmt and must not exceed the accumulated reward (no clamping — excess is rejected with "insufficient reward"). The reward is credited to the sender. The withdraw controller ignores the destination, but the generic transaction layer still expects a valid 20-byte to address, so set it to the sender address. |
Fees & Gas
The charged transaction fee is gasUsed × gasPrice, paid in fons by the sender (or by the payer when fee delegation is used). For non-EVM paths gasUsed equals tx.gas, so this is the full gas limit; for EVM paths it is the actual used gas. BEATOZ has no fee market: gasPrice is a governance parameter, and the node rejects any transaction whose gasPrice differs from it.
| Rule | Description |
|---|---|
gasPrice == gov_params.gasPrice |
Exact match required. Read it from gov_params right before building. Do NOT offer user-adjustable fee levels. Current testnet value: 48,000,000,000 fons (48 Gfons). |
minTrxGas <= gas <= effective block gas |
gas must be at least minTrxGas (testnet: 5,000) and fit in the effective remaining block gas. Testnet gov_params.blockGasLimit is 120,000,000, while live Tendermint consensus max_gas is 100,000,000; enforce the stricter current network limit and expect rejection if the block is already near full. |
| Native types charge the FULL limit | For non-EVM execution paths (regular transfer to a normal account, staking, unstaking, proposal, voting, setdoc, withdraw) gas_used equals the full tx.gas and there is NO refund. Set gas = minTrxGas for these paths; overprovisioning burns the entire limit. Transfer to a contract account is EVM-handled and must use EVM gas rules. |
| TRX_CONTRACT charges actual usage | The EVM path charges usedGas × gasPrice. Ethereum intrinsic gas rules apply (a call costs at least 21,000 gas), so minTrxGas is not enough. vm_estimate_gas simulates addr/to/data with zero value and cannot model tx.amount; payable/value-bearing calls and native transfers to contract fallback functions may need manual gas limits or extra margin. |
| Balance requirement | At validation the sender balance must cover amount + gas × gasPrice up front (when a payer is set, the payer's balance must cover the fee part). |
Reward and fee settlement
| Rule | Description |
|---|---|
| Staking / inflation reward | At heights where height % inflationCycleBlocks == 0, rewards are calculated during BeginBlock/EndBlock and accumulated in the reward ledger. They are not added to account balance until the account sends TRX_WITHDRAW. |
| TRX_WITHDRAW | Moves accumulated reward from the reward ledger into the sender account balance. amount must be 0 and the requested amount is stored in payload _reqAmt. |
| Transaction fee distribution | For successful transactions, fees are collected from the sender or payer. In EndBlock, txFeeRewardRate percent of the block fee total is paid to the proposer and the remainder is sent to the configured dead address. |
| Failure cases | CheckTx failure is not included in a block and does not change committed fee/nonce. DeliverTx validation failure can be included without post-run fee/nonce changes. DeliverTx execution failure after validation can still increment nonce and charge fee, but current app code adds fees to EndBlock proposer/dead-address distribution only on successful execution. |
Fee delegation (payer)
A third-party payer can cover transaction fees by setting payer and attaching payerSig.
Payer account
The payer account must already exist and hold enough balance for the upfront tx.gas × gasPrice fee. A valid payerSig alone is not enough.
Fee charging
The node verifies both sender and payer signatures, charges gasUsed × gasPrice to the payer, and requires the sender to hold only the transfer amount.
Payer signature
The payer signs AFTER the sender. The payer preimage is the RLP encoding of the transaction with the sender's sig included and payer/payerSig cleared, followed by chain ID bytes. Use the same V1 hash and v = 27/28 rules. Legacy V0 payer signatures may be accepted by the node, but new wallet integrations should use V1.
SDK limitation
The current JavaScript SDK proto path does not include payer/payer_sig, so fee-delegated transactions should be implemented against the Go proto until the SDK is updated.
V1 Signing
V1 signing clears the signature, RLP-encodes the transaction as one 11-element list in the exact order below, appends the chain ID bytes, hashes the preimage with SHA-256, signs the digest with secp256k1, and stores v as 27/28. Add 27 only when the signing library returns a raw recovery id 0/1; if it already returns 27/28, keep it unchanged. The node selects the signature scheme by the v byte: 27/28 means V1. A legacy V0 scheme (v = 0/1) exists for backward compatibility — do NOT use it for new integrations.
// Signer V1
tx.sig = nil // the cleared sig is still an element of the RLP list (empty string)
// payer / payerSig are never part of the sender's RLP struct
payloadBytes = (tx.payload == nil)
? EMPTY // transfer, staking
: rlpEncode(payload) // see the "RLP payload" column in Payload Structures
rlpBytes = rlpEncode([ // ONE list with exactly 11 elements
version, // RLP unsigned integer
time, // RLP unsigned integer (Unix nanoseconds)
nonce, // RLP unsigned integer
from, // 20-byte string
to, // 20-byte string (empty string for contract deploy)
amountBytes, // uint256 -> minimal big-endian bytes (empty string for 0)
gas, // RLP unsigned integer
gasPriceBytes, // uint256 -> minimal big-endian bytes
type, // RLP unsigned integer
payloadBytes, // byte string (empty when there is no payload)
'' // the cleared sig -> always an empty string when signing
])
chainIdBytes = minimalBigEndianBytes(parseChainId(chainId))
// chainId "0xbea701" is parsed as a NUMBER (hex because of the 0x prefix).
// uint256(0xbea701).bytes() = 0xBE 0xA7 0x01 (3 bytes)
// It is NOT the ASCII bytes of the string "0xbea701".
preimage = rlpBytes || chainIdBytes
digest = SHA-256(preimage) // NOT keccak
sig = secp256k1.sign(digest, privateKey) // r(32) || s(32) || v(1)
if (sig[64] === 0 || sig[64] === 1) sig[64] += 27
// final v must be 27 or 28; do not add 27 again if your signer already returns 27/28
tx.sig = sig
| Element | RLP encoding |
|---|---|
version, time, nonce, gas, type |
Unsigned integers (Go uint64): canonical RLP integer encoding — minimal big-endian bytes, 0 encodes as the empty string (0x80). |
from, to |
20-byte strings. For contract deployment 'to' is the empty string. |
amount, gasPrice |
uint256 values encoded as byte strings of their minimal big-endian bytes (no leading zeros; 0 becomes the empty string). |
payload |
The RLP encoding of the payload (per the Payload Structures table), embedded as one byte string. This is signing-only data; it is not the protobuf _payload bytes used in the final TrxProto. Empty string when the type has no payload. |
sig (11th element) |
Always present in the list and always the empty string at signing time, because the signature is cleared before encoding. |
chainIdBytes |
The chain ID string parsed as a number (hex when 0x-prefixed, else decimal), then serialized as minimal big-endian bytes. "0xbea701" → BE A7 01. Appended raw after rlpBytes — not RLP-encoded. |
digest |
SHA-256 of the preimage. Do not use keccak for BEATOZ transaction signing. |
sig[64] (v) |
Final V1 v must be 27 or 28. Add 27 only for raw recovery ids 0/1; do not add 27 again if the signer library already returns 27/28. The node recovers the signer address and requires it to equal 'from'. |
Signed Transaction Marshal
After the signature is attached, the transaction is converted to the Go-node TrxProto schema and marshaled with protobuf v3. The resulting bytes are the signed transaction bytes used by check_tx and broadcast_tx methods, and their SHA-256 hash is the transaction hash. Note: the current JavaScript SDK transaction type omits payer/payer_sig, so treat the Go proto as canonical for fee-delegated transactions.
message TrxProto {
int32 version = 1;
int64 time = 2;
int64 nonce = 3;
bytes from = 4;
bytes to = 5;
bytes _amount = 6;
int64 gas = 7;
bytes _gasPrice = 8;
int32 type = 9;
bytes _payload = 10;
bytes sig = 11;
bytes payer = 12;
bytes payer_sig = 13;
}
signedTxBytes = proto3.Marshal(trxProto)
txHash = SHA-256(signedTxBytes) // full 32 bytes
// Display the hash as hex; pass it as base64 in JSON-RPC params.
| Field | Type | Description |
|---|---|---|
_amount |
bytes |
uint256 as minimal big-endian bytes — no leading zeros, and the field is left unset when the value is 0. |
_gasPrice |
bytes |
uint256 as minimal big-endian bytes, same rule as _amount. |
_payload |
bytes |
The protobuf-marshaled payload message for the selected type. This differs from the RLP payload bytes used for signing. Leave unset for transfer and staking (their payload messages are empty and marshal to zero bytes). |
sig |
bytes |
65-byte sender signature (r || s || v) after V1 normalization. |
payer, payer_sig |
bytes |
Optional fee-delegation fields — leave unset unless using a payer (see Fees & Gas). |
Broadcast Signed Transaction
Send the signed proto3 bytes through JSON-RPC. In the JSON body, the transaction bytes are passed as a base64 string in the tx parameter.
POST https://rpc-testnet0.beatoz.io/
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "broadcast_tx_commit",
"params": {
"tx": "<base64 signed TrxProto bytes>"
}
}
broadcast_tx_async
Returns after the node accepts the request for gossip. It does not wait for CheckTx or block execution. Response: hash, code, data, log, codespace (no events).
broadcast_tx_sync
Returns after the first CheckTx. Use result.code to detect immediate mempool validation failure, but remember that the node may later re-run CheckTx before inclusion. For timeout retry, resend the exact same signed bytes or poll the same hash.
broadcast_tx_commit
Waits for the tx to be committed in a block. Success requires check_tx.code == 0 AND deliver_tx.code == 0. When CheckTx fails, deliver_tx can be a zero-valued/empty object rather than a committed execution result; do not use deliver_tx presence as the success signal. The call blocks and can time out under load; on retry, send the same signed bytes or poll the same hash rather than re-signing.
Tx 조회와 파싱
브로드캐스트 이후 월렛에는 두 흐름이 필요합니다: transaction result를 읽는 것과 signed transaction bytes를 decode하는 것입니다. tx RPC는 실행 결과와 raw signed TrxProto bytes를 함께 반환합니다.
txHashBytes = hexToBytes(txHashHex) // 32 bytes
txRequestHash = base64(txHashBytes) // JSON-RPC tx.hash param
result = rpc.tx({ hash: txRequestHash, prove: false })
// Execution status for wallet UI
statusCode = result.tx_result.code
statusLog = result.tx_result.log
// What the user signed
signedTxBytes = base64Decode(result.tx)
trxProto = proto3.Unmarshal<TrxProto>(signedTxBytes)
from = hex(trxProto.from)
to = hex(trxProto.to)
amount = bigEndianBytesToDecimal(trxProto._amount) // fons
gasPrice = bigEndianBytesToDecimal(trxProto._gasPrice) // fons
payload = decodePayloadByType(trxProto.type, trxProto._payload)
라이브 tx 예제
이 테스트넷 transaction은 HTTP GET route로 열 수 있습니다. 같은 32바이트 hash를 JSON-RPC tx 메서드의 POST body에서 사용할 때는 base64로 인코딩해야 합니다. 이 과거 샘플의 signature는 legacy V0(v = 1)이므로 V1 signing test vector가 아니라 조회/파싱 예제로 사용하세요.
| 필드 | 디코딩 값 | 인코딩 / 표시 규칙 |
|---|---|---|
| HTTP GET URL | https://rpc-testnet0.beatoz.io/tx?hash=0x68b5403533aaad4d125bd24291a763f853e31f9e955112ba43cf7517bdbeb9d4 |
GET route accepts a 0x-prefixed hex transaction hash. |
| GET query hash | 0x68b5403533aaad4d125bd24291a763f853e31f9e955112ba43cf7517bdbeb9d4 |
Strip 0x and hex-decode to the original 32 hash bytes. |
| JSON-RPC POST hash | aLVANTOqrU0SW9JCkadj+FPjH56VURK6Q891F72+udQ= |
POST tx.hash is base64(raw 32 hash bytes), not the hex string. |
Annotated tx response
The live RPC response is plain JSON. The comments below are documentation notes that explain how a wallet should interpret each field.
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"hash": "68B5403533AAAD4D125BD24291A763F853E31F9E955112BA43CF7517BDBEB9D4",
// Display hash. It equals SHA-256(base64Decode(result.tx)).
"height": "175621",
// Block height as an int64 string.
"index": 0,
// Transaction index inside the block.
"tx_result": {
"code": 0,
// Execution status. 0 = success, non-zero = failed DeliverTx.
"data": "gXtFlm4t5BIVj2Gpk7Wex8fVBEfAIdO/tPBtxrKg3PY=",
// Base64 bytes returned by execution. Decode to bytes first.
"log": "",
"info": "",
// Human-readable failure detail usually appears in log when code != 0.
"gas_wanted": "2000000",
"gas_used": "58355",
// Gas fields are strings. For TRX_CONTRACT, gas_used is actual EVM usage.
"events": [
{
"type": "evm",
// Event namespace. This field is plain text, not base64.
"attributes": [
{
"key": "Y29udHJhY3RBZGRyZXNz",
"value": "OTYwMzExNTM2ZTdhMDM2Y2UyNTc3OGM3OTkyNGUyNjQ0NjgwYTM0ZQ==",
"index": false
},
// key = base64("contractAddress")
// value = base64("960311536e7a036ce25778c79924e2644680a34e")
{
"key": "dG9waWMuMA==",
"value": "RERGMjUyQUQxQkUyQzg5QjY5QzJCMDY4RkMzNzhEQUE5NTJCQTdGMTYzQzRBMTE2MjhGNTVBNERGNTIzQjNFRg==",
"index": true
},
// key = base64("topic.0")
// value = base64("DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF")
// index=true means this attribute can be used by tx_search.
{ "key": "dG9waWMuMQ==", "value": "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMA==", "index": true },
{ "key": "dG9waWMuMg==", "value": "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNzZCOEIzQkFENjlBNzdEQzYwQzdFQjJENTE2QTFCQjQ4N0Y5NURDMQ==", "index": true },
{ "key": "ZGF0YQ==", "value": "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2OWUxMGRlNzY2NzZkMDgwMDAwMA==", "index": false },
{ "key": "YmxvY2tOdW1iZXI=", "value": "MA==", "index": false },
{ "key": "cmVtb3ZlZA==", "value": "ZmFsc2U=", "index": false }
]
},
{
"type": "tx",
// BEATOZ transaction event namespace.
"attributes": [
{ "key": "dHlwZQ==", "value": "Y29udHJhY3Q=", "index": true },
// type = contract
{ "key": "c2VuZGVy", "value": "RDlBRDE4ODg0ODA3N0E5RjAzNjhFNzVGNzczMzExM0ZBOUI5MDYzRA==", "index": true },
// sender = D9AD188848077A9F0368E75F7733113FA9B9063D
{ "key": "cmVjZWl2ZXI=", "value": "OTYwMzExNTM2RTdBMDM2Q0UyNTc3OEM3OTkyNEUyNjQ0NjgwQTM0RQ==", "index": true },
// receiver = 960311536E7A036CE25778C79924E2644680A34E
{ "key": "YWRkcnBhaXI=", "value": "RDlBRDE4ODg0ODA3N0E5RjAzNjhFNzVGNzczMzExM0ZBOUI5MDYzRDk2MDMxMTUzNkU3QTAzNkNFMjU3NzhDNzk5MjRFMjY0NDY4MEEzNEU=", "index": true },
{ "key": "YW1vdW50", "value": "MA==", "index": false },
// amount = "0" fons
{ "key": "c3RhdHVz", "value": "MA==", "index": false }
// status = "0"
]
}
],
"codespace": ""
},
"tx": "CAEQgMnQ4rvgmt8YGBQiFNmtGIhIB3qfA2jnX3czET+puQY9KhSWAxFTbnoDbOJXeMeZJOJkRoCjTjiAiXpCBQstBeAASAZSRgpEQMEPGQAAAAAAAAAAAAAAAHa4s7rWmnfcYMfrLVFqG7SH+V3BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGnhDedmdtCAAABaQeItYCrmymyc21ZfzkmkSQVI5K+7hbp/4oqzyGyzCy3xcG7xhsi90l4JrK7dnsKpGSAS4mi0j+5oqOiiZLsmpUsB"
// Base64 signed TrxProto bytes. Decode and proto-unmarshal this field.
}
}
tx RPC result 필드
| 필드 | 의미 |
|---|---|
result.hash |
signed TrxProto bytes의 SHA-256 hash이며 응답에서는 대문자 hex입니다. tx 요청에서는 같은 32바이트를 0x hex가 아니라 base64로 보내야 합니다. |
result.height |
transaction이 포함된 block height입니다. 문자열로 반환됩니다. |
result.index |
블록 안의 transaction index입니다. |
result.tx_result |
실행 결과입니다. code 0은 성공, non-zero는 실패입니다. 사용자에게 보여줄 사유는 log/codespace를 사용하세요. gas_wanted/gas_used는 문자열입니다. |
result.tx_result.data |
tx_result.data는 application event Merkle root를 담은 base64 bytes이며 contract return data나 배포된 contract address가 아닙니다. 배포 주소는 Ethereum CreateAddress(from, nonce)로 계산하고, tx type/to/nonce가 deploy임을 확인한 경우에만 evm.contractAddress 이벤트로 검증하세요. |
result.tx |
Base64 signed TrxProto bytes입니다. 이 필드를 decode하고 proto-unmarshal하면 nonce, from/to, amount, gas, gasPrice, type, payload, sig, optional payer fields를 복원할 수 있습니다. |
result.tx_result.events |
이벤트는 tx_result.events 아래에 있습니다. Tendermint는 event attribute key/value를 base64로 인코딩합니다. |
TrxProto
result.tx를 Go node TrxProto schema로 unmarshal합니다. _amount와 _gasPrice는 minimal big-endian bytes에서 fons decimal string으로 변환하고, from/to/payer bytes는 20바이트 hex address로 정규화하세요.
payload
_payload는 type에 따라 decode합니다: transfer/staking은 비어 있고, unstaking은 tx_hash, proposal은 message/start_voting_height/voting_blocks/opt_type/options/applying_height, voting은 tx_hash/choice, contract는 _data, setdoc은 name/url, withdraw는 _reqAmt를 가집니다.
status
월렛 상태를 decoded payload만으로 판단하지 마세요. 실행 상태는 tx_result.code/log를 사용하고, decoded transaction은 사용자가 서명한 내용을 표시하는 데 사용하세요.
result.tx decode 예시
| 필드 | 디코딩 값 | 인코딩 / 표시 규칙 |
|---|---|---|
version |
1 |
Transaction version입니다. 정수로 표시합니다. |
time |
1782980166274000000 |
Unix nanoseconds입니다. timestamp로 표시하려면 nanoseconds 기준으로 변환하세요. |
nonce |
20 |
account RPC에서 읽은 sender nonce를 그대로 사용한 값입니다. decimal string/integer로 표시합니다. |
from |
D9AD188848077A9F0368E75F7733113FA9B9063D |
20바이트 address bytes입니다. hex로 표시하세요. 요청은 0x prefix를 허용하고, raw node 응답은 보통 0x를 생략합니다. |
to |
960311536E7A036CE25778C79924E2644680A34E |
20바이트 address bytes입니다. hex로 표시하세요. 요청은 0x prefix를 허용하고, raw node 응답은 보통 0x를 생략합니다. |
_amount |
0 fons |
uint256 minimal big-endian bytes입니다. 빈 bytes는 0 fons입니다. UI에는 decimal fons 또는 BEATOZ로 변환해 표시하세요. |
gas |
2000000 |
Gas limit입니다. decoded 값은 정수이고, RPC result의 gas 필드는 문자열입니다. |
_gasPrice |
48000000000 fons |
uint256 minimal big-endian bytes입니다. decimal fons로 변환하세요. 이 예시는 48,000,000,000 fons입니다. |
type |
6 (TRX_CONTRACT) |
type 6은 TRX_CONTRACT입니다. transaction type 표를 보고 payload decoder를 선택하세요. |
_payload._data |
0x40C10F1900000000000000000000000076B8B3BAD69A77DC60C7EB2D516A1BB487F95DC10000000000000000000000000000000000000000000069E10DE76676D0800000 |
TRX_CONTRACT는 _payload를 TrxPayloadContractProto로 proto-unmarshal하고, _data는 raw EVM calldata bytes로 취급해 hex로 표시합니다. |
sig |
r || s || v, v = 1 |
65바이트 r || s || v signature입니다. v=0/1은 legacy V0, v=27/28은 V1입니다. 이 과거 샘플은 v=1입니다. |
Event attribute decode
Tendermint는 event attribute key와 value를 base64로 인코딩합니다. 월렛 label이나 filter를 만들기 전에 먼저 decode하세요.
function base64ToUtf8(value) {
return new TextDecoder().decode(Uint8Array.from(atob(value), c => c.charCodeAt(0)));
}
function normalizeAddress(value) {
return value.replace(/^0x/i, "").toUpperCase(); // 40 hex chars, no 0x
}
function parseBeatozEvents(events) {
return (events || []).map(event => {
const parsed = {
type: event.type, // plain text: "tx", "evm", ...
attributes: {},
indexed: {}, // values usable in tx_search when attr.index === true
};
for (const attr of event.attributes || []) {
const key = base64ToUtf8(attr.key);
const rawValue = base64ToUtf8(attr.value);
let value = rawValue;
if (["sender", "receiver", "contractAddress"].includes(key)) {
value = normalizeAddress(rawValue);
} else if (key === "addrpair" && rawValue.length === 80) {
value = {
sender: normalizeAddress(rawValue.slice(0, 40)),
receiver: normalizeAddress(rawValue.slice(40, 80)),
};
} else if (key === "amount") {
value = { fons: rawValue };
} else if (key.startsWith("topic.") || key === "data") {
value = `0x${rawValue}`; // EVM log topic/data; base64 payload is ASCII hex
}
parsed.attributes[key] = value;
if (attr.index === true) {
// Use event.type + "." + key in tx_search, e.g. tx.sender or evm.topic.2.
parsed.indexed[key] = rawValue; // decoded raw string without 0x
}
}
return parsed;
});
}
[
{
"type": "evm",
"attributes": {
"contractAddress": "960311536E7A036CE25778C79924E2644680A34E",
"topic.0": "0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF",
"topic.1": "0x0000000000000000000000000000000000000000000000000000000000000000",
"topic.2": "0x00000000000000000000000076B8B3BAD69A77DC60C7EB2D516A1BB487F95DC1",
"data": "0x0000000000000000000000000000000000000000000069e10de76676d0800000",
"blockNumber": "0",
"removed": "false"
},
"indexed": {
"topic.0": "DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF",
"topic.1": "0000000000000000000000000000000000000000000000000000000000000000",
"topic.2": "00000000000000000000000076B8B3BAD69A77DC60C7EB2D516A1BB487F95DC1"
}
},
{
"type": "tx",
"attributes": {
"type": "contract",
"sender": "D9AD188848077A9F0368E75F7733113FA9B9063D",
"receiver": "960311536E7A036CE25778C79924E2644680A34E",
"addrpair": {
"sender": "D9AD188848077A9F0368E75F7733113FA9B9063D",
"receiver": "960311536E7A036CE25778C79924E2644680A34E"
},
"amount": { "fons": "0" },
"status": "0"
},
"indexed": {
"type": "contract",
"sender": "D9AD188848077A9F0368E75F7733113FA9B9063D",
"receiver": "960311536E7A036CE25778C79924E2644680A34E",
"addrpair": "D9AD188848077A9F0368E75F7733113FA9B9063D960311536E7A036CE25778C79924E2644680A34E"
}
}
]
tx_search
For tx.sender/tx.receiver, use the decoded uppercase 20-byte address without 0x. For evm.topic.N, use the full 32-byte ABI topic hex without 0x; indexed address topics are 12 zero bytes followed by the 20-byte address.
addrpair/status
Legacy/deployment-dependent tx event fields. Current BTIP35 success events should not require addrpair or status for wallet history.
evm.blockNumber
EVM log blockNumber is not the Tendermint transaction height. Use result.height for wallet history height.
evm.topic/data
EVM topic/data values base64-decode to ASCII hex strings that represent ABI words. Add 0x/hex-decode them before ABI decoding. For ERC-20 Transfer, indexed addresses are the last 20 bytes of topic.1/topic.2.
| 인코딩된 attribute | 디코딩 값 | 인코딩 / 표시 규칙 |
|---|---|---|
Y29udHJhY3RBZGRyZXNz / OTYwMzEx... |
contractAddress = 960311536e7a036ce25778c79924e2644680a34e |
key/value를 base64 decode한 뒤 address 값은 20바이트 hex로 정규화합니다. |
dHlwZQ== / Y29udHJhY3Q= |
type = contract |
일반 텍스트 값은 UTF-8 string으로 표시합니다. |
c2VuZGVy / RDlBRDE4... |
sender = D9AD188848077A9F0368E75F7733113FA9B9063D |
tx.sender/tx.receiver 검색에는 decode한 20바이트 uppercase hex address를 0x 없이 사용하세요. evm.topic.N 검색에는 전체 32바이트 ABI topic hex를 0x 없이 사용해야 하며, indexed address topic은 12바이트 zero + 20바이트 address 형식입니다. |
cmVjZWl2ZXI= / OTYwMzEx... |
receiver = 960311536E7A036CE25778C79924E2644680A34E |
tx.sender/tx.receiver 검색에는 decode한 20바이트 uppercase hex address를 0x 없이 사용하세요. evm.topic.N 검색에는 전체 32바이트 ABI topic hex를 0x 없이 사용해야 하며, indexed address topic은 12바이트 zero + 20바이트 address 형식입니다. |
YW1vdW50 / MA== |
amount = 0 |
event amount는 UTF-8 decimal string입니다. 단위는 fons로 해석하세요. |
dG9waWMuMA== / RERGMjUy... |
topic.0 = DDF252AD... |
EVM topic/data 값은 base64 decode 후 ASCII hex로 나오며, UI에서는 필요하면 0x prefix를 붙입니다. |
History Search
Use tx_search to discover candidate account history from indexed transaction events. For custody/deposit credit, always perform direct tx lookup, decode result.tx, and verify tx_result.code == 0, type, to, and amount.
Per-account history with tx_search
Successful executed transactions emit indexed "tx" events with sender and receiver attributes. Build display history by searching both directions with namespaced keys such as tx.sender and tx.receiver, then fetch each tx by hash for canonical details. Do not credit deposits from tx.receiver alone: other successful transaction types can emit tx.receiver as tx.To. Failed DeliverTx is not reliably discoverable through tx_search: current BTIP35 failure responses can contain no events at all, while older failure paths may index only the sender. For failed transactions, prefer direct tx lookup when the wallet already knows the hash.
// Outgoing transactions
{ "method": "tx_search", "params": {
"query": "tx.sender='13BB453C5D5FE390D2AF30F345E679B3E6AB9D74'",
"prove": false, "page": "1", "per_page": "30", "order_by": "desc" } }
// Incoming transactions
{ "method": "tx_search", "params": {
"query": "tx.receiver='13BB453C5D5FE390D2AF30F345E679B3E6AB9D74'",
"prove": false, "page": "1", "per_page": "30", "order_by": "desc" } }
// Also indexed: tx.type (e.g. tx.type='transfer'), tx.height=<n>
// Note: event attribute keys/values inside results are base64-encoded.
REP-20 / ERC-20 token history
Search EVM logs with evm.topic.0 = Transfer event signature and evm.topic.1 or evm.topic.2 = 32-byte ABI topic (12 zero bytes + 20-byte address). evm.contractAddress is not indexed for tx_search; after finding candidate hashes, fetch tx details and filter events client-side by decoded contractAddress.
// REP-20 / ERC-20 Transfer(address,address,uint256)
// topic.0 = keccak256("Transfer(address,address,uint256)")
// topic.1/topic.2 = 32-byte ABI topic: 000000000000000000000000 + 20-byte address
{ "method": "tx_search", "params": {
"query": "evm.topic.0='DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF' AND evm.topic.2='00000000000000000000000013BB453C5D5FE390D2AF30F345E679B3E6AB9D74'",
"prove": false, "page": "1", "per_page": "30", "order_by": "desc" } }
Transaction Results & Error Codes
Broadcast and lookup responses include both transport-level JSON-RPC status and application-level execution status. Inspect the JSON-RPC error first, then the result code fields. Note that int64 values (height, gas) are returned as strings.
| Field | Type | Description |
|---|---|---|
hash |
HexString |
Transaction hash: SHA-256 of the signed proto bytes (uppercase hex in responses). |
height |
string |
Block height where the transaction was included, as a string. |
index |
number |
Transaction index inside the block. |
tx |
base64 string |
Raw signed TrxProto bytes returned by the tx lookup RPC. |
tx_result.code |
number |
Application result code. 0 means success — see the error code table below. |
tx_result.log |
string |
Human-readable failure reason. This is where the actual cause (invalid nonce, insufficient fund, ...) appears. |
tx_result.gas_wanted |
string |
Gas requested by the transaction. |
tx_result.gas_used |
string |
Gas charged. For non-EVM types this equals the full gas limit (see Fees & Gas). |
tx_result.events |
Event[] |
Events emitted by execution. Attribute keys and values are base64-encoded in JSON responses. |
proof |
TxProof? |
Included only when the tx query requests proof. |
Result codes wallets should map
| Code | Meaning |
|---|---|
0 |
Success. |
3 |
CheckTx (mempool validation) failed. ALL validation failures — wrong nonce, wrong gasPrice, bad signature, insufficient balance — share this code; the specific cause is only in the log string. Parse log for user-facing messages. |
5 |
DeliverTx failed after the tx was included in a block. If failure happens after validation during execution, nonce can still increment and fee can still be charged; show the log reason and refresh account state. |
Quickstart
The SDK is useful for JavaScript/TypeScript integration examples, account lookup, unit helpers, and RPC wrappers. Important: the current SDK signer still uses the legacy V0 preimage (v = 0/1), its transaction type omits payer/payer_sig, its proposal payload field numbers differ from the Go proto, and TrxProtoBuilder.signedRawTrxProto hard-codes localnet0. Use account.signTransaction(tx, CHAIN_ID) for SDK examples, and use the Transactions chapter and Go proto as the canonical spec for new V1 wallet signing, fee-delegated transactions, and TRX_PROPOSAL.
npm install @beatoz/web3 @beatoz/web3-accounts
import { Web3 } from '@beatoz/web3';
const RPC_URL = 'https://rpc-testnet0.beatoz.io/';
const web3 = new Web3(RPC_URL);
const status = await web3.beatoz.status();
console.log(status.node_info.network); // 0xbea701
console.log(status.sync_info.latest_block_height);
Account Lookup
The SDK accepts addresses with or without the 0x prefix. Note two SDK-side decodes: nonce is converted to a JavaScript number (the raw RPC returns it as a string), and the raw QueryResult height field is not exposed on the SDK response.
import { Web3 } from '@beatoz/web3';
const web3 = new Web3('https://rpc-testnet0.beatoz.io/');
const account = await web3.beatoz.getAccount('0x13BB453C5D5FE390D2AF30F345E679B3E6AB9D74');
console.log(account.value.address); // uppercase hex without 0x, as returned by the node
console.log(account.value.nonce); // number - use as-is for the next tx
console.log(account.value.balance); // string, balance in fons
Transfer Transaction
Native BEATOZ transfers can be built with TrxProtoBuilder and submitted through broadcastRawTxCommit. The current SDK account signer uses the legacy V0 signing scheme; for new wallet integrations that need V1, implement the Signing chapter directly or wait for an updated SDK signer. Do not use TrxProtoBuilder.signedRawTrxProto on testnet because it hard-codes localnet0 internally. broadcastRawTxSync waits for initial CheckTx only; broadcastRawTxAsync is the non-blocking submission variant.
import { Web3, TrxProtoBuilder } from '@beatoz/web3';
import { privateKeyToAccount } from '@beatoz/web3-accounts';
const RPC_URL = 'https://rpc-testnet0.beatoz.io/';
const CHAIN_ID = '0xbea701';
const PRIVATE_KEY = process.env.BEATOZ_PRIVATE_KEY;
const RECIPIENT_ADDRESS = process.env.BEATOZ_RECIPIENT_ADDRESS;
const web3 = new Web3(RPC_URL);
if (!PRIVATE_KEY) throw new Error('Set BEATOZ_PRIVATE_KEY before broadcasting.');
if (!RECIPIENT_ADDRESS) throw new Error('Set BEATOZ_RECIPIENT_ADDRESS before broadcasting.');
const account = privateKeyToAccount(PRIVATE_KEY);
const accountInfo = await web3.beatoz.getAccount(account.address);
const params = await web3.beatoz.rule(); // gov_params (rule is the legacy alias)
const tx = TrxProtoBuilder.buildTransferTrxProto({
from: accountInfo.value.address,
to: RECIPIENT_ADDRESS,
nonce: accountInfo.value.nonce, // use as-is, no +1
amount: web3.utils.toFons('1', 'beatoz'),
gas: Number(params.value.minTrxGas), // full gas is charged - do not overprovision
gasPrice: params.value.gasPrice // must equal the network value exactly
});
const { rawTransaction, transactionHash } = account.signTransaction(tx, CHAIN_ID);
const result = await web3.beatoz.broadcastRawTxCommit(rawTransaction);
// Do not treat deliver_tx presence as success; CheckTx failures can include an empty object.
const ok = result.check_tx?.code === 0 && result.deliver_tx?.code === 0;
console.log(transactionHash, ok);
Core Calls
web3.beatoz.status()
Node and sync status.
web3.beatoz.getAccount(address)
Address, nonce, and balance.
web3.beatoz.rule()
Governance parameters (gasPrice, minTrxGas, blockGasLimit, staking minimums, ...). Same data as the gov_params RPC.
web3.beatoz.tx(hash)
Transaction lookup by hex transaction hash. SDK tx(hash) base64-encodes the hash internally and requests proof=true.
web3.beatoz.txSearch(query, ...)
Event-indexed transaction search — use for per-account history (see Lookup & History).
web3.beatoz.broadcastRawTxCommit(rawTx)
Submit a signed transaction and wait for the commit result. Sync/Async variants: broadcastRawTxSync, broadcastRawTxAsync.
Response Notes
Raw RPC response schemas are documented per-method in the RPC Reference. The SDK decodes a few fields differently from the raw JSON — these differences matter when switching between SDK and raw RPC code paths.
SDK decoding vs raw RPC
| Field | Raw RPC JSON | SDK value |
|---|---|---|
account value.nonce |
string ("12") |
number (12) |
account height |
string |
Not exposed — the SDK AccountResponse has no height field. |
broadcastRawTxCommit height |
string |
number |
broadcastRawTxCommit deliver_tx |
Present on success; CheckTx failures can return a zero-valued/empty object | Do not use presence as success. Check check_tx.code and deliver_tx.code. |
vm_call value.returnData |
base64 string, omitted when empty | web3.beatoz.vmCall() returns raw base64 when returnData is present; empty-return calls may omit returnData. Contract helper call() converts returnData to hex. |
tx result.tx |
base64 string containing raw signed TrxProto bytes. | The current SDK type declares HexString, but the decoder passes through the raw base64 tx field. Decode it as base64 before proto3 unmarshaling. |
rule() value |
Runtime JSON uses blockGasLimit, minValidatorPower, ... (see gov_params in the RPC Reference). Several pass-through SDK types can lag runtime JSON keys. | Passed through as-is. Caution: the SDK's TypeScript RuleResponse and some staking/VM response types declare field names or presence differently from runtime JSON — trust the live RPC/runtime keys. |
RPC List
Each read-only RPC card can call the testnet endpoint with the request envelope shown below. Mutation/broadcast routes show request/response schemas but the live button is disabled in the public guide. This wallet reference focuses on BEATOZ native/testnet integration routes. Do not rely on Ethereum JSON-RPC compatibility for wallet integration: the testnet endpoint exposes only a limited eth_* subset (eth_chainId, eth_blockNumber, eth_getBlockByNumber), and does not provide full Ethereum-shaped balances, nonces, eth_call, eth_estimateGas, eth_getLogs, receipts, or eth_sendRawTransaction. Remember the encoding conventions: BEATOZ-native methods accept 0x-optional hex for addresses/hashes, most Tendermint byte params take base64, abci_query.data takes hex without 0x, int64 response values are strings, and height/page params must be sent as strings.