Starknet Substreams Type Glossary
Block
pub struct Block {
pub block_hash: Vec<u8>,
pub parent_hash: Vec<u8>,
pub block_number: u64,
pub new_root: Vec<u8>,
pub timestamp: u64,
pub sequencer_address: Vec<u8>,
pub l1_gas_price: Option<ResourcePrice>,
pub l1_data_gas_price: Option<ResourcePrice>,
pub l1_da_mode: i32,
pub starknet_version: String,
pub transactions: Vec<TransactionWithReceipt>,
pub state_update: Option<StateUpdate>,
}
L1DaMode
pub enum L1DaMode {
Unspecified = 0, // Default mode when not specified
Calldata = 1, // Data posted as calldata on L1 (traditional method)
Blob = 2, // Data posted as blob on L1 (EIP-4844)
}
Fields
block_hash
- Description: Unique identifier of the block
- Type:
Vec<u8>
- Example: Hash bytes representing the block’s identity
parent_hash
- Description: Hash of the previous block in the chain
- Type:
Vec<u8>
- Example: Hash bytes linking to the parent block
block_number
- Description: Sequential number of this block
- Type:
u64
- Example:
1234567
new_root
- Description: New global state root after block execution
- Type:
Vec<u8>
- Example: State root hash bytes
timestamp
- Description: Unix timestamp when block was created
- Type:
u64
- Example:
1678901234
sequencer_address
- Description: Starknet address of the sequencer that produced the block
- Type:
Vec<u8>
- Example: Sequencer’s address bytes
l1_gas_price, l1_data_gas_price
- Description: Gas pricing information for L1 operations
- Type:
Option<ResourcePrice>
- Example: Current gas prices in FRI and Wei
l1_da_mode
- Description: Data availability mode (Calldata or Blob)
- Type:
i32
(enum L1DaMode) - Values:
0
: Unknown1
: Calldata2
: Blob
starknet_version
- Description: Protocol version string
- Type:
String
- Example:
"0.11.0"
transactions
- Description: List of transactions with their receipts
- Type:
Vec<TransactionWithReceipt>
state_update
- Description: Changes to the global state in this block
- Type:
Option<StateUpdate>
Important Notes
- Block structure represents the fundamental unit of the Starknet blockchain
- Contains both transaction data and state changes
- Links to Ethereum L1 through gas prices and data availability modes
- Maintains version information for protocol compatibility
- Includes complete execution results through transaction receipts
Example Use Case
fn analyze_block(block: &Block) -> BlockAnalysis {
BlockAnalysis {
timestamp: block.timestamp,
transaction_count: block.transactions.len(),
version: block.starknet_version.clone(),
da_mode: match block.l1_da_mode {
1 => "Calldata",
2 => "Blob",
_ => "Unknown"
}.to_string(),
has_state_update: block.state_update.is_some()
}
}
ResourcePrice
pub struct ResourcePrice {
pub price_in_fri: Vec<u8>,
pub price_in_wei: Vec<u8>,
}
Fields
price_in_fri
- Description: Price in Starknet’s native token (FRI - Fee Rate Index)
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 0, 0, 100]
(representing 100 FRI) - Note: Stored as big-endian bytes representing a large integer
price_in_wei
- Description: Price in Ethereum’s smallest unit (Wei)
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 0, 1, 0]
(representing 256 Wei) - Note: Stored as big-endian bytes representing a large integer
Example Use Cases
fn calculate_transaction_costs(
resource_price: &ResourcePrice,
gas_used: u64
) -> TransactionCosts {
// Convert bytes to numeric values
let fri_price = BigInt::from_bytes_be(Sign::Plus, &resource_price.price_in_fri);
let wei_price = BigInt::from_bytes_be(Sign::Plus, &resource_price.price_in_wei);
TransactionCosts {
cost_in_fri: fri_price * gas_used,
cost_in_wei: wei_price * gas_used,
}
}
// Example of price comparison
fn is_price_favorable(
current: &ResourcePrice,
threshold: &ResourcePrice
) -> bool {
let current_wei = BigInt::from_bytes_be(Sign::Plus, ¤t.price_in_wei);
let threshold_wei = BigInt::from_bytes_be(Sign::Plus, &threshold.price_in_wei);
current_wei <= threshold_wei
}
Important Notes
- Used for both L1 gas prices and L1 data gas prices in blocks
- Prices are represented as byte arrays to handle large numbers
- Dual pricing system reflects Starknet’s L2 nature:
- FRI pricing for Starknet’s internal economy
- Wei pricing for L1 interaction costs
- Common scenarios:
- Gas cost estimation
- Fee calculation
- Price monitoring
- Transaction cost optimization
Common Patterns
// Example of typical resource price structure
let typical_price = ResourcePrice {
price_in_fri: vec![0, 0, 0, 0, 0, 0, 0, 100], // 100 FRI
price_in_wei: vec![0, 0, 0, 0, 0, 0, 1, 0], // 256 Wei
};
// Example of high gas price scenario
let high_price = ResourcePrice {
price_in_fri: vec![0, 0, 0, 0, 0, 1, 0, 0], // Higher FRI amount
price_in_wei: vec![0, 0, 0, 0, 1, 0, 0, 0], // Higher Wei amount
};
TransactionWithReceipt
pub struct TransactionWithReceipt {
pub receipt: Option<TransactionReceipt>,
pub transaction: Option<transaction_with_receipt::Transaction>,
}
pub enum Transaction {
InvokeTransactionV0(InvokeTransactionV0),
InvokeTransactionV1(InvokeTransactionV1),
InvokeTransactionV3(InvokeTransactionV3),
L1HandlerTransaction(L1HandlerTransaction),
DeclareTransactionV0(DeclareTransactionV0),
DeclareTransactionV1(DeclareTransactionV1),
DeclareTransactionV2(DeclareTransactionV2),
DeclareTransactionV3(DeclareTransactionV3),
DeployTransactionV0(DeployTransactionV0),
DeployAccountTransactionV1(DeployAccountTransactionV1),
DeployAccountTransactionV3(DeployAccountTransactionV3),
}
Fields
receipt
- Description: Execution receipt containing the transaction outcome
- Type:
Option<TransactionReceipt>
- Example: Contains execution status, events, messages, etc.
transaction
- Description: The actual transaction data in one of multiple possible formats
- Type:
Option<Transaction>
(enum) - Variants:
- InvokeTransactionV0/V1/V3: Contract function calls
- L1HandlerTransaction: Processes messages from Ethereum L1
- DeclareTransactionV0/V1/V2/V3: Declares new contract classes
- DeployTransactionV0: Deploys new contract instances
- DeployAccountTransactionV1/V3: Creates new account contracts
Example Use Cases
fn process_transaction(tx_with_receipt: &TransactionWithReceipt) -> TransactionAnalysis {
let tx_type = match &tx_with_receipt.transaction {
Some(tx) => match tx {
Transaction::InvokeTransactionV0(_) => "Invoke V0",
Transaction::InvokeTransactionV1(_) => "Invoke V1",
Transaction::InvokeTransactionV3(_) => "Invoke V3",
Transaction::L1HandlerTransaction(_) => "L1 Handler",
Transaction::DeclareTransactionV0(_) => "Declare V0",
Transaction::DeclareTransactionV1(_) => "Declare V1",
Transaction::DeclareTransactionV2(_) => "Declare V2",
Transaction::DeclareTransactionV3(_) => "Declare V3",
Transaction::DeployTransactionV0(_) => "Deploy V0",
Transaction::DeployAccountTransactionV1(_) => "Deploy Account V1",
Transaction::DeployAccountTransactionV3(_) => "Deploy Account V3",
},
None => "Unknown",
};
let status = tx_with_receipt.receipt.as_ref().map(|r| r.execution_status);
TransactionAnalysis {
transaction_type: tx_type.to_string(),
success: matches!(status, Some(1)), // ExecutionStatus::Success
has_events: tx_with_receipt.receipt
.as_ref()
.map(|r| !r.events.is_empty())
.unwrap_or(false)
}
}
Important Notes
- Combines transaction data with its execution receipt
- Supports multiple transaction versions and types
- Key transaction types:
- Invoke: Function calls to existing contracts
- Declare: Adding new contract classes
- Deploy: Creating contract instances
- L1Handler: Processing L1→L2 messages
- DeployAccount: Creating account contracts
Common Patterns
// Example of an invoke transaction with receipt
let invoke_tx = TransactionWithReceipt {
transaction: Some(Transaction::InvokeTransactionV3(InvokeTransactionV3 {
// Invoke transaction details
})),
receipt: Some(TransactionReceipt {
execution_status: 1, // Success
events: vec![/* ... */],
// Other receipt fields
})
};
// Example of a declare transaction with receipt
let declare_tx = TransactionWithReceipt {
transaction: Some(Transaction::DeclareTransactionV2(DeclareTransactionV2 {
// Contract declaration details
})),
receipt: Some(TransactionReceipt {
execution_status: 1, // Success
// Other receipt fields
})
};
TransactionReceipt
pub struct TransactionReceipt {
pub transaction_hash: Vec<u8>,
pub actual_fee: Option<ActualFee>,
pub execution_status: i32,
pub revert_reason: String,
pub r#type: i32,
pub message_hash: String,
pub messages_sent: Vec<MessagesSent>,
pub events: Vec<Event>,
pub execution_resources: Option<ExecutionResources>,
pub contract_address: Vec<u8>,
}
ExecutionStatus
pub enum ExecutionStatus {
Unspecified = 0, // Default/unknown execution status
Succeeded = 1, // Transaction executed successfully
Reverted = 2, // Transaction execution was reverted
}
TransactionType
pub enum TransactionType {
Unspecified = 0, // Default/unknown transaction type
Declare = 1, // Contract class declaration
Deploy = 2, // Contract deployment
DeployAccount = 3, // Account contract deployment
Invoke = 4, // Contract function invocation
L1Handler = 5, // L1 to L2 message handler
Initialize = 6, // Contract initialization
}
Fields
transaction_hash
- Description: Unique identifier hash of the transaction
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of transaction hash */]
actual_fee
- Description: The fee charged by the sequencer for transaction execution
- Type:
Option<ActualFee>
- Example: Contains amount and unit of fee charged
execution_status
- Description: Status of transaction execution
- Type:
i32
(enum ExecutionStatus) - Values:
0
: Unknown1
: Success2
: Reverted
revert_reason
- Description: Error message if transaction was reverted
- Type:
String
- Example:
"Insufficient balance"
type
- Description: Type of transaction
- Type:
i32
(enum TransactionType) - Values:
0
: Unknown1
: Invoke2
: Declare3
: Deploy4
: DeployAccount5
: L1Handler
message_hash
- Description: Hash of messages sent to L1
- Type:
String
- Example: Hash string of L1 messages
messages_sent
- Description: List of messages sent to L1
- Type:
Vec<MessagesSent>
- Example: L2→L1 messages for cross-layer communication
events
- Description: Events emitted during transaction execution
- Type:
Vec<Event>
- Example: Contract events, state changes, etc.
execution_resources
- Description: Resources consumed during execution
- Type:
Option<ExecutionResources>
- Example: Steps, memory usage, builtin instance counts
contract_address
- Description: Address of deployed/interacted contract
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of contract address */]
Example Use Cases
fn analyze_receipt(receipt: &TransactionReceipt) -> ReceiptAnalysis {
ReceiptAnalysis {
success: receipt.execution_status == 1,
has_messages: !receipt.messages_sent.is_empty(),
event_count: receipt.events.len(),
fee_info: receipt.actual_fee.as_ref().map(|fee| {
format!("{} {}", fee.amount, fee.unit)
}),
error: if receipt.execution_status == 2 {
Some(receipt.revert_reason.clone())
} else {
None
}
}
}
// Example of processing L2->L1 messages
fn process_l1_messages(receipt: &TransactionReceipt) -> Vec<L1Message> {
receipt.messages_sent.iter().map(|msg| {
L1Message {
from: msg.from_address.clone(),
to: msg.to_address.clone(),
payload: msg.payload.clone()
}
}).collect()
}
Important Notes
- Key component for transaction result analysis
- Contains all execution outcomes:
- Success/failure status
- Resource usage
- Cross-layer messages
- Emitted events
- Fee information
Common Patterns
// Successful transaction receipt
let success_receipt = TransactionReceipt {
transaction_hash: vec![/* hash */],
execution_status: 1, // Success
actual_fee: Some(ActualFee {
amount: vec![/* amount */],
unit: "WEI".to_string()
}),
events: vec![/* events */],
messages_sent: vec![],
revert_reason: "".to_string(),
// ... other fields
};
// Failed transaction receipt
let failed_receipt = TransactionReceipt {
transaction_hash: vec![/* hash */],
execution_status: 2, // Reverted
revert_reason: "Insufficient funds".to_string(),
actual_fee: Some(ActualFee {
amount: vec![/* amount */],
unit: "WEI".to_string()
}),
// ... other fields
};
MessagesSent
pub struct MessagesSent {
pub from_address: Vec<u8>,
pub to_address: Vec<u8>,
pub payload: Vec<Vec<u8>>,
}
Fields
from_address
- Description: The address of the L2 contract sending the message
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of Starknet contract address */]
to_address
- Description: The target L1 address the message is sent to
- Type:
Vec<u8>
- Example:
vec![/* 20 bytes of Ethereum address */]
payload
- Description: The payload data of the message
- Type:
Vec<Vec<u8>>
- Example: Vector of byte arrays containing function selector and parameters
Example Use Cases
fn process_l2_to_l1_message(message: &MessagesSent) -> MessageAnalysis {
// Convert addresses to hex strings
let from_hex = format!("0x{}", hex::encode(&message.from_address));
let to_hex = format!("0x{}", hex::encode(&message.to_address));
// Analyze payload
let payload_analysis = if !message.payload.is_empty() {
analyze_message_payload(&message.payload)
} else {
PayloadType::Empty
};
MessageAnalysis {
l2_sender: from_hex,
l1_recipient: to_hex,
payload_type: payload_analysis,
payload_size: message.payload.len()
}
}
// Example of common message patterns
fn analyze_message_payload(payload: &[Vec<u8>]) -> PayloadType {
if let Some(first_word) = payload.first() {
match first_word.as_slice() {
// Common function selectors
[0x12, 0x34, 0x56, 0x78, ..] => PayloadType::TokenTransfer,
[0xa9, 0x05, 0x9c, 0xbb, ..] => PayloadType::TokenApproval,
_ => PayloadType::Unknown
}
} else {
PayloadType::Empty
}
}
Common Message Patterns
// Token bridge message example
let token_bridge_message = MessagesSent {
from_address: vec![/* L2 bridge contract address */],
to_address: vec![/* L1 bridge contract address */],
payload: vec![
vec![0x12, 0x34, 0x56, 0x78], // transfer function selector
vec![/* token amount */],
vec![/* recipient address */]
]
};
// State verification message
let verification_message = MessagesSent {
from_address: vec![/* L2 verifier contract */],
to_address: vec![/* L1 verifier contract */],
payload: vec![
vec![/* state root */],
vec![/* block number */],
vec![/* timestamp */]
]
};
Important Notes
-
Key component for L2→L1 communication
-
Common use cases:
- Token bridging
- State verification
- Cross-layer governance
- Data availability proofs
-
Security considerations:
- Messages must be verified on L1
- Payload format must match L1 expectations
- Address formats differ between L1 and L2
Event
pub struct Event {
pub from_address: Vec<u8>,
pub keys: Vec<Vec<u8>>,
pub data: Vec<Vec<u8>>,
}
Fields
from_address
- Description: The address of the contract that emitted the event
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of contract address */]
keys
- Description: Indexed event parameters used for efficient filtering
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* event selector */], vec![/* indexed param 1 */], vec![/* indexed param 2 */] ]
data
- Description: Non-indexed event parameters containing additional data
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* amount */], vec![/* timestamp */], vec![/* other data */] ]
Example Use Cases
fn process_event(event: &Event) -> EventAnalysis {
// Convert event selector (first key) to identify event type
let event_type = if let Some(selector) = event.keys.first() {
match selector.as_slice() {
// Common event selectors
[0x12, 0x34, 0x56, 0x78, ..] => "Transfer",
[0xa9, 0x05, 0x9c, 0xbb, ..] => "Approval",
[0xdc, 0x13, 0x45, 0x67, ..] => "Swap",
_ => "Unknown"
}
} else {
"Unknown"
};
EventAnalysis {
emitter: format!("0x{}", hex::encode(&event.from_address)),
event_type: event_type.to_string(),
indexed_params: event.keys.len() - 1, // Subtract selector
data_params: event.data.len()
}
}
// Example of processing transfer events
fn process_transfer_event(event: &Event) -> Option<TransferDetails> {
if event.keys.len() < 3 || event.data.is_empty() {
return None;
}
Some(TransferDetails {
from: event.keys[1].clone(), // First indexed param
to: event.keys[2].clone(), // Second indexed param
amount: event.data[0].clone() // First data param
})
}
Common Event Patterns
// Transfer event example
let transfer_event = Event {
from_address: vec![/* token contract address */],
keys: vec![
vec![0x12, 0x34, 0x56, 0x78], // Transfer event selector
vec![/* from address */], // Indexed
vec![/* to address */] // Indexed
],
data: vec![
vec![/* amount */] // Non-indexed
]
};
// Swap event example
let swap_event = Event {
from_address: vec![/* DEX contract address */],
keys: vec![
vec![0xdc, 0x13, 0x45, 0x67], // Swap event selector
vec![/* token_in */], // Indexed
vec![/* token_out */] // Indexed
],
data: vec![
vec![/* amount_in */], // Non-indexed
vec![/* amount_out */], // Non-indexed
vec![/* recipient */] // Non-indexed
]
};
Important Notes
-
Events are crucial for:
- Off-chain indexing
- State tracking
- UI updates
- Analytics
-
Key characteristics:
- Indexed parameters (keys) for efficient filtering
- Non-indexed parameters (data) for additional information
- Emitted by specific contracts
- Immutable once emitted
ExecutionResources
pub struct ExecutionResources {
pub steps: u64,
pub memory_holes: u64,
pub range_check_builtin_applications: u64,
pub pedersen_builtin_applications: u64,
pub poseidon_builtin_applications: u64,
pub ec_op_builtin_applications: u64,
pub ecdsa_builtin_applications: u64,
pub bitwise_builtin_applications: u64,
pub keccak_builtin_applications: u64,
pub segment_arena_builtin: u64,
}
Fields
steps
- Description: Number of Cairo VM steps executed
- Type:
u64
- Example:
1500
(represents computational complexity)
memory_holes
- Description: Number of unused memory cells
- Type:
u64
- Example:
32
(memory optimization metric)
range_check_builtin_applications
- Description: Number of range check operations
- Type:
u64
- Example:
50
(number of range validations)
pedersen_builtin_applications
- Description: Number of Pedersen hash computations
- Type:
u64
- Example:
10
(hash operations count)
poseidon_builtin_applications
- Description: Number of Poseidon hash operations
- Type:
u64
- Example:
5
(alternative hash operations)
ec_op_builtin_applications
- Description: Number of elliptic curve operations
- Type:
u64
- Example:
2
(cryptographic operations)
ecdsa_builtin_applications
- Description: Number of ECDSA signature verifications
- Type:
u64
- Example:
1
(signature checks)
bitwise_builtin_applications
- Description: Number of bitwise operations
- Type:
u64
- Example:
20
(bit manipulation operations)
keccak_builtin_applications
- Description: Number of Keccak hash computations
- Type:
u64
- Example:
3
(Ethereum-compatible hashing)
segment_arena_builtin
- Description: Memory segment allocations
- Type:
u64
- Example:
15
(memory management operations)
Example Use Cases
fn analyze_execution_cost(resources: &ExecutionResources) -> ResourceAnalysis {
ResourceAnalysis {
total_steps: resources.steps,
crypto_operations: resources.pedersen_builtin_applications
+ resources.poseidon_builtin_applications
+ resources.ec_op_builtin_applications
+ resources.ecdsa_builtin_applications,
hash_operations: resources.pedersen_builtin_applications
+ resources.poseidon_builtin_applications
+ resources.keccak_builtin_applications,
memory_efficiency: calculate_memory_efficiency(
resources.steps,
resources.memory_holes
)
}
}
// Example of resource usage estimation
fn estimate_gas_cost(resources: &ExecutionResources) -> u64 {
let base_cost = resources.steps * STEP_COST;
let builtin_cost =
resources.pedersen_builtin_applications * PEDERSEN_COST +
resources.ecdsa_builtin_applications * ECDSA_COST +
resources.bitwise_builtin_applications * BITWISE_COST;
base_cost + builtin_cost
}
Common Resource Patterns
// Simple transfer transaction resources
let transfer_resources = ExecutionResources {
steps: 1000,
memory_holes: 10,
range_check_builtin_applications: 20,
pedersen_builtin_applications: 2,
poseidon_builtin_applications: 0,
ec_op_builtin_applications: 0,
ecdsa_builtin_applications: 1,
bitwise_builtin_applications: 0,
keccak_builtin_applications: 0,
segment_arena_builtin: 5,
};
// Complex DeFi transaction resources
let defi_resources = ExecutionResources {
steps: 5000,
memory_holes: 50,
range_check_builtin_applications: 100,
pedersen_builtin_applications: 10,
poseidon_builtin_applications: 5,
ec_op_builtin_applications: 2,
ecdsa_builtin_applications: 1,
bitwise_builtin_applications: 30,
keccak_builtin_applications: 3,
segment_arena_builtin: 25,
};
Important Notes
-
Used for:
- Gas cost calculation
- Performance optimization
- Resource usage tracking
- Transaction fee estimation
-
Key aspects:
- Different builtin costs
- Memory efficiency metrics
- Computational complexity
- Cryptographic operation tracking
InvokeTransactionV0
pub struct InvokeTransactionV0 {
pub max_fee: Vec<u8>,
pub signature: Vec<Vec<u8>>,
pub contract_address: Vec<u8>,
pub entry_point_selector: Vec<u8>,
pub calldata: Vec<Vec<u8>>,
}
Fields
max_fee
- Description: Maximum fee the sender is willing to pay for the transaction
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 1, 0, 0]
(represents fee in wei)
signature
- Description: List of signature elements proving transaction authorization
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* r value */], vec![/* s value */] ]
contract_address
- Description: Address of the contract to invoke
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of contract address */]
entry_point_selector
- Description: The selector of the function to call
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of function selector */]
calldata
- Description: Arguments passed to the function
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* arg1 */], vec![/* arg2 */] ]
Example Use Cases
fn process_invoke_v0(tx: &InvokeTransactionV0) -> InvokeAnalysis {
// Convert function selector to identify the function
let function_name = match tx.entry_point_selector.as_slice() {
[0x12, 0x34, 0x56, 0x78, ..] => "transfer",
[0xa9, 0x05, 0x9c, 0xbb, ..] => "approve",
_ => "unknown"
};
InvokeAnalysis {
contract: format!("0x{}", hex::encode(&tx.contract_address)),
function: function_name.to_string(),
argument_count: tx.calldata.len(),
max_fee: format!("0x{}", hex::encode(&tx.max_fee)),
signed: !tx.signature.is_empty()
}
}
Common Transaction Patterns
// ERC20 transfer transaction
let transfer_tx = InvokeTransactionV0 {
max_fee: vec![0, 0, 0, 0, 0, 1, 0, 0], // 256 wei
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
contract_address: vec![/* ERC20 contract address */],
entry_point_selector: vec![0x12, 0x34, 0x56, 0x78], // transfer
calldata: vec![
vec![/* recipient address */],
vec![/* amount */]
]
};
// Contract interaction transaction
let interaction_tx = InvokeTransactionV0 {
max_fee: vec![0, 0, 0, 0, 0, 2, 0, 0], // 512 wei
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
contract_address: vec![/* contract address */],
entry_point_selector: vec![/* custom function selector */],
calldata: vec![
vec![/* parameter 1 */],
vec![/* parameter 2 */],
vec![/* parameter 3 */]
]
};
Important Notes
- Legacy transaction format (V0)
- Key differences from later versions:
- Single contract call only
- Direct function selector specification
- Simpler fee model
- Basic signature scheme
InvokeTransactionV1
pub struct InvokeTransactionV1 {
pub max_fee: Vec<u8>,
pub signature: Vec<Vec<u8>>,
pub nonce: Vec<u8>,
pub sender_address: Vec<u8>,
pub calldata: Vec<Vec<u8>>,
}
Fields
max_fee
- Description: Maximum fee the sender is willing to pay for the transaction
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 1, 0, 0]
(represents fee in wei)
signature
- Description: List of signature elements for transaction authorization
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* r value */], vec![/* s value */] ]
nonce
- Description: Account nonce to prevent transaction replay
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 1]
(sequential transaction number)
sender_address
- Description: Address of the account initiating the transaction
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of account address */]
calldata
- Description: Encoded function calls and parameters
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* contract address */], vec![/* function selector */], vec![/* parameters... */] ]
Example Use Cases
fn process_invoke_v1(tx: &InvokeTransactionV1) -> InvokeV1Analysis {
// Parse calldata to extract calls
let calls = parse_calldata(&tx.calldata);
InvokeV1Analysis {
sender: format!("0x{}", hex::encode(&tx.sender_address)),
nonce: u64::from_be_bytes(tx.nonce.try_into().unwrap_or([0; 8])),
call_count: calls.len(),
max_fee: format!("0x{}", hex::encode(&tx.max_fee)),
signed: !tx.signature.is_empty()
}
}
fn parse_calldata(calldata: &[Vec<u8>]) -> Vec<Call> {
let mut calls = Vec::new();
let mut i = 0;
while i < calldata.len() {
if i + 2 < calldata.len() {
calls.push(Call {
contract: calldata[i].clone(),
selector: calldata[i + 1].clone(),
params: if i + 2 < calldata.len() {
calldata[i + 2].clone()
} else {
vec![]
}
});
}
i += 3;
}
calls
}
Common Transaction Patterns
// Single contract call
let single_call_tx = InvokeTransactionV1 {
max_fee: vec![0, 0, 0, 0, 0, 1, 0, 0],
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
nonce: vec![0, 0, 0, 1],
sender_address: vec![/* account address */],
calldata: vec![
vec![/* contract address */],
vec![/* function selector */],
vec![/* parameters */]
]
};
// Multi-call transaction
let multi_call_tx = InvokeTransactionV1 {
max_fee: vec![0, 0, 0, 0, 0, 2, 0, 0],
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
nonce: vec![0, 0, 0, 2],
sender_address: vec![/* account address */],
calldata: vec![
// First call
vec![/* contract address 1 */],
vec![/* function selector 1 */],
vec![/* parameters 1 */],
// Second call
vec![/* contract address 2 */],
vec![/* function selector 2 */],
vec![/* parameters 2 */]
]
};
Important Notes
-
Improvements over V0:
- Added nonce for replay protection
- Explicit sender address
- Support for multiple contract calls
- More flexible calldata format
-
Key features:
- Transaction sequencing
- Multi-call support
- Account abstraction ready
- Improved security
InvokeTransactionV3
pub struct InvokeTransactionV3 {
pub sender_address: Vec<u8>,
pub calldata: Vec<Vec<u8>>,
pub signature: Vec<Vec<u8>>,
pub nonce: Vec<u8>,
pub resource_bounds: Option<ResourceBounds>,
pub tip: Vec<u8>,
pub paymaster_data: Vec<Vec<u8>>,
pub account_deployment_data: Vec<Vec<u8>>,
pub nonce_data_availability_mode: i32,
pub fee_data_availability_mode: i32,
}
FeeDataAvailabilityMode (nonce_data_availability_mode & fee_data_availability_mode)
pub enum FeeDataAvailabilityMode {
Unspecified = 0, // Default mode when not specified
L1 = 1, // Fee data stored on L1
L2 = 2, // Fee data stored on L2
}
Fields
sender_address
- Description: Address of the account initiating the transaction
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of account address */]
calldata
- Description: Encoded function calls and parameters
- Type:
Vec<Vec<u8>>
- Example: Multiple contract calls encoded sequentially
signature
- Description: Authorization signature(s) for the transaction
- Type:
Vec<Vec<u8>>
- Example: Multiple signature components
nonce
- Description: Account nonce for replay protection
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 1]
resource_bounds
- Description: Maximum resources allowed for transaction
- Type:
Option<ResourceBounds>
- Example: Limits on L1 gas, L2 gas, etc.
tip
- Description: Additional fee paid to sequencer
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 100]
(100 wei tip)
paymaster_data
- Description: Data for fee payment delegation
- Type:
Vec<Vec<u8>>
- Example: Paymaster contract info and authorization
account_deployment_data
- Description: Data for deploying account contract
- Type:
Vec<Vec<u8>>
- Example: Account class hash and constructor params
nonce_data_availability_mode
- Description: DA mode for nonce storage
- Type:
i32
- Values:
0
(Unknown),1
(L1),2
(L2)
fee_data_availability_mode
- Description: DA mode for fee storage
- Type:
i32
- Values:
0
(Unknown),1
(L1),2
(L2)
Example Use Cases
fn process_invoke_v3(tx: &InvokeTransactionV3) -> InvokeV3Analysis {
InvokeV3Analysis {
sender: format!("0x{}", hex::encode(&tx.sender_address)),
is_account_deployment: !tx.account_deployment_data.is_empty(),
has_paymaster: !tx.paymaster_data.is_empty(),
resource_bounds: tx.resource_bounds.as_ref().map(analyze_bounds),
da_mode: analyze_da_modes(
tx.nonce_data_availability_mode,
tx.fee_data_availability_mode
),
tip_amount: u64::from_be_bytes(tx.tip.try_into().unwrap_or([0; 8]))
}
}
// Example of paymaster integration
fn create_paymaster_tx(
base_tx: InvokeTransactionV3,
paymaster: &PaymasterInfo
) -> InvokeTransactionV3 {
InvokeTransactionV3 {
paymaster_data: vec![
paymaster.address.clone(),
paymaster.signature.clone(),
paymaster.extra_data.clone()
],
..base_tx
}
}
Common Transaction Patterns
// Account deployment with transaction
let deployment_tx = InvokeTransactionV3 {
sender_address: vec![/* new account address */],
calldata: vec![/* initial transaction calldata */],
signature: vec![/* signature components */],
nonce: vec![0, 0, 0, 0], // Initial nonce
resource_bounds: Some(ResourceBounds {
max_amount: vec![/* resource limits */],
max_price_per_unit: vec![/* price limits */]
}),
account_deployment_data: vec![
vec![/* account class hash */],
vec![/* constructor params */]
],
..Default::default()
};
// Paymaster-sponsored transaction
let sponsored_tx = InvokeTransactionV3 {
sender_address: vec![/* user account */],
calldata: vec![/* transaction calldata */],
signature: vec![/* user signature */],
nonce: vec![0, 0, 0, 1],
resource_bounds: Some(ResourceBounds { /* ... */ }),
paymaster_data: vec![
vec![/* paymaster contract */],
vec![/* paymaster signature */]
],
fee_data_availability_mode: 1, // L1
..Default::default()
};
Important Notes
- Latest transaction version with advanced features:
- Resource bounds for better fee estimation
- Paymaster support for fee abstraction
- Account deployment capabilities
- Flexible data availability modes
- Sequencer tips
L1HandlerTransaction
pub struct L1HandlerTransaction {
pub contract_address: Vec<u8>,
pub entry_point_selector: Vec<u8>,
pub calldata: Vec<Vec<u8>>,
pub nonce: Vec<u8>,
pub version: i32,
}
Fields
contract_address
- Description: Address of the L2 contract handling the L1 message
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of contract address */]
entry_point_selector
- Description: Selector of the L1 handler function to be called
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of function selector */]
calldata
- Description: Parameters from the L1 message
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* from_address */], vec![/* payload... */] ]
nonce
- Description: Unique identifier for the L1 message
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 1]
version
- Description: Version of the L1 handler transaction format
- Type:
i32
- Example:
1
Example Use Cases
fn process_l1_handler(tx: &L1HandlerTransaction) -> L1HandlerAnalysis {
// Identify common L1 handler types
let handler_type = match tx.entry_point_selector.as_slice() {
[0x12, 0x34, 0x56, 0x78, ..] => "TokenBridge",
[0xa9, 0x05, 0x9c, 0xbb, ..] => "StateUpdate",
[0xdc, 0x13, 0x45, 0x67, ..] => "MessageHandler",
_ => "Unknown"
};
L1HandlerAnalysis {
contract: format!("0x{}", hex::encode(&tx.contract_address)),
handler_type: handler_type.to_string(),
nonce: u64::from_be_bytes(tx.nonce.try_into().unwrap_or([0; 8])),
version: tx.version,
param_count: tx.calldata.len()
}
}
// Example of parsing L1 message data
fn parse_l1_message(tx: &L1HandlerTransaction) -> Option<L1Message> {
if tx.calldata.len() < 2 {
return None;
}
Some(L1Message {
from_address: tx.calldata[0].clone(),
payload: tx.calldata[1..].to_vec()
})
}
Common Transaction Patterns
// Token bridge message handler
let bridge_handler = L1HandlerTransaction {
contract_address: vec![/* bridge contract address */],
entry_point_selector: vec![0x12, 0x34, 0x56, 0x78], // handle_bridge
calldata: vec![
vec![/* L1 bridge address */],
vec![/* token address */],
vec![/* recipient */],
vec![/* amount */]
],
nonce: vec![0, 0, 0, 1],
version: 1
};
// State update handler
let state_handler = L1HandlerTransaction {
contract_address: vec![/* state contract address */],
entry_point_selector: vec![0xa9, 0x05, 0x9c, 0xbb], // handle_state
calldata: vec![
vec![/* L1 contract */],
vec![/* state root */],
vec![/* block number */]
],
nonce: vec![0, 0, 0, 2],
version: 1
};
Important Notes
-
Key component for L1→L2 communication
-
Common use cases:
- Token bridging
- State synchronization
- Cross-layer messaging
- Governance actions
-
Security considerations:
- Message origin verification
- Nonce uniqueness
- Handler authorization
- State consistency
DeclareTransactionV0
pub struct DeclareTransactionV0 {
pub max_fee: Vec<u8>,
pub signature: Vec<Vec<u8>>,
pub class_hash: Vec<u8>,
pub sender_address: Vec<u8>,
}
Fields
max_fee
- Description: Maximum fee the sender is willing to pay for declaration
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 1, 0, 0]
(represents fee in wei)
signature
- Description: Transaction authorization signature(s)
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* r signature component */], vec![/* s signature component */] ]
class_hash
- Description: Hash of the contract class being declared
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of class hash */]
sender_address
- Description: Address of the account declaring the contract
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of account address */]
Example Use Cases
fn process_declare_v0(tx: &DeclareTransactionV0) -> DeclareAnalysis {
DeclareAnalysis {
declarer: format!("0x{}", hex::encode(&tx.sender_address)),
class: format!("0x{}", hex::encode(&tx.class_hash)),
max_fee: u64::from_be_bytes(tx.max_fee.try_into().unwrap_or([0; 8])),
signed: !tx.signature.is_empty()
}
}
// Example of validating a declare transaction
fn validate_declare_v0(tx: &DeclareTransactionV0) -> Result<(), String> {
// Check signature presence
if tx.signature.is_empty() {
return Err("Missing signature".to_string());
}
// Validate class hash length
if tx.class_hash.len() != 32 {
return Err("Invalid class hash length".to_string());
}
// Validate sender address
if tx.sender_address.len() != 32 {
return Err("Invalid sender address length".to_string());
}
Ok(())
}
Common Transaction Patterns
// Basic contract declaration
let basic_declare = DeclareTransactionV0 {
max_fee: vec![0, 0, 0, 0, 0, 1, 0, 0], // 256 wei
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
class_hash: vec![/* contract class hash */],
sender_address: vec![/* account address */]
};
// System contract declaration
let system_declare = DeclareTransactionV0 {
max_fee: vec![0, 0, 0, 0, 0, 2, 0, 0], // 512 wei
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
class_hash: vec![/* system contract class hash */],
sender_address: vec![/* privileged account address */]
};
Important Notes
- Legacy declaration format (V0)
- Used to register contract classes on Starknet
- Key aspects:
- Simple fee model
- Basic signature scheme
- No class code included
- Class must be valid Cairo
DeclareTransactionV1
pub struct DeclareTransactionV1 {
pub max_fee: Vec<u8>,
pub signature: Vec<Vec<u8>>,
pub nonce: Vec<u8>,
pub class_hash: Vec<u8>,
pub sender_address: Vec<u8>,
}
Fields
max_fee
- Description: Maximum fee the sender is willing to pay for declaration
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 1, 0, 0]
(represents fee in wei)
signature
- Description: Transaction authorization signature(s)
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* r signature component */], vec![/* s signature component */] ]
nonce
- Description: Account nonce for replay protection
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 1]
class_hash
- Description: Hash of the contract class being declared
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of class hash */]
sender_address
- Description: Address of the account declaring the contract
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of account address */]
Example Use Cases
fn process_declare_v1(tx: &DeclareTransactionV1) -> DeclareV1Analysis {
DeclareV1Analysis {
declarer: format!("0x{}", hex::encode(&tx.sender_address)),
class: format!("0x{}", hex::encode(&tx.class_hash)),
nonce: u64::from_be_bytes(tx.nonce.try_into().unwrap_or([0; 8])),
max_fee: u64::from_be_bytes(tx.max_fee.try_into().unwrap_or([0; 8])),
signed: !tx.signature.is_empty()
}
}
// Example of transaction sequencing validation
fn validate_declare_sequence(
tx: &DeclareTransactionV1,
current_nonce: &[u8]
) -> Result<(), String> {
if tx.nonce <= current_nonce {
return Err("Invalid nonce sequence".to_string());
}
// Additional validation logic
Ok(())
}
Common Transaction Patterns
// Standard contract declaration
let standard_declare = DeclareTransactionV1 {
max_fee: vec![0, 0, 0, 0, 0, 1, 0, 0], // 256 wei
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
nonce: vec![0, 0, 0, 1],
class_hash: vec![/* contract class hash */],
sender_address: vec![/* account address */]
};
// Upgraded contract version declaration
let upgrade_declare = DeclareTransactionV1 {
max_fee: vec![0, 0, 0, 0, 0, 2, 0, 0], // 512 wei
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
nonce: vec![0, 0, 0, 2],
class_hash: vec![/* upgraded class hash */],
sender_address: vec![/* account address */]
};
Important Notes
-
Improvements over V0:
- Added nonce for replay protection
- Better transaction sequencing
- Same class declaration capabilities
- Enhanced security model
-
Key features:
- Transaction ordering
- Replay prevention
- Account abstraction support
- Legacy fee model compatibility
DeclareTransactionV2
pub struct DeclareTransactionV2 {
pub max_fee: Vec<u8>,
pub signature: Vec<Vec<u8>>,
pub nonce: Vec<u8>,
pub class_hash: Vec<u8>,
pub sender_address: Vec<u8>,
pub compiled_class_hash: Vec<u8>,
}
Fields
max_fee
- Description: Maximum fee the sender is willing to pay for declaration
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 1, 0, 0]
(represents fee in wei)
signature
- Description: Transaction authorization signature(s)
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* r signature component */], vec![/* s signature component */] ]
nonce
- Description: Account nonce for replay protection
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 1]
class_hash
- Description: Hash of the Sierra contract class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of Sierra class hash */]
sender_address
- Description: Address of the account declaring the contract
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of account address */]
compiled_class_hash
- Description: Hash of the compiled CASM class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of CASM class hash */]
Example Use Cases
fn process_declare_v2(tx: &DeclareTransactionV2) -> DeclareV2Analysis {
DeclareV2Analysis {
declarer: format!("0x{}", hex::encode(&tx.sender_address)),
sierra_class: format!("0x{}", hex::encode(&tx.class_hash)),
casm_class: format!("0x{}", hex::encode(&tx.compiled_class_hash)),
nonce: u64::from_be_bytes(tx.nonce.try_into().unwrap_or([0; 8])),
max_fee: u64::from_be_bytes(tx.max_fee.try_into().unwrap_or([0; 8])),
signed: !tx.signature.is_empty()
}
}
// Example of class hash validation
fn validate_class_hashes(tx: &DeclareTransactionV2) -> Result<(), String> {
// Validate Sierra class hash
if tx.class_hash.len() != 32 {
return Err("Invalid Sierra class hash length".to_string());
}
// Validate CASM class hash
if tx.compiled_class_hash.len() != 32 {
return Err("Invalid CASM class hash length".to_string());
}
// Additional validation logic
Ok(())
}
Common Transaction Patterns
// Sierra contract declaration
let sierra_declare = DeclareTransactionV2 {
max_fee: vec![0, 0, 0, 0, 0, 1, 0, 0],
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
nonce: vec![0, 0, 0, 1],
class_hash: vec![/* Sierra class hash */],
compiled_class_hash: vec![/* CASM class hash */],
sender_address: vec![/* account address */]
};
// Upgraded Sierra contract declaration
let upgrade_declare = DeclareTransactionV2 {
max_fee: vec![0, 0, 0, 0, 0, 2, 0, 0],
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
nonce: vec![0, 0, 0, 2],
class_hash: vec![/* new Sierra class hash */],
compiled_class_hash: vec![/* new CASM class hash */],
sender_address: vec![/* account address */]
};
Important Notes
-
First version supporting Sierra contracts
-
Key improvements:
- Sierra class support
- Compiled class verification
- Better type safety
- Enhanced compilation validation
-
Important features:
- Dual hash verification
- Sierra/CASM compatibility
- Backward compatibility
- Enhanced security checks
DeclareTransactionV3
pub struct DeclareTransactionV3 {
pub sender_address: Vec<u8>,
pub signature: Vec<Vec<u8>>,
pub nonce: Vec<u8>,
pub class_hash: Vec<u8>,
pub compiled_class_hash: Vec<u8>,
pub resource_bounds: Option<ResourceBounds>,
pub tip: Vec<u8>,
pub paymaster_data: Vec<Vec<u8>>,
pub account_deployment_data: Vec<Vec<u8>>,
pub nonce_data_availability_mode: i32,
pub fee_data_availability_mode: i32,
}
FeeDataAvailabilityMode (nonce_data_availability_mode & fee_data_availability_mode)
pub enum FeeDataAvailabilityMode {
Unspecified = 0, // Default mode when not specified
L1 = 1, // Fee data stored on L1
L2 = 2, // Fee data stored on L2
}
Fields
sender_address
- Description: Address of the account declaring the contract
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of account address */]
signature
- Description: Transaction authorization signature(s)
- Type:
Vec<Vec<u8>>
- Example: Multiple signature components
nonce
- Description: Account nonce for replay protection
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 1]
class_hash
- Description: Hash of the Sierra contract class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of Sierra class hash */]
compiled_class_hash
- Description: Hash of the compiled CASM class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of CASM class hash */]
resource_bounds
- Description: Maximum resources allowed for declaration
- Type:
Option<ResourceBounds>
- Example: Limits on L1 gas, L2 gas
tip
- Description: Additional fee for sequencer
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 100]
(100 wei tip)
paymaster_data
- Description: Data for fee payment delegation
- Type:
Vec<Vec<u8>>
- Example: Paymaster contract info and authorization
account_deployment_data
- Description: Data for deploying account contract
- Type:
Vec<Vec<u8>>
- Example: Account class hash and constructor params
nonce_data_availability_mode, fee_data_availability_mode
- Description: DA modes for nonce and fee storage
- Type:
i32
- Values:
0
(Unknown),1
(L1),2
(L2)
Example Use Cases
fn process_declare_v3(tx: &DeclareTransactionV3) -> DeclareV3Analysis {
DeclareV3Analysis {
declarer: format!("0x{}", hex::encode(&tx.sender_address)),
sierra_class: format!("0x{}", hex::encode(&tx.class_hash)),
casm_class: format!("0x{}", hex::encode(&tx.compiled_class_hash)),
has_paymaster: !tx.paymaster_data.is_empty(),
is_account_deployment: !tx.account_deployment_data.is_empty(),
resource_bounds: tx.resource_bounds.as_ref().map(analyze_bounds),
tip_amount: u64::from_be_bytes(tx.tip.try_into().unwrap_or([0; 8]))
}
}
// Example of paymaster-sponsored declaration
fn create_sponsored_declare(
base_tx: DeclareTransactionV3,
paymaster: &PaymasterInfo
) -> DeclareTransactionV3 {
DeclareTransactionV3 {
paymaster_data: vec![
paymaster.address.clone(),
paymaster.signature.clone()
],
fee_data_availability_mode: 1, // L1
..base_tx
}
}
Common Transaction Patterns
// Standard Sierra declaration
let standard_declare = DeclareTransactionV3 {
sender_address: vec![/* account address */],
signature: vec![/* signature components */],
nonce: vec![0, 0, 0, 1],
class_hash: vec![/* Sierra class hash */],
compiled_class_hash: vec![/* CASM class hash */],
resource_bounds: Some(ResourceBounds {
max_amount: vec![/* resource limits */],
max_price_per_unit: vec![/* price limits */]
}),
tip: vec![0, 0, 0, 0],
paymaster_data: vec![],
account_deployment_data: vec![],
nonce_data_availability_mode: 1,
fee_data_availability_mode: 1,
};
// Sponsored declaration with account deployment
let sponsored_declare = DeclareTransactionV3 {
sender_address: vec![/* new account address */],
signature: vec![/* signature components */],
nonce: vec![0, 0, 0, 0],
class_hash: vec![/* Sierra class hash */],
compiled_class_hash: vec![/* CASM class hash */],
resource_bounds: Some(ResourceBounds { /* ... */ }),
paymaster_data: vec![/* paymaster info */],
account_deployment_data: vec![
vec![/* account class hash */],
vec![/* constructor params */]
],
..Default::default()
};
Important Notes
- Latest declaration version with advanced features:
- Resource bounds
- Fee delegation via paymaster
- Account deployment support
- Flexible DA modes
- Sequencer tips
DeployTransactionV0
pub struct DeployTransactionV0 {
pub version: i32,
pub contract_address: Vec<u8>,
pub contract_address_salt: Vec<u8>,
pub class_hash: Vec<u8>,
pub constructor_calldata: Vec<Vec<u8>>,
}
Fields
version
- Description: Version identifier for the deploy transaction
- Type:
i32
- Example:
0
(V0 deployment)
contract_address
- Description: Computed address where contract will be deployed
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of contract address */]
contract_address_salt
- Description: Salt used in contract address computation
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of random salt */]
class_hash
- Description: Hash of the contract class to deploy
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of class hash */]
constructor_calldata
- Description: Arguments for contract constructor
- Type:
Vec<Vec<u8>>
- Example: Constructor parameters as byte arrays
Example Use Cases
fn process_deploy_v0(tx: &DeployTransactionV0) -> DeployAnalysis {
DeployAnalysis {
address: format!("0x{}", hex::encode(&tx.contract_address)),
class: format!("0x{}", hex::encode(&tx.class_hash)),
salt: format!("0x{}", hex::encode(&tx.contract_address_salt)),
constructor_args: tx.constructor_calldata.len(),
version: tx.version
}
}
// Example of address computation validation
fn validate_contract_address(
tx: &DeployTransactionV0,
computed_address: &[u8]
) -> Result<(), String> {
if tx.contract_address != computed_address {
return Err("Invalid contract address computation".to_string());
}
// Additional validation logic
Ok(())
}
Common Transaction Patterns
// Basic contract deployment
let basic_deploy = DeployTransactionV0 {
version: 0,
contract_address: vec![/* computed contract address */],
contract_address_salt: vec![/* random salt */],
class_hash: vec![/* contract class hash */],
constructor_calldata: vec![
vec![/* initial parameter */]
]
};
// Token contract deployment
let token_deploy = DeployTransactionV0 {
version: 0,
contract_address: vec![/* computed address */],
contract_address_salt: vec![/* unique salt */],
class_hash: vec![/* ERC20 class hash */],
constructor_calldata: vec![
vec![/* name */],
vec![/* symbol */],
vec![/* decimals */],
vec![/* initial_supply */],
vec![/* owner */]
]
};
Important Notes
-
Legacy deployment format (V0)
-
Key characteristics:
- Deterministic addressing
- No explicit fee handling
- Simple constructor interface
- Basic deployment logic
-
Address computation:
- Based on salt
- Class hash included
- Constructor parameters affect address
- Must match computed value
DeployAccountTransactionV1
pub struct DeployAccountTransactionV1 {
pub max_fee: Vec<u8>,
pub signature: Vec<Vec<u8>>,
pub nonce: Vec<u8>,
pub contract_address_salt: Vec<u8>,
pub constructor_calldata: Vec<Vec<u8>>,
pub class_hash: Vec<u8>,
}
Fields
max_fee
- Description: Maximum fee willing to pay for account deployment
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 1, 0, 0]
(represents fee in wei)
signature
- Description: Signature(s) authorizing the deployment
- Type:
Vec<Vec<u8>>
- Example:
vec![ vec![/* r signature component */], vec![/* s signature component */] ]
nonce
- Description: Initial nonce for the account
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0]
(typically starts at 0)
contract_address_salt
- Description: Salt for deterministic address computation
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of random salt */]
constructor_calldata
- Description: Arguments for account constructor
- Type:
Vec<Vec<u8>>
- Example: Account initialization parameters
class_hash
- Description: Hash of the account contract class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of account class hash */]
Example Use Cases
fn process_deploy_account_v1(
tx: &DeployAccountTransactionV1
) -> DeployAccountAnalysis {
DeployAccountAnalysis {
class: format!("0x{}", hex::encode(&tx.class_hash)),
salt: format!("0x{}", hex::encode(&tx.contract_address_salt)),
max_fee: u64::from_be_bytes(tx.max_fee.try_into().unwrap_or([0; 8])),
constructor_args: tx.constructor_calldata.len(),
initial_nonce: u64::from_be_bytes(tx.nonce.try_into().unwrap_or([0; 8]))
}
}
// Example of computing account address
fn compute_account_address(tx: &DeployAccountTransactionV1) -> Vec<u8> {
let mut hasher = StarknetKeccak::new();
hasher.update(&[1]); // prefix for account deployment
hasher.update(&tx.contract_address_salt);
hasher.update(&tx.class_hash);
hasher.update(&serialize_calldata(&tx.constructor_calldata));
hasher.finalize().to_vec()
}
Common Transaction Patterns
// Basic account deployment
let basic_account = DeployAccountTransactionV1 {
max_fee: vec![0, 0, 0, 0, 0, 1, 0, 0],
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
nonce: vec![0, 0, 0, 0],
contract_address_salt: vec![/* random salt */],
constructor_calldata: vec![
vec![/* public_key */]
],
class_hash: vec![/* account contract class hash */]
};
// Multisig account deployment
let multisig_account = DeployAccountTransactionV1 {
max_fee: vec![0, 0, 0, 0, 0, 2, 0, 0],
signature: vec![
vec![/* r signature component */],
vec![/* s signature component */]
],
nonce: vec![0, 0, 0, 0],
contract_address_salt: vec![/* random salt */],
constructor_calldata: vec![
vec![/* threshold */],
vec![/* owner_1 */],
vec![/* owner_2 */],
vec![/* owner_3 */]
],
class_hash: vec![/* multisig class hash */]
};
Important Notes
-
First version with explicit account deployment
-
Key features:
- Fee specification
- Signature validation
- Nonce initialization
- Deterministic addressing
- Constructor parameters
-
Common use cases:
- EOA account deployment
- Multisig account creation
- Smart contract wallet deployment
- Custom account implementation
DeployAccountTransactionV3
pub struct DeployAccountTransactionV3 {
pub signature: Vec<Vec<u8>>,
pub nonce: Vec<u8>,
pub contract_address_salt: Vec<u8>,
pub constructor_calldata: Vec<Vec<u8>>,
pub class_hash: Vec<u8>,
pub resource_bounds: Option<ResourceBounds>,
pub tip: Vec<u8>,
pub paymaster_data: Vec<Vec<u8>>,
pub nonce_data_availability_mode: i32,
pub fee_data_availability_mode: i32,
}
FeeDataAvailabilityMode (nonce_data_availability_mode & fee_data_availability_mode)
pub enum FeeDataAvailabilityMode {
Unspecified = 0, // Default mode when not specified
L1 = 1, // Fee data stored on L1
L2 = 2, // Fee data stored on L2
}
Fields
signature
- Description: Signature(s) authorizing the deployment
- Type:
Vec<Vec<u8>>
- Example: Multiple signature components
nonce
- Description: Initial nonce for the account
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0]
contract_address_salt
- Description: Salt for deterministic address computation
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of random salt */]
constructor_calldata
- Description: Arguments for account constructor
- Type:
Vec<Vec<u8>>
- Example: Account initialization parameters
class_hash
- Description: Hash of the account contract class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of account class hash */]
resource_bounds
- Description: Maximum resources allowed for deployment
- Type:
Option<ResourceBounds>
- Example: Limits on L1 gas, L2 gas
tip
- Description: Additional fee for sequencer
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 100]
(100 wei tip)
paymaster_data
- Description: Data for fee payment delegation
- Type:
Vec<Vec<u8>>
- Example: Paymaster contract info and authorization
nonce_data_availability_mode, fee_data_availability_mode
- Description: DA modes for nonce and fee storage
- Type:
i32
- Values:
0
(Unknown),1
(L1),2
(L2)
Example Use Cases
fn process_deploy_account_v3(
tx: &DeployAccountTransactionV3
) -> DeployAccountV3Analysis {
DeployAccountV3Analysis {
class: format!("0x{}", hex::encode(&tx.class_hash)),
salt: format!("0x{}", hex::encode(&tx.contract_address_salt)),
has_paymaster: !tx.paymaster_data.is_empty(),
resource_bounds: tx.resource_bounds.as_ref().map(analyze_bounds),
tip_amount: u64::from_be_bytes(tx.tip.try_into().unwrap_or([0; 8])),
constructor_args: tx.constructor_calldata.len(),
da_mode: format!("nonce:{}, fee:{}",
tx.nonce_data_availability_mode,
tx.fee_data_availability_mode
)
}
}
// Example of sponsored account deployment
fn create_sponsored_deployment(
base_tx: DeployAccountTransactionV3,
paymaster: &PaymasterInfo
) -> DeployAccountTransactionV3 {
DeployAccountTransactionV3 {
paymaster_data: vec![
paymaster.address.clone(),
paymaster.signature.clone()
],
fee_data_availability_mode: 1, // L1
..base_tx
}
}
Common Transaction Patterns
// Standard account deployment
let standard_deploy = DeployAccountTransactionV3 {
signature: vec![/* signature components */],
nonce: vec![0, 0, 0, 0],
contract_address_salt: vec![/* random salt */],
constructor_calldata: vec![
vec![/* public_key */]
],
class_hash: vec![/* account class hash */],
resource_bounds: Some(ResourceBounds {
max_amount: vec![/* resource limits */],
max_price_per_unit: vec![/* price limits */]
}),
tip: vec![0, 0, 0, 0],
paymaster_data: vec![],
nonce_data_availability_mode: 1,
fee_data_availability_mode: 1,
};
// Sponsored multisig deployment
let sponsored_multisig = DeployAccountTransactionV3 {
signature: vec![/* signature components */],
nonce: vec![0, 0, 0, 0],
contract_address_salt: vec![/* random salt */],
constructor_calldata: vec![
vec![/* threshold */],
vec![/* owners... */]
],
class_hash: vec![/* multisig class hash */],
resource_bounds: Some(ResourceBounds { /* ... */ }),
paymaster_data: vec![/* paymaster info */],
fee_data_availability_mode: 1,
..Default::default()
};
Important Notes
-
Latest account deployment version with advanced features:
- Resource bounds
- Fee delegation
- Flexible DA modes
- Sequencer tips
- Enhanced fee model
-
Key improvements:
- Better resource management
- Sponsored deployments
- Customizable DA strategy
- Advanced fee mechanics
ResourceBounds
pub struct ResourceBounds {
pub max_amount: Vec<u8>,
pub max_price_per_unit: Vec<u8>,
}
Fields
max_amount
- Description: Maximum amount of resource units allowed
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 1, 0, 0]
(represents maximum gas units)
max_price_per_unit
- Description: Maximum price willing to pay per resource unit
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 0, 0, 0, 1, 0]
(represents price per gas unit)
Example Use Cases
fn calculate_max_cost(bounds: &ResourceBounds) -> Result<u128, String> {
let max_amount = u128::from_be_bytes(
bounds.max_amount.try_into()
.map_err(|_| "Invalid max_amount")?
);
let max_price = u128::from_be_bytes(
bounds.max_price_per_unit.try_into()
.map_err(|_| "Invalid max_price")?
);
Ok(max_amount.checked_mul(max_price)
.ok_or("Overflow in cost calculation")?)
}
// Example of validating resource bounds
fn validate_resource_bounds(
bounds: &ResourceBounds,
current_price: &[u8]
) -> Result<(), String> {
if bounds.max_price_per_unit < current_price {
return Err("Price too high".to_string());
}
// Additional validation logic
Ok(())
}
Common Resource Patterns
// Conservative resource bounds
let conservative_bounds = ResourceBounds {
max_amount: vec![0, 0, 0, 0, 0, 1, 0, 0], // 256 units
max_price_per_unit: vec![0, 0, 0, 0, 0, 0, 1, 0] // 256 wei/unit
};
// Liberal resource bounds for complex operations
let liberal_bounds = ResourceBounds {
max_amount: vec![0, 0, 0, 0, 0, 10, 0, 0], // 2560 units
max_price_per_unit: vec![0, 0, 0, 0, 0, 0, 2, 0] // 512 wei/unit
};
// Resource bounds for different scenarios
let bounds_by_type = vec![
// For simple transfers
ResourceBounds {
max_amount: vec![0, 0, 0, 0, 0, 0, 50, 0],
max_price_per_unit: vec![0, 0, 0, 0, 0, 0, 1, 0]
},
// For contract deployments
ResourceBounds {
max_amount: vec![0, 0, 0, 0, 0, 5, 0, 0],
max_price_per_unit: vec![0, 0, 0, 0, 0, 0, 2, 0]
},
// For complex DeFi operations
ResourceBounds {
max_amount: vec![0, 0, 0, 0, 0, 20, 0, 0],
max_price_per_unit: vec![0, 0, 0, 0, 0, 0, 3, 0]
}
];
Important Notes
-
Used in V3 transactions for:
- Fee estimation
- Resource limitation
- Cost control
- Price protection
-
Key aspects:
- Separate amount and price bounds
- Multiplication for total cost
- Price protection mechanism
- Resource usage control
Common Use Cases
Transaction Fee Control
let fee_bounds = ResourceBounds {
max_amount: vec![0, 0, 0, 0, 0, 1, 0, 0], // Max gas units
max_price_per_unit: vec![0, 0, 0, 0, 0, 0, 1, 0] // Max gas price
};
Resource Usage Limits
let compute_bounds = ResourceBounds {
max_amount: vec![0, 0, 0, 0, 0, 5, 0, 0], // Max computation steps
max_price_per_unit: vec![0, 0, 0, 0, 0, 0, 0, 100] // Price per step
};
Resource
pub struct Resource {
pub max_amount: String,
pub max_price_per_unit: String,
}
Fields
max_amount
- Description: Maximum amount of the resource that can be used in the transaction
- Type:
String
- Example:
"1000000"
(represents maximum resource units) - Note: Stored as a string to handle large numbers precisely
max_price_per_unit
- Description: Maximum price per unit of resource for this transaction
- Type:
String
- Example:
"100000000"
(represents price per unit in wei) - Note: Stored as a string to handle large numbers precisely
Example Use Cases
fn calculate_max_cost(resource: &Resource) -> Result<String, String> {
// Using string-based big number arithmetic for precision
let max_amount = BigInt::from_str(&resource.max_amount)
.map_err(|_| "Invalid max_amount")?;
let max_price = BigInt::from_str(&resource.max_price_per_unit)
.map_err(|_| "Invalid max_price")?;
Ok((max_amount * max_price).to_string())
}
// Example of resource validation
fn validate_resource(
resource: &Resource,
current_price: &str
) -> Result<(), String> {
let max_price = BigInt::from_str(&resource.max_price_per_unit)
.map_err(|_| "Invalid max_price")?;
let current = BigInt::from_str(current_price)
.map_err(|_| "Invalid current_price")?;
if max_price < current {
return Err("Price exceeds maximum".to_string());
}
Ok(())
}
Common Resource Patterns
// Standard resource limits
let standard_resource = Resource {
max_amount: "1000000".to_string(), // 1M units
max_price_per_unit: "1000000".to_string() // 1M wei per unit
};
// High computation resource
let computation_resource = Resource {
max_amount: "5000000".to_string(), // 5M units
max_price_per_unit: "2000000".to_string() // 2M wei per unit
};
// Resource configurations for different operations
let resources = vec![
// For simple operations
Resource {
max_amount: "500000".to_string(),
max_price_per_unit: "1000000".to_string()
},
// For complex operations
Resource {
max_amount: "2000000".to_string(),
max_price_per_unit: "3000000".to_string()
}
];
Important Notes
-
Uses string representation for:
- Arbitrary precision arithmetic
- Large number handling
- Exact value preservation
- Gas cost calculations
-
Key considerations:
- String-based numeric operations
- Precision handling
- Overflow prevention
- Cost calculations
Receipt
pub struct Receipt {
pub actual_fee: String,
pub execution_resources: Option<ExecutionResources>,
pub l1_to_l2_consumed_message: Option<L1ToL2Message>,
pub l2_to_l1_messages: Vec<L2ToL1Message>,
pub events: Vec<Event>,
pub execution_status: i32,
pub revert_error: String,
}
Fields
actual_fee
- Description: The actual fee charged for the transaction execution
- Type:
String
- Example:
"1234567890"
(in wei)
execution_resources
- Description: Detailed breakdown of resources consumed during execution
- Type:
Option<ExecutionResources>
- Example: Contains steps, memory usage, and builtin applications
l1_to_l2_consumed_message
- Description: L1 message that was consumed by this transaction
- Type:
Option<L1ToL2Message>
- Example: Message from Ethereum that triggered this transaction
l2_to_l1_messages
- Description: Messages sent from L2 to L1 during execution
- Type:
Vec<L2ToL1Message>
- Example: State updates, token bridge messages
events
- Description: Events emitted during transaction execution
- Type:
Vec<Event>
- Example: Transfer events, state change notifications
execution_status
- Description: Status code of the transaction execution
- Type:
i32
- Values:
0
: UNSPECIFIED1
: SUCCEEDED2
: REVERTED
revert_error
- Description: Error message if transaction was reverted
- Type:
String
- Example:
"Insufficient balance"
Example Use Cases
fn analyze_receipt(receipt: &Receipt) -> ReceiptAnalysis {
ReceiptAnalysis {
success: receipt.execution_status == 1,
fee_paid: receipt.actual_fee.clone(),
message_count: receipt.l2_to_l1_messages.len(),
event_count: receipt.events.len(),
has_l1_message: receipt.l1_to_l2_consumed_message.is_some(),
resources: receipt.execution_resources.as_ref().map(analyze_resources),
error: if receipt.execution_status == 2 {
Some(receipt.revert_error.clone())
} else {
None
}
}
}
// Example of processing receipt events
fn process_receipt_events(receipt: &Receipt) -> Vec<EventAnalysis> {
receipt.events.iter().map(|event| {
EventAnalysis {
emitter: format!("0x{}", hex::encode(&event.from_address)),
keys: event.keys.len(),
data: event.data.len()
}
}).collect()
}
Common Receipt Patterns
// Successful transaction receipt
let success_receipt = Receipt {
actual_fee: "1000000".to_string(),
execution_resources: Some(ExecutionResources {
steps: 1000,
memory_holes: 10,
range_check_builtin_applications: 20,
pedersen_builtin_applications: 2,
// ... other resource fields
}),
l1_to_l2_consumed_message: None,
l2_to_l1_messages: vec![],
events: vec![
Event {
from_address: vec![/* contract address */],
keys: vec![/* event keys */],
data: vec![/* event data */]
}
],
execution_status: 1, // SUCCEEDED
revert_error: "".to_string()
};
// Failed transaction receipt
let failed_receipt = Receipt {
actual_fee: "500000".to_string(),
execution_resources: Some(ExecutionResources {
// ... resource usage until failure
}),
l1_to_l2_consumed_message: None,
l2_to_l1_messages: vec![],
events: vec![],
execution_status: 2, // REVERTED
revert_error: "Insufficient balance".to_string()
};
Important Notes
-
Key transaction outcome record
-
Contains:
- Execution result
- Resource usage
- Cross-layer messages
- Emitted events
- Fee information
-
Used for:
- Transaction verification
- State tracking
- Fee calculation
- Event monitoring
- Error handling
ActualFee
pub struct ActualFee {
pub amount: String,
pub unit: String,
}
Fields
amount
- Description: The actual amount of fee charged
- Type:
String
- Example:
"1000000"
(represents exact fee amount) - Note: Stored as string to handle large numbers precisely
unit
- Description: The currency unit of the fee
- Type:
String
- Example:
"WEI"
or"FRI"
- Note: Indicates the denomination of the fee
Example Use Cases
fn process_actual_fee(fee: &ActualFee) -> FeeAnalysis {
FeeAnalysis {
amount: BigInt::from_str(&fee.amount)
.unwrap_or_default(),
is_wei: fee.unit.to_uppercase() == "WEI",
is_fri: fee.unit.to_uppercase() == "FRI",
formatted: format!("{} {}", fee.amount, fee.unit)
}
}
// Example of fee conversion
fn convert_fee_to_eth(fee: &ActualFee) -> Result<f64, String> {
if fee.unit.to_uppercase() != "WEI" {
return Err("Fee not in WEI".to_string());
}
let wei_amount = BigInt::from_str(&fee.amount)
.map_err(|_| "Invalid amount")?;
// Convert wei to ETH (1 ETH = 10^18 wei)
let eth_value = (wei_amount / BigInt::from(10u64.pow(18)))
.to_f64()
.ok_or("Conversion error")?;
Ok(eth_value)
}
Common Fee Patterns
// Standard transaction fee
let standard_fee = ActualFee {
amount: "1000000000000000".to_string(), // 0.001 ETH in wei
unit: "WEI".to_string()
};
// FRI-denominated fee
let fri_fee = ActualFee {
amount: "5000000".to_string(),
unit: "FRI".to_string()
};
// Fee examples for different transaction types
let fee_examples = vec![
// Simple transfer fee
ActualFee {
amount: "21000000000000".to_string(),
unit: "WEI".to_string()
},
// Contract deployment fee
ActualFee {
amount: "500000000000000".to_string(),
unit: "WEI".to_string()
},
// Complex DeFi operation fee
ActualFee {
amount: "1500000000000000".to_string(),
unit: "WEI".to_string()
}
];
Important Notes
-
Used for precise fee representation
-
Key characteristics:
- String-based amount storage
- Explicit unit specification
- Multi-currency support
- Exact value preservation
-
Common units:
- WEI: Ethereum’s smallest unit
- FRI: Starknet’s fee token
- Other potential future units
Fee Calculation Examples
// Fee aggregation
fn aggregate_fees(fees: &[ActualFee]) -> Result<ActualFee, String> {
let mut total = BigInt::from(0);
let mut unit = String::new();
for fee in fees {
if unit.is_empty() {
unit = fee.unit.clone();
} else if unit != fee.unit {
return Err("Mixed units not supported".to_string());
}
total += BigInt::from_str(&fee.amount)
.map_err(|_| "Invalid amount")?;
}
Ok(ActualFee {
amount: total.to_string(),
unit
})
}
// Fee comparison
fn compare_fees(fee1: &ActualFee, fee2: &ActualFee) -> Result<Ordering, String> {
if fee1.unit != fee2.unit {
return Err("Cannot compare different units".to_string());
}
let amount1 = BigInt::from_str(&fee1.amount)
.map_err(|_| "Invalid amount in fee1")?;
let amount2 = BigInt::from_str(&fee2.amount)
.map_err(|_| "Invalid amount in fee2")?;
Ok(amount1.cmp(&amount2))
}
DataAvailability
pub struct DataAvailability {
pub l1_gas_price: ResourcePrice,
pub l1_gas_usage: ResourceBounds,
pub l1_data_gas_price: ResourcePrice,
pub l1_data_gas_usage: ResourceBounds,
}
Fields
l1_gas_price
- Description: Price information for L1 gas
- Type:
ResourcePrice
- Example: Price in both wei and fri units
l1_gas_usage
- Description: Bounds for L1 gas consumption
- Type:
ResourceBounds
- Example: Maximum amount and price per unit for L1 gas
l1_data_gas_price
- Description: Price information for L1 data gas
- Type:
ResourcePrice
- Example: Price for data posting on L1
l1_data_gas_usage
- Description: Bounds for L1 data gas consumption
- Type:
ResourceBounds
- Example: Maximum amount and price per unit for data posting
Example Use Cases
fn calculate_da_costs(da: &DataAvailability) -> DAAnalysis {
// Calculate L1 gas costs
let l1_gas_cost = calculate_resource_cost(
&da.l1_gas_price,
&da.l1_gas_usage
);
// Calculate L1 data gas costs
let l1_data_cost = calculate_resource_cost(
&da.l1_data_gas_price,
&da.l1_data_gas_usage
);
DAAnalysis {
total_l1_gas: l1_gas_cost,
total_data_gas: l1_data_cost,
total_cost: l1_gas_cost + l1_data_cost
}
}
fn calculate_resource_cost(
price: &ResourcePrice,
bounds: &ResourceBounds
) -> u128 {
let amount = u128::from_be_bytes(
bounds.max_amount.try_into().unwrap_or([0; 16])
);
let price_per_unit = u128::from_be_bytes(
price.price_in_wei.try_into().unwrap_or([0; 16])
);
amount * price_per_unit
}
Common Usage Patterns
// Standard DA configuration
let standard_da = DataAvailability {
l1_gas_price: ResourcePrice {
price_in_fri: vec![0, 0, 0, 100], // 100 FRI
price_in_wei: vec![0, 0, 0, 1000] // 1000 WEI
},
l1_gas_usage: ResourceBounds {
max_amount: vec![0, 0, 0, 50000], // 50K gas units
max_price_per_unit: vec![0, 0, 0, 2000] // 2000 WEI/unit
},
l1_data_gas_price: ResourcePrice {
price_in_fri: vec![0, 0, 0, 200], // 200 FRI
price_in_wei: vec![0, 0, 0, 2000] // 2000 WEI
},
l1_data_gas_usage: ResourceBounds {
max_amount: vec![0, 0, 0, 10000], // 10K data gas units
max_price_per_unit: vec![0, 0, 0, 3000] // 3000 WEI/unit
}
};
// High data usage configuration
let high_data_da = DataAvailability {
// ... l1_gas_price and l1_gas_usage ...
l1_data_gas_usage: ResourceBounds {
max_amount: vec![0, 0, 0, 100000], // 100K data gas units
max_price_per_unit: vec![0, 0, 0, 4000] // 4000 WEI/unit
},
// ... other fields ...
};
Important Notes
-
Key aspects:
- Separate tracking of gas and data gas
- Dual pricing in FRI and WEI
- Resource bounds for both types
- L1 cost management
-
Used for:
- DA cost estimation
- Resource allocation
- Fee calculation
- Transaction planning
StateUpdate
pub struct StateUpdate {
pub block_hash: Vec<u8>,
pub new_root: Vec<u8>,
pub old_root: Vec<u8>,
pub state_diff: Option<StateDiff>,
}
Fields
block_hash
- Description: Hash of the block this state update belongs to
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of block hash */]
new_root
- Description: New state root after applying the update
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of new state root */]
old_root
- Description: Previous state root before the update
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of old state root */]
state_diff
- Description: Detailed changes made to the state
- Type:
Option<StateDiff>
- Example: Contains storage updates, deployed contracts, etc.
Example Use Cases
fn process_state_update(update: &StateUpdate) -> StateUpdateAnalysis {
StateUpdateAnalysis {
block: format!("0x{}", hex::encode(&update.block_hash)),
old_state: format!("0x{}", hex::encode(&update.old_root)),
new_state: format!("0x{}", hex::encode(&update.new_root)),
has_changes: update.state_diff.is_some(),
diff_summary: update.state_diff.as_ref().map(summarize_diff)
}
}
fn validate_state_transition(update: &StateUpdate) -> Result<(), String> {
// Verify root lengths
if update.new_root.len() != 32 || update.old_root.len() != 32 {
return Err("Invalid root length".to_string());
}
// Verify state hasn't changed if no diff present
if update.state_diff.is_none() && update.new_root != update.old_root {
return Err("Root changed without state diff".to_string());
}
Ok(())
}
Common State Update Patterns
// Simple state update with storage changes
let storage_update = StateUpdate {
block_hash: vec![/* block hash */],
new_root: vec![/* new state root */],
old_root: vec![/* old state root */],
state_diff: Some(StateDiff {
storage_diffs: vec![
StorageDiff {
address: vec![/* contract address */],
storage_entries: vec![
StorageEntry {
key: vec![/* storage key */],
value: vec![/* new value */]
}
]
}
],
deployed_contracts: vec![],
declared_classes: vec![],
deprecated_declared_classes: vec![]
})
};
// Contract deployment state update
let deployment_update = StateUpdate {
block_hash: vec![/* block hash */],
new_root: vec![/* new state root */],
old_root: vec![/* old state root */],
state_diff: Some(StateDiff {
storage_diffs: vec![],
deployed_contracts: vec![
DeployedContract {
address: vec![/* contract address */],
class_hash: vec![/* class hash */]
}
],
declared_classes: vec![
DeclaredClass {
class_hash: vec![/* class hash */],
compiled_class_hash: vec![/* compiled hash */]
}
],
deprecated_declared_classes: vec![]
})
};
Important Notes
-
Key state transition record
-
Used for:
- State verification
- State synchronization
- History tracking
- Root chain updates
-
Verification aspects:
- Root consistency
- State transition validity
- Diff completeness
- Block correlation
State Diff Analysis Examples
fn analyze_state_changes(update: &StateUpdate) -> StateChanges {
let diff = match &update.state_diff {
Some(diff) => diff,
None => return StateChanges::default()
};
StateChanges {
storage_changes: diff.storage_diffs.len(),
new_contracts: diff.deployed_contracts.len(),
new_classes: diff.declared_classes.len(),
deprecated_classes: diff.deprecated_declared_classes.len(),
root_changed: update.new_root != update.old_root
}
}
fn verify_root_consistency(updates: &[StateUpdate]) -> bool {
for window in updates.windows(2) {
if window[0].new_root != window[1].old_root {
return false;
}
}
true
}
StateDiff
pub struct StateDiff {
pub storage_diffs: Vec<StorageDiff>,
pub deployed_contracts: Vec<DeployedContract>,
pub declared_classes: Vec<DeclaredClass>,
pub deprecated_declared_classes: Vec<DeprecatedDeclaredClass>,
}
Fields
storage_diffs
- Description: List of storage changes across contracts
- Type:
Vec<StorageDiff>
- Example: Changes to contract storage values
deployed_contracts
- Description: New contracts deployed in this state update
- Type:
Vec<DeployedContract>
- Example: Newly deployed contract addresses and their class hashes
declared_classes
- Description: New contract classes declared
- Type:
Vec<DeclaredClass>
- Example: Newly declared class hashes and their compiled versions
deprecated_declared_classes
- Description: Contract classes marked as deprecated
- Type:
Vec<DeprecatedDeclaredClass>
- Example: Class hashes that are no longer supported
Example Use Cases
fn analyze_state_diff(diff: &StateDiff) -> StateDiffAnalysis {
StateDiffAnalysis {
storage_changes: summarize_storage_changes(&diff.storage_diffs),
new_contracts: summarize_deployments(&diff.deployed_contracts),
new_classes: summarize_declarations(&diff.declared_classes),
deprecated_classes: diff.deprecated_declared_classes.len(),
total_changes: calculate_total_changes(diff)
}
}
fn calculate_total_changes(diff: &StateDiff) -> usize {
let storage_entries: usize = diff.storage_diffs
.iter()
.map(|sd| sd.storage_entries.len())
.sum();
storage_entries
+ diff.deployed_contracts.len()
+ diff.declared_classes.len()
+ diff.deprecated_declared_classes.len()
}
Common Diff Patterns
// Storage update diff
let storage_diff = StateDiff {
storage_diffs: vec![
StorageDiff {
address: vec![/* contract address */],
storage_entries: vec![
StorageEntry {
key: vec![/* storage key */],
value: vec![/* new value */]
}
]
}
],
deployed_contracts: vec![],
declared_classes: vec![],
deprecated_declared_classes: vec![]
};
// Contract deployment with storage initialization
let deployment_diff = StateDiff {
storage_diffs: vec![
StorageDiff {
address: vec![/* new contract address */],
storage_entries: vec![
// Initial storage values
StorageEntry {
key: vec![/* owner key */],
value: vec![/* owner address */]
},
StorageEntry {
key: vec![/* balance key */],
value: vec![/* initial balance */]
}
]
}
],
deployed_contracts: vec![
DeployedContract {
address: vec![/* contract address */],
class_hash: vec![/* class hash */]
}
],
declared_classes: vec![
DeclaredClass {
class_hash: vec![/* class hash */],
compiled_class_hash: vec![/* compiled hash */]
}
],
deprecated_declared_classes: vec![]
};
Important Notes
- Comprehensive state change record
- Used for:
- State synchronization
- Change tracking
- Contract deployment monitoring
- Storage updates
- Class management
Diff Analysis Examples
// Group changes by contract
fn group_changes_by_contract(diff: &StateDiff) -> HashMap<Vec<u8>, ContractChanges> {
let mut changes = HashMap::new();
// Add storage changes
for storage_diff in &diff.storage_diffs {
changes.entry(storage_diff.address.clone())
.or_insert_with(ContractChanges::default)
.storage_changes += storage_diff.storage_entries.len();
}
// Add deployments
for deployment in &diff.deployed_contracts {
changes.entry(deployment.address.clone())
.or_insert_with(ContractChanges::default)
.is_new_deployment = true;
}
changes
}
// Track class changes
fn track_class_changes(diff: &StateDiff) -> ClassChangesSummary {
ClassChangesSummary {
new_classes: diff.declared_classes
.iter()
.map(|dc| format!("0x{}", hex::encode(&dc.class_hash)))
.collect(),
deprecated_classes: diff.deprecated_declared_classes
.iter()
.map(|dc| format!("0x{}", hex::encode(&dc.class_hash)))
.collect()
}
}
NonceDiff
pub struct NonceDiff {
pub contract_address: Vec<u8>,
pub nonce: Vec<u8>,
}
Fields
contract_address
- Description: Address of the contract whose nonce is being updated
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of contract address */]
nonce
- Description: New nonce value for the contract
- Type:
Vec<u8>
- Example:
vec![0, 0, 0, 1]
(incremented nonce value)
Example Use Cases
fn process_nonce_diff(diff: &NonceDiff) -> NonceDiffAnalysis {
NonceDiffAnalysis {
contract: format!("0x{}", hex::encode(&diff.contract_address)),
new_nonce: u64::from_be_bytes(
diff.nonce.clone().try_into().unwrap_or([0; 8])
),
is_first_transaction: diff.nonce == vec![0, 0, 0, 1]
}
}
// Example of nonce validation
fn validate_nonce_sequence(
current: &[u8],
diff: &NonceDiff
) -> Result<(), String> {
let current_nonce = u64::from_be_bytes(
current.try_into().map_err(|_| "Invalid current nonce")?
);
let new_nonce = u64::from_be_bytes(
diff.nonce.clone().try_into().map_err(|_| "Invalid new nonce")?
);
if new_nonce <= current_nonce {
return Err("Invalid nonce sequence".to_string());
}
Ok(())
}
Common Nonce Patterns
// First transaction nonce update
let initial_nonce = NonceDiff {
contract_address: vec![/* contract address */],
nonce: vec![0, 0, 0, 1] // First nonce
};
// Sequential nonce update
let sequential_nonce = NonceDiff {
contract_address: vec![/* contract address */],
nonce: vec![0, 0, 0, 2] // Next nonce in sequence
};
// Batch of nonce updates
let nonce_updates = vec![
// Account 1 nonce update
NonceDiff {
contract_address: vec![/* account 1 address */],
nonce: vec![0, 0, 0, 5] // New nonce value
},
// Account 2 nonce update
NonceDiff {
contract_address: vec![/* account 2 address */],
nonce: vec![0, 0, 0, 3] // New nonce value
}
];
Important Notes
-
Used for:
- Transaction ordering
- Replay protection
- Account state tracking
- Sequence validation
-
Key aspects:
- Sequential incrementing
- Contract-specific tracking
- State synchronization
- Transaction validation
Nonce Management Examples
// Track nonce changes by contract
fn track_contract_nonces(
diffs: &[NonceDiff]
) -> HashMap<Vec<u8>, Vec<u8>> {
let mut nonce_map = HashMap::new();
for diff in diffs {
nonce_map.insert(
diff.contract_address.clone(),
diff.nonce.clone()
);
}
nonce_map
}
// Validate nonce transitions
fn validate_nonce_transitions(
diffs: &[NonceDiff]
) -> Result<(), String> {
let mut last_nonces = HashMap::new();
for diff in diffs {
let last_nonce = last_nonces
.get(&diff.contract_address)
.unwrap_or(&vec![0, 0, 0, 0]);
validate_nonce_sequence(last_nonce, diff)?;
last_nonces.insert(
diff.contract_address.clone(),
diff.nonce.clone()
);
}
Ok(())
}
ReplacedClass
pub struct ReplacedClass {
pub contract_address: Vec<u8>,
pub class_hash: Vec<u8>,
}
Fields
contract_address
- Description: Address of the contract whose class is being replaced
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of contract address */]
class_hash
- Description: Hash of the new contract class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of new class hash */]
Example Use Cases
fn process_class_replacement(
replacement: &ReplacedClass
) -> ClassReplacementAnalysis {
ClassReplacementAnalysis {
contract: format!("0x{}", hex::encode(&replacement.contract_address)),
new_class: format!("0x{}", hex::encode(&replacement.class_hash)),
timestamp: chrono::Utc::now()
}
}
// Example of replacement validation
fn validate_class_replacement(
replacement: &ReplacedClass,
old_class_hash: &[u8]
) -> Result<(), String> {
// Ensure addresses are valid
if replacement.contract_address.len() != 32 {
return Err("Invalid contract address length".to_string());
}
// Ensure class hash is valid
if replacement.class_hash.len() != 32 {
return Err("Invalid class hash length".to_string());
}
// Ensure new class is different
if replacement.class_hash == old_class_hash {
return Err("New class same as old class".to_string());
}
Ok(())
}
Common Replacement Patterns
// Standard class replacement
let standard_replacement = ReplacedClass {
contract_address: vec![/* contract address */],
class_hash: vec![/* new class hash */]
};
// Batch of class replacements
let upgrade_batch = vec![
// Upgrade contract 1
ReplacedClass {
contract_address: vec![/* contract 1 address */],
class_hash: vec![/* new class 1 hash */]
},
// Upgrade contract 2
ReplacedClass {
contract_address: vec![/* contract 2 address */],
class_hash: vec![/* new class 2 hash */]
}
];
// System contract upgrade
let system_upgrade = ReplacedClass {
contract_address: vec![/* system contract address */],
class_hash: vec![/* new system class hash */]
};
Important Notes
-
Used for:
- Contract upgrades
- Implementation changes
- Bug fixes
- Feature additions
-
Key aspects:
- Contract immutability
- Class versioning
- Upgrade tracking
- State preservation
Replacement Analysis Examples
// Track class replacements over time
struct ClassHistory {
contract: Vec<u8>,
replacements: Vec<ClassReplacement>,
}
struct ClassReplacement {
class_hash: Vec<u8>,
timestamp: DateTime<Utc>,
}
fn track_class_history(
replacements: &[ReplacedClass]
) -> HashMap<Vec<u8>, Vec<Vec<u8>>> {
let mut history = HashMap::new();
for replacement in replacements {
history
.entry(replacement.contract_address.clone())
.or_insert_with(Vec::new)
.push(replacement.class_hash.clone());
}
history
}
// Analyze upgrade patterns
fn analyze_upgrade_patterns(
replacements: &[ReplacedClass]
) -> UpgradeAnalysis {
let mut analysis = UpgradeAnalysis::default();
let mut contract_upgrades = HashMap::new();
for replacement in replacements {
*contract_upgrades
.entry(replacement.contract_address.clone())
.or_insert(0) += 1;
}
analysis.total_upgrades = replacements.len();
analysis.contracts_upgraded = contract_upgrades.len();
analysis.max_upgrades = contract_upgrades.values().max().copied().unwrap_or(0);
analysis
}
DeployedContract
pub struct DeployedContract {
pub address: Vec<u8>,
pub class_hash: Vec<u8>,
}
Fields
address
- Description: Address where the contract was deployed
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of contract address */]
class_hash
- Description: Hash of the contract’s class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of class hash */]
Example Use Cases
fn process_deployment(
deployment: &DeployedContract
) -> DeploymentAnalysis {
DeploymentAnalysis {
contract: format!("0x{}", hex::encode(&deployment.address)),
class: format!("0x{}", hex::encode(&deployment.class_hash)),
timestamp: chrono::Utc::now()
}
}
// Example of deployment validation
fn validate_deployment(
deployment: &DeployedContract,
existing_contracts: &HashMap<Vec<u8>, bool>
) -> Result<(), String> {
// Check address length
if deployment.address.len() != 32 {
return Err("Invalid address length".to_string());
}
// Check class hash length
if deployment.class_hash.len() != 32 {
return Err("Invalid class hash length".to_string());
}
// Check for address collision
if existing_contracts.contains_key(&deployment.address) {
return Err("Address already in use".to_string());
}
Ok(())
}
Common Deployment Patterns
// Standard contract deployment
let standard_deployment = DeployedContract {
address: vec![/* contract address */],
class_hash: vec![/* class hash */]
};
// Batch deployments
let deployment_batch = vec![
// Deploy token contract
DeployedContract {
address: vec![/* token address */],
class_hash: vec![/* token class hash */]
},
// Deploy vault contract
DeployedContract {
address: vec![/* vault address */],
class_hash: vec![/* vault class hash */]
}
];
// Factory deployment pattern
struct ContractFactory {
deployments: Vec<DeployedContract>,
class_hash: Vec<u8>,
}
impl ContractFactory {
fn deploy_new_instance(&mut self, salt: &[u8]) -> DeployedContract {
let address = compute_address(salt, &self.class_hash);
let deployment = DeployedContract {
address: address.clone(),
class_hash: self.class_hash.clone()
};
self.deployments.push(deployment.clone());
deployment
}
}
Important Notes
-
Used for:
- Contract creation tracking
- Address registration
- Class association
- Deployment verification
-
Key aspects:
- Unique addresses
- Class verification
- State initialization
- Deployment tracking
Deployment Analysis Examples
// Track deployments by class
fn analyze_deployments_by_class(
deployments: &[DeployedContract]
) -> HashMap<Vec<u8>, Vec<Vec<u8>>> {
let mut class_instances = HashMap::new();
for deployment in deployments {
class_instances
.entry(deployment.class_hash.clone())
.or_insert_with(Vec::new)
.push(deployment.address.clone());
}
class_instances
}
// Deployment statistics
fn calculate_deployment_stats(
deployments: &[DeployedContract]
) -> DeploymentStats {
let mut stats = DeploymentStats::default();
let mut unique_classes = HashSet::new();
for deployment in deployments {
unique_classes.insert(deployment.class_hash.clone());
}
stats.total_deployments = deployments.len();
stats.unique_classes = unique_classes.len();
stats
}
DeclaredClass
pub struct DeclaredClass {
pub class_hash: Vec<u8>,
pub compiled_class_hash: Vec<u8>,
}
Fields
class_hash
- Description: Hash of the Sierra contract class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of Sierra class hash */]
compiled_class_hash
- Description: Hash of the compiled CASM class
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of CASM class hash */]
Example Use Cases
fn process_declaration(
declaration: &DeclaredClass
) -> DeclarationAnalysis {
DeclarationAnalysis {
sierra_class: format!("0x{}", hex::encode(&declaration.class_hash)),
casm_class: format!("0x{}", hex::encode(&declaration.compiled_class_hash)),
timestamp: chrono::Utc::now()
}
}
// Example of declaration validation
fn validate_declaration(
declaration: &DeclaredClass,
existing_classes: &HashMap<Vec<u8>, bool>
) -> Result<(), String> {
// Validate Sierra class hash
if declaration.class_hash.len() != 32 {
return Err("Invalid Sierra class hash length".to_string());
}
// Validate CASM class hash
if declaration.compiled_class_hash.len() != 32 {
return Err("Invalid CASM class hash length".to_string());
}
// Check for existing declaration
if existing_classes.contains_key(&declaration.class_hash) {
return Err("Class already declared".to_string());
}
Ok(())
}
Common Declaration Patterns
// Standard class declaration
let standard_declaration = DeclaredClass {
class_hash: vec![/* Sierra class hash */],
compiled_class_hash: vec![/* CASM class hash */]
};
// Batch declarations
let declaration_batch = vec![
// Declare token contract
DeclaredClass {
class_hash: vec![/* token Sierra hash */],
compiled_class_hash: vec![/* token CASM hash */]
},
// Declare vault contract
DeclaredClass {
class_hash: vec![/* vault Sierra hash */],
compiled_class_hash: vec![/* vault CASM hash */]
}
];
// System contract declarations
let system_declarations = vec![
DeclaredClass {
class_hash: vec![/* proxy Sierra hash */],
compiled_class_hash: vec![/* proxy CASM hash */]
},
DeclaredClass {
class_hash: vec![/* registry Sierra hash */],
compiled_class_hash: vec![/* registry CASM hash */]
}
];
Important Notes
-
Used for:
- Class registration
- Sierra/CASM mapping
- Contract versioning
- Class verification
-
Key aspects:
- Unique class hashes
- Compilation verification
- Version tracking
- Declaration order
Declaration Analysis Examples
// Track class declarations
struct ClassRegistry {
declarations: HashMap<Vec<u8>, DeclaredClass>,
declaration_order: Vec<Vec<u8>>,
}
impl ClassRegistry {
fn register_class(&mut self, declaration: DeclaredClass) -> Result<(), String> {
if self.declarations.contains_key(&declaration.class_hash) {
return Err("Class already registered".to_string());
}
self.declaration_order.push(declaration.class_hash.clone());
self.declarations.insert(declaration.class_hash.clone(), declaration);
Ok(())
}
fn get_casm_hash(&self, sierra_hash: &[u8]) -> Option<&Vec<u8>> {
self.declarations.get(sierra_hash)
.map(|dec| &dec.compiled_class_hash)
}
}
// Declaration statistics
fn analyze_declarations(
declarations: &[DeclaredClass]
) -> DeclarationStats {
DeclarationStats {
total_declarations: declarations.len(),
unique_sierra_classes: declarations.iter()
.map(|d| &d.class_hash)
.collect::<HashSet<_>>()
.len(),
unique_casm_classes: declarations.iter()
.map(|d| &d.compiled_class_hash)
.collect::<HashSet<_>>()
.len()
}
}
ContractStorageDiff
pub struct ContractStorageDiff {
pub address: Vec<u8>,
pub storage_entries: Vec<StorageEntries>,
}
Fields
address
- Description: Address of the contract whose storage is being modified
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of contract address */]
storage_entries
- Description: List of storage changes for the contract
- Type:
Vec<StorageEntry>
- Example: Collection of key-value pairs representing storage changes
Example Use Cases
fn process_storage_diff(
diff: &ContractStorageDiff
) -> StorageDiffAnalysis {
StorageDiffAnalysis {
contract: format!("0x{}", hex::encode(&diff.address)),
changes: diff.storage_entries.len(),
entries: diff.storage_entries.iter()
.map(analyze_storage_entry)
.collect()
}
}
// Example of storage diff validation
fn validate_storage_diff(
diff: &ContractStorageDiff
) -> Result<(), String> {
// Validate contract address
if diff.address.len() != 32 {
return Err("Invalid contract address length".to_string());
}
// Validate storage entries
for entry in &diff.storage_entries {
if entry.key.len() != 32 {
return Err("Invalid storage key length".to_string());
}
if entry.value.len() != 32 {
return Err("Invalid storage value length".to_string());
}
}
Ok(())
}
Common Storage Diff Patterns
// Balance update diff
let balance_update = ContractStorageDiff {
address: vec![/* token contract address */],
storage_entries: vec![
StorageEntry {
key: vec![/* balance mapping key */],
value: vec![/* new balance */]
}
]
};
// Multiple storage updates
let complex_update = ContractStorageDiff {
address: vec![/* contract address */],
storage_entries: vec![
StorageEntry {
key: vec![/* owner key */],
value: vec![/* new owner */]
},
StorageEntry {
key: vec![/* status key */],
value: vec![/* new status */]
},
StorageEntry {
key: vec![/* config key */],
value: vec![/* new config */]
}
]
};
// Batch of contract storage diffs
let batch_diffs = vec![
// Token contract updates
ContractStorageDiff {
address: vec![/* token address */],
storage_entries: vec![/* token storage changes */]
},
// Vault contract updates
ContractStorageDiff {
address: vec![/* vault address */],
storage_entries: vec![/* vault storage changes */]
}
];
Important Notes
-
Used for:
- State updates
- Storage tracking
- Contract modifications
- State synchronization
-
Key aspects:
- Contract-specific changes
- Multiple entry updates
- Key-value modifications
- State transition tracking
Storage Analysis Examples
// Track storage changes by contract
struct StorageChangeTracker {
changes: HashMap<Vec<u8>, Vec<StorageEntry>>,
change_count: HashMap<Vec<u8>, usize>,
}
impl StorageChangeTracker {
fn record_changes(&mut self, diff: &ContractStorageDiff) {
self.changes
.entry(diff.address.clone())
.or_default()
.extend(diff.storage_entries.clone());
*self.change_count
.entry(diff.address.clone())
.or_default() += diff.storage_entries.len();
}
fn get_contract_changes(&self, address: &[u8]) -> Option<&Vec<StorageEntry>> {
self.changes.get(address)
}
fn get_change_count(&self, address: &[u8]) -> usize {
self.change_count.get(address).copied().unwrap_or(0)
}
}
// Analyze storage patterns
fn analyze_storage_patterns(
diffs: &[ContractStorageDiff]
) -> StoragePatternAnalysis {
let mut analysis = StoragePatternAnalysis::default();
for diff in diffs {
analysis.total_changes += diff.storage_entries.len();
analysis.contracts_modified += 1;
// Analyze key patterns
for entry in &diff.storage_entries {
analysis.record_key_pattern(&entry.key);
}
}
analysis
}
StorageEntries
pub struct StorageEntries {
pub key: Vec<u8>,
pub value: Vec<u8>,
}
Fields
key
- Description: Storage slot key
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of storage key */]
value
- Description: Value stored at the key
- Type:
Vec<u8>
- Example:
vec![/* 32 bytes of storage value */]
Example Use Cases
fn process_storage_entry(
entry: &StorageEntries
) -> StorageEntryAnalysis {
StorageEntryAnalysis {
key: format!("0x{}", hex::encode(&entry.key)),
value: format!("0x{}", hex::encode(&entry.value)),
is_zero: entry.value.iter().all(|&b| b == 0),
key_prefix: get_key_prefix(&entry.key)
}
}
// Example of entry validation
fn validate_storage_entry(
entry: &StorageEntries
) -> Result<(), String> {
// Validate key length
if entry.key.len() != 32 {
return Err("Invalid key length".to_string());
}
// Validate value length
if entry.value.len() != 32 {
return Err("Invalid value length".to_string());
}
Ok(())
}
Common Storage Entry Patterns
// Simple value storage
let simple_entry = StorageEntries {
key: vec![/* storage key */],
value: vec![/* stored value */]
};
// Mapping entry (e.g., balances)
let mapping_entry = StorageEntries {
key: compute_mapping_key(
"balances", // mapping name
&address // mapping key
),
value: vec![/* balance amount */]
};
// Common storage patterns
let storage_examples = vec![
// Owner storage
StorageEntries {
key: vec![/* owner slot */],
value: vec![/* owner address */]
},
// Total supply
StorageEntries {
key: vec![/* total supply slot */],
value: vec![/* supply amount */]
},
// Paused status
StorageEntries {
key: vec![/* pause slot */],
value: vec![/* pause status */]
}
];
Important Notes
-
Used for:
- State storage
- Value retrieval
- State modifications
- Data persistence
-
Key aspects:
- Fixed-size keys
- Fixed-size values
- Key uniqueness
- Value immutability
Storage Entry Analysis Examples
// Track value changes
struct ValueChangeTracker {
previous_values: HashMap<Vec<u8>, Vec<u8>>,
changes: Vec<ValueChange>,
}
impl ValueChangeTracker {
fn record_change(&mut self, entry: &StorageEntries) {
let previous = self.previous_values.get(&entry.key).cloned();
self.changes.push(ValueChange {
key: entry.key.clone(),
old_value: previous.clone(),
new_value: entry.value.clone(),
is_initial: previous.is_none(),
});
self.previous_values.insert(entry.key.clone(), entry.value.clone());
}
}
// Analyze storage patterns
fn analyze_storage_patterns(
entries: &[StorageEntries]
) -> StoragePatternAnalysis {
let mut analysis = StoragePatternAnalysis::default();
for entry in entries {
// Analyze key patterns
analysis.record_key_pattern(&entry.key);
// Analyze value patterns
analysis.record_value_pattern(&entry.value);
// Track zero values
if entry.value.iter().all(|&b| b == 0) {
analysis.zero_values += 1;
}
}
analysis
}
// Key computation utilities
fn compute_mapping_key(
name: &str,
key: &[u8]
) -> Vec<u8> {
let mut hasher = Keccak256::new();
hasher.update(name.as_bytes());
hasher.update(key);
hasher.finalize().to_vec()
}