Understanding Transactions
A transaction must be encoded and signed by the sender's wallet, once sent to the chain, it's decoded and verified, and then executed and appended to the ledger.
- A transaction is identified by
hash
similar to other chains such as Bitcoin and Ethereum. - A transaction is encoded as binary before being signed and sent to the chain.
- A transaction is decoded and formatted before being displayed on the chain explorer.
Transaction Structure#
To make transactions flexible and extensible, we use a mail-like structure to define a transaction:
envelope
=outer transaction
: contains required fields for all transactions, such assender
,signature
letter
=inner transaction
: the actual transaction content, such asDeclareTx
,DelegateTx
You can inspect the "Raw Transaction" on transaction detail from the explorer to get a better understanding of the structure.
Outer Transaction#
The outer transaction structure is defined as follows:
type Transaction = {
from: string; // the transaction sender address
pk: string; // the transaction sender public key
nonce: number;
chainId: string; // must match with the chain
delegator: string;
signature: string; // the transaction sender's signature
signatures: Array<Multisig>; // signatures from other parties, default to empty
itx: Any;
};
Inner Transaction (itx)#
The inner transaction structure varies across different transaction types. Here are some examples:
type DeclareTx = {
moniker: string;
issuer: string;
data?: Any;
};
type TransferV2Tx = {
to: string;
assets: Array<string>;
tokens: Array<{
address: string;
value: string;
}>;
data?: Any;
};
Any Type#
Any type is widely used in transactions to encode data that's attached to the same field but has different types and shapes:
type Any = {
type: string;
value: any;
};
Let's look at how Any
type is used with an example:
const tx = {
from: "zNKnjMDau9aK6kYkxX7fhFkbozQQhvTzGqnX",
pk: "0848uy6x11gle2SR3hummTMyZpLRNv_ai4RaquxIDyg",
chainId: "beta",
nonce: "1663680171202",
signature: "Sd4ALBExbm5ssWo4S2RITqJm48BPcb8s1c4UmO-l6o5x5WW619ZEK",
signatures: [],
delegator: "",
itx: {
type: "DeclareTx",
value: {
moniker: "blocklet-registry-service",
issuer: "",
data: {
type: "json",
value: {
role: "ROLE_APPLICATION",
pk: "ED25519",
hash: "SHA3",
address: "BASE58",
},
},
},
},
};
Any is used in 2 places in above transaction:
itx
field on the outer transaction to attach the inner transaction, which can be any of the supported types.data
filed on the inner transaction to attach optional data that can be any type, such asjson
andvc
.
As you can see from the example Any types can be nested.
Transaction Types#
The transaction filter from chain explorer lists all supported transaction types supported. Each transaction type defines a structure that can be used in itx
.
- Account
- DeclareTx
- AccountMigrateTx
- DelegateTx
- RevokeDelegateTx
- NFT
- CreateAssetTx
- UpdateAssetTx
- MintAssetTx
- AcquireAssetV2Tx
- AcquireAssetV3Tx
- CreateFactoryTx
- Token
- CreateTokenTx
- DepositTokenV2Tx
- WithdrawTokenV2Tx
- Trading
- ExchangeV2Tx: with multiple input support
- TransferV2Tx: with multiple input support
- TransferV3Tx: with multiple input and output support
- Staking
- StakeTx
- RevokeStakeTx
- ClaimStakeTx
- Rollup
- CreateRollupTx
- UpdateRollupTx
- JoinRollupTx
- PauseRollupTx
- ResumeRollupTx
- MigrateRollupContractTx
- MigrateRollupTokenTx
- JoinRollupTx
- LeaveRollupTx
- CreateRollupBlockTx
- ClaimBlockRewardTx
Transaction Data#
Each transaction can have a data
field that is Any
type, see next section.
Transaction Size Limit#
To protect the chain from abuse, there are hard limits for each transaction size, transactions that exceed the size limit will fail at the verification phase.
The current size limit (measured by byteLength of the encoded transaction buffer) for each transaction type is:
- AccountMigrate: 348
- CreateAsset: 65881
- CreateFactory: 65881
- Declare: 664
- ExchangeV2: 1691
- Transfer: 664
- TransferV2: 664
- TransferV3: 4096
- UpdateAsset: 65881
- AcquireAssetV2: 4096
- AcquireAssetV3: 4096
- MintAsset: 4096
- CreateToken: 664
- DepositTokenV2: 1691
- WithdrawTokenV2: 1691
- Stake: 4096
- RevokeStake: 664
- ClaimStake: 664
- CreateRollup: 1691
- UpdateRollup: 1691
- PauseRollup: 1691
- ResumeRollup: 1691
- JoinRollup: 1691
- LeaveRollup: 1691
- MigrateRollupContract: 1691
- MigrateRollupToken: 1691
- CreateRollupBlock: 4096
- ClaimBlockReward: 1691
Transaction Fees#
The Transaction Fee is another mechanism for protecting the chain from attacks. There are 3 types of fees charged when a transaction is successfully executed:
- Service fee: charged when moving tokens with DepositTokenV2Tx and WithdrawTokenV2Tx across ArcBridge, fee rate may vary between bridges.
- Protocol fee: charged when sending
CreateAssetTx
,CreateFactoryTx
,CreateTokenTx
andCreateRollupTx
. - Gas fee: charged when sending all transaction types, neither the main nor beta chain enabled gas fee.
Transaction Signature#
Sender signature#
Each transaction carries a signature generated using the sender's secret key. The chain and recipient can verify the sender's signature using the sender's public key, which is published on the chain. This helps ensure that the transaction content received is exactly as it was when the sender initiated the transaction.
Multi-party signature#
In most chain implementations, only the transaction sender is required to sign the transaction. With the sender's signature, we would trust that the operation is authorized by the sender. Transactions like Transfer
is a typical example that the sender would send some tokens / NFTs to the receiver (without the receiver's permission). In the real-world, vast use cases require authorization among all parties in the transaction.
ArcBlock chain has native support for multi-party-signed transactions. Transactions that support multi-party-signature includes:
- TransferV3Tx: if the transaction input is from multiple payers
- AcquireAssetV3Tx: if the transaction input is from multiple payers
- Rollup Related Tx: signatures are required from all rollup validators