Transaction

Cryptographically signed instructions from accounts are known as transactions. An account initiates a transaction to modify the state of the Pollux network. The most straightforward transaction involves transferring PRC from one account to another.

Transactions that alter the chain's state must be broadcasted across the entire network. Any node has the capability to broadcast a transaction request. Once the super node receives the transaction, it executes the transaction, incorporates it into a block, and then disseminates the block throughout the entire network.

Confirmation of a transaction only occurs after it has been packed into a block by the super node and the block's confirmation is finalized.

The format of a transaction is as follows:

// Javascript
{
    "raw_data": 
    {
        "contract": [{<-->}],
        "ref_block_bytes": "c145",
        "ref_block_hash": "c56bd8a3b3341d9d",
        "expiration": 1646796363000,
        "data": "74657374",
        "timestamp": 1646796304152,
        "fee_limit":10000000000
    },
    "signature":["47b1f77b3e30cfbbfa41d795dd34475865240617dd1c5a7bad526f5fd89e52cd057c80b665cc2431efab53520e2b1b92a0425033baee915df858ca1c588b0a1800" ] 
}

A submitted transaction primarily includes the following fields:

raw_data.contract - This field encompasses the main content of the transaction. Contract is a list, although currently only one element is utilized. Different types of transactions exhibit distinct contract contents. For instance, in a PRC transfer-type transaction, the contract will encompass details such as the transfer amount, receiver address, and other pertinent information. Pollux supports multiple types of contracts; refer to the Types of Transaction section below for specifics.

raw_data.ref_block_bytes - Representing the height of the transaction reference block, this field utilizes the 6th to 8th (exclusive) bytes of the reference block height, totaling 2 bytes. The reference block is integral to the Pollux TAPOS mechanism, preventing the replay of a transaction on forks lacking the referenced block. Typically, the latest solidified block serves as the reference block.

raw_data.ref_block_hash - This field holds the hash of the transaction reference block, using the 8th to 16th (exclusive) bytes of the reference block hash, totaling 8 bytes. The reference block plays a crucial role in the Pollux TAPOS mechanism, preventing the replay of a transaction on forks without the referenced block. Generally, the latest solidified block is designated as the reference block.

raw_data.expiration - Denoting the transaction expiration time, this field signifies the point beyond which the transaction will no longer be included in blocks. If the transaction is generated using the java-pollux API, the node automatically sets its expiration time by adding 60 seconds to the timestamp of the node's latest block. The expiration time interval can be adjusted in the node's configuration file, with a maximum value not exceeding 24 hours.

raw_data.data - Capturing the transaction memo.

raw_data.timestamp - Reflecting the transaction timestamp, indicating the time of transaction sion.

raw_data.fee_limit - Establishing the maximum allowable energy cost for the execution of smart contract transactions. This field only requires configuration for deploying and triggering smart contract transactions, while others remain unaffected.

signature - The sender's signature for the transaction, providing proof that the transaction is authentic and was not sent fraudulently.

Types of Transaction

On Pollux, various transaction types exist, including PRC transfer transactions, PRC10 transfer transactions, deploying smart contract transactions, triggering smart contract transactions, staking PRC transactions, and more.

To generate different transaction types, distinct APIs must be invoked. For instance, the smart contract deployment transaction type is CreateSmartContract; you need to call the wallet/deploycontract API to create a transaction. Similarly, for the staking PRC transaction type, FreezeBalanceV2Contract is used, and you need to call the wallet/freezebalancev2 API to create transactions.

//JSON
$ curl -X POST https://node.poxscan.com/wallet/freezebalancev2 -d '{"owner_address":"PCrkRWJuHP4VgQF3xwLNBAjVVXvxRRGpbA","frozen_balance": 2100000,"resource" : "BANDWIDTH","visible":true}' | jq
{
  "visible": true,
  "txID": "e54bab34838a59e85d5684e46a2e8e512cd11dfb07b35a9728adeaf3d2666fa6",
  "raw_data": {
    "contract": [
      {
        "parameter": {
          "value": {
            "frozen_balance": 2100000,
            "owner_address": "PCrkRWJuHP4VgQF3xwLNBAjVVXvxRRGpbA"
          },
          "type_url": "type.googleapis.com/protocol.FreezeBalanceV2Contract"
        },
        "type": "FreezeBalanceV2Contract"
      }
    ],
    "ref_block_bytes": "7139",
    "ref_block_hash": "d291dee525445093",
    "expiration": 1646902209000,
    "timestamp": 1646902151591
  },
  "raw_data_hex": "0a0271392208d291dee52544509340e8d39598f72f5a58080b12540a32747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e467265657a6542616c616e6365436f6e7472616374121e0a15411fafb1e96dfe4f609e2259bfaf8c77b60c535b9310a0968001180370a7939298f72f"
}

