This doc goes into the required features for an SDK, and what they should support.
It should support serialization and deserialization of:
- unsigned integers u8, u16, u32, u64, u128, u256
- signed integers i8, i16, i32, i64, i128, i256 (Move/Aptos has no signed integer types; not implemented)
- booleans bool
- vectors
- tuples
- fixed length vectors (and addresses)
Serialization should be provided as the native way for the language of the SDK. More details of the specification can be found here
Transactions should be able to be signed and authorized with the following key types:
- Ed25519 (standalone) -
TransactionAuthenticator::Ed25519(variant 0) - MultiEd25519 -
TransactionAuthenticator::MultiEd25519(variant 1) - Ed25519 (Single Key) -
TransactionAuthenticator::SingleSender>AccountAuthenticator::SingleKey>AnyPublicKey::Ed25519(variant 0) - Secp256k1 (Single Key) -
TransactionAuthenticator::SingleSender>AccountAuthenticator::SingleKey>AnyPublicKey::Secp256k1Ecdsa(variant 1) - Secp256r1 (Single Key) -
TransactionAuthenticator::SingleSender>AccountAuthenticator::SingleKey>AnyPublicKey::Secp256r1Ecdsa(variant 2) - MultiKey -
TransactionAuthenticator::SingleSender>AccountAuthenticator::MultiKey(variant 3) - (Optional) Keyless - not yet supported (the previous implementation was removed as chain-incompatible). When re-added it must ride inside
TransactionAuthenticator::SingleSender>AccountAuthenticator::SingleKey>AnyPublicKey::Keyless(variant 3); seecrates/aptos-sdk/feature-plans/09-keyless-accounts.md.
-
Build a
RawTransactionwith BCS serialization:RawTransaction { sender: AccountAddress (32 bytes) sequence_number: u64 (little-endian) payload: TransactionPayload (enum) max_gas_amount: u64 (little-endian) gas_unit_price: u64 (little-endian) expiration_timestamp_secs: u64 (little-endian) chain_id: ChainId (1 byte) } -
Create signing message:
signing_message = SHA3-256_prefix || BCS(RawTransaction)- The prefix is:
b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193(SHA3-256 of "APTOS::RawTransaction")
- The prefix is:
-
Sign the signing message with the appropriate key type
-
Create
SignedTransaction=RawTransaction + TransactionAuthenticator
| Variant | Index | Description |
|---|---|---|
| Ed25519 | 0 | Single Ed25519 signature (97 bytes: 1 + 32 + 64) |
| MultiEd25519 | 1 | K-of-N Ed25519 multisig |
| MultiAgent | 2 | Multiple independent signers |
| FeePayer | 3 | Sponsored transaction |
| SingleSender | 4 | Modern unified format (recommended) |
| Variant | Index | Description |
|---|---|---|
| Ed25519 | 0 | Legacy Ed25519 |
| MultiEd25519 | 1 | Legacy multi-Ed25519 |
| SingleKey | 2 | Modern single-key (supports multiple algorithms) |
| MultiKey | 3 | Modern multi-key (supports heterogeneous keys) |
| NoAccountAuthenticator | 4 | No authentication |
| Abstract | 5 | Abstract authentication |
| Variant | Index | Key Size |
|---|---|---|
| Ed25519 | 0 | 32 bytes |
| Secp256k1Ecdsa | 1 | 65 bytes (uncompressed) |
| Secp256r1Ecdsa | 2 | 65 bytes (uncompressed) |
| Keyless | 3 | Variable |
| FederatedKeyless | 4 | Variable |
| Variant | Index | Signature Size |
|---|---|---|
| Ed25519 | 0 | 64 bytes |
| Secp256k1Ecdsa | 1 | 64 bytes (r || s) |
| WebAuthn | 2 | Variable |
| Keyless | 3 | Variable |
Authentication keys must be able to be created for accounts that use:
- Ed25519 (standalone):
auth_key = SHA3-256(public_key || 0x00)(scheme_id = 0) - MultiEd25519:
auth_key = SHA3-256(serialized_multi_public_key || 0x01)(scheme_id = 1) - Ed25519 (Single Key):
auth_key = SHA3-256(BCS(AnyPublicKey::Ed25519) || 0x02)(scheme_id = 2) - Secp256k1 (Single Key):
auth_key = SHA3-256(BCS(AnyPublicKey::Secp256k1) || 0x02)(scheme_id = 2) - Secp256r1 (Single Key):
auth_key = SHA3-256(BCS(AnyPublicKey::Secp256r1) || 0x02)(scheme_id = 2) - MultiKey:
auth_key = SHA3-256(BCS(MultiKey) || 0x03)(scheme_id = 3) - (Optional) Keyless: not yet supported (removed as chain-incompatible). When re-added the correct derivation is
auth_key = SHA3-256(BCS(AnyPublicKey::Keyless) || 0x02)(scheme_id = 2, the SingleKey scheme -- keyless has no dedicated scheme byte); seecrates/aptos-sdk/feature-plans/09-keyless-accounts.md.
Important: The same key using different authenticator formats (e.g., Ed25519 standalone vs SingleKey(Ed25519)) will produce different addresses due to different scheme IDs.
-
For SingleKey authentication,
BCS(AnyPublicKey)includes the variant index:- Ed25519:
0x00 || public_key_bytes (32 bytes) - Secp256k1:
0x01 || public_key_bytes (65 bytes) - Secp256r1:
0x02 || public_key_bytes (65 bytes)
- Ed25519:
-
For MultiKey authentication:
MultiKey { public_keys: Vec<AnyPublicKey> // ULEB128 length + each serialized AnyPublicKey signatures_required: u8 } -
For MultiEd25519 (legacy format, no length prefix):
public_key_bytes = pk0 (32) || pk1 (32) || ... || pkN (32) || threshold (1)
Must support the following key types
- Ed25519 - 32-byte public key, 64-byte signature (EdDSA over Curve25519)
- Secp256k1 - 65-byte uncompressed public key (04 || x || y), 64-byte signature (r || s)
- Secp256r1 - 65-byte uncompressed public key (04 || x || y), WebAuthn signatures
All keys must support reading and outputting in AIP-80 format.
Transactions must be able to use the following replay protectors (not at the same time)
- Sequence Number - u64 field in RawTransaction, must equal on-chain sequence number
- Orderless (hash for 60s)
Transactions also must support the following types:
- Standard (Single sender) - Uses
TransactionAuthenticator::Ed25519orTransactionAuthenticator::SingleSender - Multiagent - Uses
TransactionAuthenticator::MultiAgent(variant 2) - Multisig - Uses
TransactionPayload::Multisig(variant 3) - Fee Payer (Sponsored) - Uses
TransactionAuthenticator::FeePayer(variant 3) - Multiagent fee payer - Combines MultiAgent and FeePayer patterns
| Variant | Index | Description |
|---|---|---|
| Script | 0 | Execute a Move script |
| ModuleBundle | 1 | Deprecated - Do not use |
| EntryFunction | 2 | Call an entry function (most common) |
| Multisig | 3 | Execute via multisig account |
| Payload | 4 | Unified payload (executable + extra config); used for orderless (nonce-based) transactions |
EntryFunction {
module: ModuleId {
address: AccountAddress (32 bytes)
name: Identifier (ULEB128 length + UTF-8 bytes)
}
function: Identifier (ULEB128 length + UTF-8 bytes)
ty_args: Vec<TypeTag> (ULEB128 length + each TypeTag)
args: Vec<Vec<u8>> (ULEB128 length + each BCS-encoded argument)
}
MultiEd25519Signature {
signatures: sig0 (64) || sig1 (64) || ... || sigM (64) // No length prefix
bitmap: 4 bytes (bits indicate which keys signed, MSB first per byte)
}
Matches aptos-core MultiKeyAuthenticator.signature_bytes() = BCS((signatures, signatures_bitmap)).
aptos-core uses aptos_bitvec::BitVec with serde_bytes for the inner Vec<u8>, so the bitmap is
ULEB128(length) + bytes (for 4-byte bitmap: ULEB128(4) + 4 bytes).
MultiKeyAuthenticator {
public_keys: MultiKey { ... }
signatures: Vec<AnySignature> // BCS: ULEB128(n) + sig0 + sig1 + ...
signatures_bitmap: BitVec // BCS: ULEB128(4) + 4 bytes (serde_bytes)
}
Transactions are signed by BCS serializing the transaction payload.
Must support account addresses that are
- Parsable from string (hex format with or without 0x prefix)
- Outputs in AIP-40 format (LONG format for standard addresses; special
addresses such as
0x1use the AIP-40 SHORT form)
- Fixed 32 bytes (256 bits)
- Derived from the authentication key (which is the full 32-byte SHA3-256 hash)
- Special addresses:
0x1(framework),0x0(VM), etc. should be left-padded with zeros