For additional transaction types, please consult: Types of transactions on Pollux. For an extended list of HTTP APIs, please refer to: HTTP API.

Transaction Lifecycle

A transaction undergoes the following stages in its life cycle:

  1. Transaction Creation and Signature:

    • The transaction is initially created and undergoes a signature process.

  2. Broadcast to the Pollux Network:

    • After verification and execution by nodes, including the block-producing node, the transaction is broadcast to the Pollux network.

    • It is then added to a transaction cache pool.

  3. Block Production:

    • The block-producing node sequentially extracts transactions from the transaction cache pool in the order of submission.

    • These transactions are then packaged into a new block.

    • Subsequently, the new block is broadcast to the Pollux network.

  4. Confirmation:

    • The transaction achieves confirmation based on the confirmation of the block it is included in.

    • Pollux's block confirmation mechanism involves 19 different super nodes producing subsequent blocks based on the initially produced block.

    • Once this process is completed, the block is considered confirmed, and by extension, the transactions within it are confirmed as well.

Create Transaction

Numerous libraries and tools are available to create transactions.

Sign Transaction

The process of generating a transaction signature involves the following steps:

  1. Calculate the hash of the transaction.

  2. Sign the transaction hash with the sender's private key.

  3. Add the generated signature to the transaction instance.

Commonly, various SDKs implement the aforementioned transaction signature-generating process and encapsulate it into an interface for developers to utilize.

Broadcast Transaction

Upon receiving a transaction from a user, the node undergoes the process of locally verifying and executing the transaction. Subsequently, valid transactions are broadcasted to other nodes, while invalid transactions are discarded. This mechanism effectively prevents the dissemination of spam transactions through invalid broadcasts in the network.

Transaction Confirmation

Whether a transaction is confirmed relies on the confirmation status of the block in which the transaction is included. Pollux's block confirmation mechanism entails the production of subsequent blocks by 19 different super nodes after the initial block is created, ultimately confirming the block.

The java-pollux node offers the /walletsolidity/* API, facilitating users in querying confirmed transactions. The distinction between /walletsolidity/* and /wallet/* lies in the fact that transactions queried by /wallet/* may indicate they are on the chain but not necessarily confirmed. Conversely, transactions queried by /walletsolidity/* indicate they have been on the chain and solidified, confirming the transaction.

For different transaction types, the confirmation methods vary:

  1. System Contract Transaction:

    • Any transaction queryable through /walletsolidity/gettransactioninfobyid or /walletsolidity/gettransactionbyid API is considered confirmed.

  2. Smart Contract Transaction:

    • For transactions involving the creation or triggering of smart contracts, successful execution can be determined by either finding transactionInfo.receipt.result equal to success via the /walletsolidity/gettransactioninfobyid API or finding transaction.ret.contractRet equal to success via the /walletsolidity/gettransactionbyid API.

  3. Internal Transaction:

    • Internal transactions, involving token transfers to external or contract addresses, can be queried through the /walletsolidity/gettransactioninfobyid API. The rejected field in the internal transaction is utilized to determine confirmation:

      • For HTTP API, rejected is not returned for successful transactions, and for failed transactions, rejected equals true.

      • For GRPC API, rejected equals false for successful transactions, indicating the internal transaction has not been discarded, and rejected equals true for failed transactions.

Transaction Fee

In addition to query operations, every on-chain transaction incurs consumption of system resources. All transaction types require the consumption of bandwidth. Furthermore, both the deployment and execution of smart contracts consume energy in addition to bandwidth. When the account's available bandwidth or energy is insufficient, POX needs to be burned to cover the corresponding resource fee. In addition to resource fees, some special transactions may incur additional fees.

Bandwidth Fee

The bandwidth consumed by a transaction is determined by the number of bytes occupied by the on-chain transaction, encompassing three components: the raw_data of the transaction, the transaction signature, and the transaction result. The total number of bytes occupied by these three parts after protobuf serialization encoding represents the bandwidth consumption of the transaction.

When the acquired bandwidth through staking and the daily free bandwidth in the account are both insufficient, POX must be burned to cover the bandwidth cost:

POX burned to pay for bandwidth = Total bandwidth consumed by the transaction * Bandwidth unit price

The current bandwidth unit price is 1000 RAM.

When the receiver address of POX and PRC10 transfer transaction is an inactivated address, the transaction will activate the receiver address. In this scenario, if the caller address lacks sufficient bandwidth obtained through staking, the transaction will consume 0.1 POX as Bandwidth fee.

For guidance on estimating the bandwidth consumption of a transaction, please refer to here.

Energy Fee

In addition to consuming bandwidth, smart contract deployment and invocation transactions also consume energy. When the contract is executed, Energy is calculated and deducted according to instruction one by one. The energy obtained by staking will be consumed first. If this part of the energy is not enough, the account's POX will continue to be burned to pay for the energy resources required for the transaction.

POX burned to pay for energy fee = (Total amount of energy consumed by the transaction - Amount of energy available in the caller's account) * Energy unit price

The current energy unit price is 420 RAM. For guidance on estimating energy consumption, please refer to here.

Other Fees

For certain unique transactions, aside from resource fees, there are additional charges to be paid. If the transaction initiator includes notes in the transaction, an extra transaction note fee of 1 POX is required. In cases where the transaction involves multiple signatures, meaning the number of signatures in the transaction exceeds 1, the transaction initiator must pay an additional multi-signature fee of 1 POX. Additionally, specific types of transactions listed below incur transaction fees:

Please be aware that for TransferContract or TransferAssetContract type transactions, specifically, POX transfer, PRC10 token transfer, if the target address is not activated, the transaction will also initiate the creation of a new account, and a new account creation fee of 1 POX will be deducted. Simultaneously, if the bandwidth obtained through staking in the transaction initiator's account is insufficient, 0.1 POX needs to be paid as bandwidth fee.

Internal Transactions

The term "transactions" typically denotes actions initiated by external accounts, such as transactions involving smart contract calls. However, during the execution of smart contract transactions, the contract may initiate other contract method invocations, transfer POX/PRC10 tokens to external accounts, or perform operations like staking, voting, resource delegating, etc. Transactions occurring within the context of smart contract execution are termed internal transactions. Consequently, internal transactions are actions triggered within the PVM by contract accounts.

The generation of internal transactions

Coming soon

Use-Cases

Internal transactions offer valuable information for your users, presenting several use cases within a decentralized application (DApp):

  1. Failed Transactions Notifications:

    • When an internal transaction fails, the entire transaction is unsuccessful. Internal transactions can pinpoint the exact location of the fault, facilitating prompt issue identification and resolution. This information is crucial for notifying users about failed transactions.

  2. Smart Contract Monitoring:

    • Deployed smart contracts can engage with other contracts through internal transactions. Monitoring your smart contract address for internal transactions allows you to track when and which contracts it interacts with.

  3. Smart Contract Analytics:

    • Internal transactions can be intricate, making insights valuable. By examining the number of internal transactions executed by a smart contract, you gain an understanding of its performance.

  4. Batch Transactions:

    • When sending a batch of transactions to different sender addresses, internal transactions provide a more convenient and secure means of ensuring they reach the intended addresses.

The preservation of internal transactions

Numerous scenarios benefit from internal transactions, offering guidance and insights into transaction execution. However, the default setting of the Pollux node does not automatically store internal transaction information. To enable this feature, manual configuration adjustments are necessary through the node configuration file:

vm = {
    ...
  saveInternalTx = true
  saveFeaturedInternalTx = true
    ...
}

saveInternalTx: Indicates whether to preserve internal transactions.

saveFeaturedInternalTx: When saveInternalTxis enabled, signifies whether to store Stake2.0-related internal transactions.

The examples of internal transactions

The node's saved internal transaction includes the following details:

  • hash: The hash value of the internal transaction.

  • caller_address: Caller's address.

  • transferTo_address: The calling contract address or the account address receiving POX/PRC10 tokens.

  • callValueInfo.callValue: The amount of POX/PRC10 tokens transferred.

  • callValueInfo.tokenId: The PRC10 name or ID of the transfer; when transferring POX, this field is empty.

  • note: Instruction type, such as call, create, suicide, freezeBalanceV2ForEnergy, freezeBalanceV2ForBandwidth, unfreezeBalanceV2ForBandwidth, etc.

  • rejected: Indicates whether the internal transaction execution failed; true means the execution failed.

  • extra: Currently, primarily used to save voting information and record the voting SR and its number of votes in JSON format.

Let's examine the information encompassed in internal transactions through various examples:

1. Smart contract calls other smart contract methods

Here is an example: the transaction involves an external address calling the "PQn9Y...." contract, and within the "PQn9Y...." contract, it calls the "PR7NHq...." contract:

{
    "id": "50e6dd05c37b8666cf4a689fe6c0d52053b76b53d8649b256e6b9dca8c9df098",
    ......
    "internal_transactions": [
        {
            "hash": "380f4d87271b83afcf5e867271ee2d30b36c19d3eeb15a043477bce7fd5b2079",
            "caller_address": "PQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
            "transferTo_address": "PR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
            "callValueInfo": [
                {}
            ],
            "note": "63616c6c"
        },
        .......
    ]
}

The details within the aforementioned internal transactions are as follows:

  • internal_transactions.caller_address: This represents the address of the caller, specifically, the contract address directly called by the external address.

  • internal_transactions.transferTo_address: This signifies the address of another contract invoked within the contract.

  • internal_transactions.callValueInfo: In this instance, the contract did not transfer POX/PRC10 tokens using a value when invoking other contracts. Therefore, the value of this field is empty. If value information is included when the contract calls other contract functions, it will be reflected through this field.

  • internal_transactions.note: This field serves as the instruction description, presented in Hex format. Once converted to a string, it reveals the operation information in plain text. In this example, it denotes the operation as "call."

2. Smart contract transfers POX to external accounts

Here is an example: the transaction involves an external address calling the "PQn9Y...." contract, and within the "PQn9Y...." contract, it transfers POX to the "PQnpn...." address:

{
    "id": "50e6dd05c37b8666cf4a689fe6c0d52053b76b53d8649b256e6b9dca8c9df098",
    ......
    "internal_transactions": [
        ......
        {
            "hash": "f47fede2a45e722e6406421d0df16142e159ae7404525de5a595f4fc0c357e26",
            "caller_address": "PQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
            "transferTo_address": "PQnpnLZJYMzH5xku535rAiYTnqYXTDTEHQ",
            "callValueInfo": [
                {
                    "callValue": 4514968563
                }
            ],
            "note": "63616c6c"
        },
        ......
    ]
}
  • internal_transactions.caller_address: This is the address of the caller, specifically, the address of the contract directly called by the external address.

  • internal_transactions.transferTo_address: This indicates the target address for transferring POX.

  • internal_transactions.callValueInfo[0].callValue: This represents the transfer amount of POX, measured in RAM.

  • internal_transactions.note: The instruction description in Hex format. Once converted to a string, it provides the operation information in plain text. In this example, it denotes the operation as "call."

3. Smart contract stakes POX

Here is an example: the transaction involves an external address calling the "PVMb...." contract. Within "PVMb....," the contract stakes 1000 POX to obtain energy:

{
    "id": "9d25a4fe417e0c7540cc5c5841e1d8c9215aec556d9b06e18910ed8b5088f0d8",
    ......
    "internal_transactions": [
        {
            "hash": "a3a4d666e7bf0729bbd8b5e5ad7afb7f8dd20191e7298ea9dbd17af345c96ed5",
            "caller_address": "PVMbhYhurKv4T3xAHQKZCeP4DtFCmWLMt",
            "transferTo_address": "PVMbhYhurKv4T3xAHQKZCeP4DtFCmWLMt",
            "callValueInfo": [
                {
                    "callValue": 1000000000
                }
            ],
            "note": "667265657a6542616c616e63655632466f72456e65726779"
        }
    ]
}
  • internal_transactions.caller_address: This is the address of the stake initiator, namely, the address of the contract directly called by the external address.

  • internal_transactions.transferTo_address: This represents the resource receiving address, which is the stake initiator address. It is also the address of the contract directly called by the external address.

  • internal_transactions.callValueInfo[0].callValue: This denotes the staked POX amount (in RAM).

  • internal_transactions.note: The instruction description in Hex format. The plain text instruction information can be obtained after converting it into a string. In this example, it is "freezeBalanceV2ForEnergy," indicating that the contract stakes POX to obtain energy. If the contract stakes POX to obtain bandwidth, the value of this field is "freezeBalanceV2ForBandwidth."

4. Smart contract unstakes POX

Here is an example: the transaction involves an external address calling the "PVMb...." contract. Within "PVMb....," the contract performs the unstaking operation to unstake the 100 POX staked to obtain bandwidth:

{
    "id": "dc110091fbd1568f8b264f287c7e0896d1afaf47b906a9e684fd17d57c7a1151",
    ......
    "internal_transactions": [
        {
            "hash": "16f73bdad5e9f984e082909b1028fff0b9865952131e681ca887446f8ec89918",
            "caller_address": "PVMbhYhurKv4T3xAHQKZCeP4DtFCmWLMt",
            "transferTo_address": "PVMbhYhurKv4T3xAHQKZCeP4DtFCmWLMt",
            "callValueInfo": [
                {
                    "callValue": 100000000
                }
            ],
            "note": "756e667265657a6542616c616e63655632466f7242616e647769647468"
        }
    ]
}
  • internal_transactions.caller_address: This is the address of the unstaking initiator, namely, the address of the contract directly called by the external address.

  • internal_transactions.transferTo_address: This represents the POX receiving address, which is the contract address directly called by the external address.

  • internal_transactions.callValueInfo[0].callValue: This denotes the amount of POX unstaked (in RAM).

  • internal_transactions.note: The instruction description. In this example, it is "unfreezeBalanceV2ForBandwidth," indicating that the contract unstakes the POX staked for obtaining bandwidth. If the contract unstakes the POX staked for obtaining energy, the value of this field is "unfreezeBalanceV2ForEnergy."

5. Smart contracts delegate resources to other accounts

Here is an example: the transaction involves an external address calling the "PVMb...." contract. Within "PVMb....," the contract delegates the energy share of 500,000,000 RAM to the "PVMznH...." address:

{
    "id": "342daa21f8865786295c45bb80e2f257740091e4e1a3a546b90daa51bcbcbd18",
    ......
    "internal_transactions": [
        {
            "hash": "58382a79c3af68c472383580309a81a9322e7520a48b6463917ba9219ca32a7d",
            "caller_address": "PVMbhYhurKv4T3xAHQKZCeP4DtFCmWLMt",
            "transferTo_address": "PVMznHJfHe6gdYY7gvWmf6bNZHuPHDZtowf",
            "callValueInfo": [
                {
                    "callValue": 500000000
                }
            ],
            "note": "64656c65676174655265736f757263654f66456e65726779"
        }
    ]
}
  • internal_transactions.caller_address: This is the resource delegate address, namely, the address of the contract directly called by the external address.

  • internal_transactions.transferTo_address: This represents the resource receiving address.

  • internal_transactions.callValueInfo[0].callValue: This denotes the delegated resource share (unit is RAM).

  • internal_transactions.note: The instruction description. In this example, it is "delegateResourceOfEnergy," indicating that the contract delegates energy resources. If it is a contract delegating bandwidth, the field value is "delegateResourceOfBandwidth."

6. The smart contract cancels the resource delegation for other accounts

Here is an example: the transaction involves an external address calling the "PU8Mb...." contract. Within "PU8Mb....," the contract cancels the delegation of 200,000,000 RAM energy shares for the "PUznH...." address:

{
    "id": "aa3961ffb0781d8b66d5e22368e92708135dac9c81eac1e2adcaa8546d729bc8",
    ......
    "internal_transactions": [
        {
            "hash": "1a8524704098770d9c6535e1112d1fb91855363c8366c93810f3cb56e8ee12bf",
            "caller_address": "PU8MbhYhurKv4T3xAHQKZCeP4DtFCmWLMt",
            "transferTo_address": "PUznHJfHe6gdYY7gvWmf6bNZHuPHDZtowf",
            "callValueInfo": [
                {
                    "callValue": 200000000
                }
            ],
            "note": "756e44656c65676174655265736f757263654f66456e65726779"
        }
    ]
}
  • internal_transactions.caller_address: This is the resource delegate address, that is, the address of the contract directly called by the external address.

  • internal_transactions.transferTo_address: This represents the resource receiving address, which is the address to cancel the resource delegation.

  • internal_transactions.callValueInfo[0].callValue: This denotes the amount of POX to undelegate (unit is RAM).

  • internal_transactions.note: The instruction description. In this example, it is "unDelegateResourceOfEnergy," signifying the cancellation of the energy resource delegation. If the contract cancels the bandwidth resource delegation, the value of this field is "unDelegateResourceOfBandwidth."

7. Smart contract votes for super representatives

Here is an example: the transaction involves an external address calling the "PNaDY...." contract. Within "PNaDY....," the contract votes 200 and 400 for the super representatives "PUoHa...." and "PUznH....," respectively:

{
    "id": "58506325f692eee0bd730d97a0086f8b0c50e8aa5392b9e4b0edd5fb0916a718",
    ......
    "internal_transactions": [
        {
            "hash": "792b26cb6fbd1c92030721c62f7fd14522a94f412e04b05442d78e1ce743c9f4",
            "caller_address": "PNaDYZaXEpL1LY8Uk4LtGTwwQrGzXTwss9",
            "callValueInfo": [
                {}
            ],
            "note": "766f74655769746e657373",
            "extra": "{\"votes\":[{\"vote_address\":\"PUoHaVjx7n5xz8LwPRDckgFrDWhMhuSuJM\",\"vote_count\":200},{\"vote_address\":\"PUznHJfHe6gdYY7gvWmf6bNZHuPHDZtowf\",\"vote_count\":400}]}"
        }
    ],
    ......
}
  • internal_transactions.caller_address: This is the address voting for the super representative, which is the contract address directly called by the external address.

  • internal_transactions.transferTo_address: For contract voting transactions, the value of this field is empty, so it is not displayed in the returned result.

  • internal_transactions.callValueInfo: For contract voting transactions, the value of this field is an empty array.

  • internal_transactions.note: This is the instruction description. In this case, it is "voteWitness," signifying the super representative voting operation.

  • internal_transactions.extra: Voting details, recording the voting SR and its votes in JSON format: votes[i].vote_address is the SR address, votes[i].vote_count is the number of votes.

8. Smart Contract Withdrawals Rewards

Here is an example: the transaction involves an external address calling the "PNaDY...." contract. Within "PNaDY....," the contract withdraws a reward of 443,803,418 RAM:

{
    "id": "8dc24b5ce1399b553cd173529e77b22172d9abf31e9f50dd32c473bcc5234b71",
    ......
    "internal_transactions": [
        {
            "hash": "e5b047a4a3d64407c93a9e667a083fe52c52b24e859e3a44488249436e79c8d4",
            "caller_address": "PNaDYZaXEpL1LY8Uk4LtGTwwQrGzXTwss9",
            "transferTo_address": "PNaDYZaXEpL1LY8Uk4LtGTwwQrGzXTwss9",
            "callValueInfo": [
                {
                    "callValue": 443803418
                }
            ],
            "note": "7769746864726177526577617264"
        }
    ]
}
  • internal_transactions.caller_address: This is the address for withdrawing rewards, which is the contract address directly called by the external address.

  • internal_transactions.transferTo_address: This is the address to receive the reward, which is the contract address directly called by the external address.

  • internal_transactions.callValueInfo[0].callValue: This is the withdrawn POX reward amount, measured in RAM.

  • internal_transactions.note: This is the instruction description. In this example, it is "withdrawReward," indicating the withdrawal of the reward.

Last updated