diff --git a/starknet-providers/src/sequencer/models/conversions.rs b/starknet-providers/src/sequencer/models/conversions.rs index bd487ed5..739cc829 100644 --- a/starknet-providers/src/sequencer/models/conversions.rs +++ b/starknet-providers/src/sequencer/models/conversions.rs @@ -457,14 +457,14 @@ impl TryFrom for core::TransactionFinalityStatus { impl From for InvokeFunctionTransactionRequest { fn from(value: core::BroadcastedInvokeTransaction) -> Self { - Self { + Self::V1(InvokeFunctionV1TransactionRequest { sender_address: value.sender_address, calldata: value.calldata, signature: value.signature, max_fee: value.max_fee, nonce: value.nonce, is_query: value.is_query, - } + }) } } @@ -513,7 +513,7 @@ impl TryFrom for DeclareV2TransactionRequ impl From for DeployAccountTransactionRequest { fn from(value: core::BroadcastedDeployAccountTransaction) -> Self { - Self { + Self::V1(DeployAccountV1TransactionRequest { class_hash: value.class_hash, contract_address_salt: value.contract_address_salt, constructor_calldata: value.constructor_calldata, @@ -521,7 +521,7 @@ impl From for DeployAccountTransactio signature: value.signature, nonce: value.nonce, is_query: value.is_query, - } + }) } } diff --git a/starknet-providers/src/sequencer/models/mod.rs b/starknet-providers/src/sequencer/models/mod.rs index fd40c6b1..3e269958 100644 --- a/starknet-providers/src/sequencer/models/mod.rs +++ b/starknet-providers/src/sequencer/models/mod.rs @@ -33,8 +33,13 @@ pub use transaction_request::{ DeclareTransaction as DeclareTransactionRequest, DeclareV1Transaction as DeclareV1TransactionRequest, DeclareV2Transaction as DeclareV2TransactionRequest, + DeclareV3Transaction as DeclareV3TransactionRequest, DeployAccountTransaction as DeployAccountTransactionRequest, - InvokeFunctionTransaction as InvokeFunctionTransactionRequest, TransactionRequest, + DeployAccountV1Transaction as DeployAccountV1TransactionRequest, + DeployAccountV3Transaction as DeployAccountV3TransactionRequest, + InvokeFunctionTransaction as InvokeFunctionTransactionRequest, + InvokeFunctionV1Transaction as InvokeFunctionV1TransactionRequest, + InvokeFunctionV3Transaction as InvokeFunctionV3TransactionRequest, TransactionRequest, }; mod contract; @@ -45,3 +50,5 @@ pub use state_update::StateUpdate; pub mod trace; pub use trace::{BlockTraces, TransactionTrace}; + +pub(crate) mod serde_impls; diff --git a/starknet-providers/src/sequencer/models/serde_impls.rs b/starknet-providers/src/sequencer/models/serde_impls.rs new file mode 100644 index 00000000..f54d344f --- /dev/null +++ b/starknet-providers/src/sequencer/models/serde_impls.rs @@ -0,0 +1,120 @@ +pub(crate) mod u64_hex { + use serde::{de::Visitor, Deserialize, Serialize}; + + struct U64HexVisitor; + + pub fn serialize(v: &u64, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&format!("{:#x}", v)) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(U64HexVisitor) + } + + impl<'de> Visitor<'de> for U64HexVisitor { + type Value = u64; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + u64::from_str_radix(v.trim_start_matches("0x"), 16) + .map_err(|err| serde::de::Error::custom(format!("invalid u64 hex string: {err}"))) + } + } +} + +pub(crate) mod u128_hex { + use serde::{de::Visitor, Deserialize, Serialize}; + + struct U128HexVisitor; + + pub fn serialize(v: &u128, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&format!("{:#x}", v)) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(U128HexVisitor) + } + + impl<'de> Visitor<'de> for U128HexVisitor { + type Value = u128; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + u128::from_str_radix(v.trim_start_matches("0x"), 16) + .map_err(|err| serde::de::Error::custom(format!("invalid u128 hex string: {err}"))) + } + } +} + +pub(crate) mod u64_hex_opt { + use serde::{de::Visitor, Deserialize, Serialize}; + + struct U64HexOptVisitor; + + pub fn serialize(v: &Option, serializer: S) -> Result + where + S: serde::Serializer, + { + match v { + Some(v) => serializer.serialize_str(&format!("{:#x}", v)), + None => serializer.serialize_none(), + } + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(U64HexOptVisitor) + } + + impl<'de> Visitor<'de> for U64HexOptVisitor { + type Value = Option; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "null or string") + } + + fn visit_none(self) -> Result + where + E: serde::de::Error, + { + Ok(None) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(Some( + u64::from_str_radix(v.trim_start_matches("0x"), 16).map_err(|err| { + serde::de::Error::custom(format!("invalid u64 hex string: {err}")) + })?, + )) + } + } +} diff --git a/starknet-providers/src/sequencer/models/transaction.rs b/starknet-providers/src/sequencer/models/transaction.rs index e2a2546a..23683e6f 100644 --- a/starknet-providers/src/sequencer/models/transaction.rs +++ b/starknet-providers/src/sequencer/models/transaction.rs @@ -1,4 +1,4 @@ -use serde::Deserialize; +use serde::{de::Visitor, Deserialize, Serialize}; use serde_with::serde_as; use starknet_core::{ serde::unsigned_field_element::{UfeHex, UfePendingBlockHash}, @@ -6,6 +6,7 @@ use starknet_core::{ }; use super::{ + serde_impls::{u128_hex, u64_hex, u64_hex_opt}, transaction_receipt::{TransactionExecutionStatus, TransactionFinalityStatus}, TransactionStatus, }; @@ -102,12 +103,11 @@ pub struct DeclareTransaction { pub transaction_hash: FieldElement, #[serde_as(deserialize_as = "Vec")] pub signature: Vec, - pub nonce_data_availability_mode: Option, - pub fee_data_availability_mode: Option, + pub nonce_data_availability_mode: Option, + pub fee_data_availability_mode: Option, pub resource_bounds: Option, - #[serde(default)] - #[serde_as(as = "Option")] - pub tip: Option, + #[serde(default, with = "u64_hex_opt")] + pub tip: Option, #[serde_as(as = "Option>")] pub paymaster_data: Option>, #[serde_as(deserialize_as = "Option>")] @@ -156,12 +156,11 @@ pub struct DeployAccountTransaction { #[serde(default)] #[serde_as(as = "Option")] pub max_fee: Option, - pub nonce_data_availability_mode: Option, - pub fee_data_availability_mode: Option, + pub nonce_data_availability_mode: Option, + pub fee_data_availability_mode: Option, pub resource_bounds: Option, - #[serde(default)] - #[serde_as(as = "Option")] - pub tip: Option, + #[serde(default, with = "u64_hex_opt")] + pub tip: Option, #[serde_as(as = "Option>")] pub paymaster_data: Option>, #[serde(default)] @@ -190,12 +189,11 @@ pub struct InvokeFunctionTransaction { pub max_fee: Option, #[serde_as(as = "Option")] pub nonce: Option, - pub nonce_data_availability_mode: Option, - pub fee_data_availability_mode: Option, + pub nonce_data_availability_mode: Option, + pub fee_data_availability_mode: Option, pub resource_bounds: Option, - #[serde(default)] - #[serde_as(as = "Option")] - pub tip: Option, + #[serde(default, with = "u64_hex_opt")] + pub tip: Option, #[serde_as(as = "Option>")] pub paymaster_data: Option>, #[serde_as(deserialize_as = "Option>")] @@ -222,24 +220,31 @@ pub struct L1HandlerTransaction { pub version: FieldElement, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub struct ResourceBoundsMapping { - l1_gas: ResourceBounds, - l2_gas: ResourceBounds, + pub l1_gas: ResourceBounds, + pub l2_gas: ResourceBounds, } -#[serde_as] -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))] pub struct ResourceBounds { - #[serde_as(as = "UfeHex")] - pub max_amount: FieldElement, - #[serde_as(as = "UfeHex")] - pub max_price_per_unit: FieldElement, + #[serde(with = "u64_hex")] + pub max_amount: u64, + #[serde(with = "u128_hex")] + pub max_price_per_unit: u128, } +#[derive(Debug)] +pub enum DataAvailabilityMode { + L1, + L2, +} + +struct DataAvailabilityModeVisitor; + impl TransactionType { pub fn transaction_hash(&self) -> FieldElement { match self { @@ -252,6 +257,49 @@ impl TransactionType { } } +impl Serialize for DataAvailabilityMode { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_u32(match self { + Self::L1 => 0, + Self::L2 => 1, + }) + } +} + +impl<'de> Deserialize<'de> for DataAvailabilityMode { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(DataAvailabilityModeVisitor) + } +} + +impl<'de> Visitor<'de> for DataAvailabilityModeVisitor { + type Value = DataAvailabilityMode; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "integer") + } + + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, + { + match v { + 0 => Ok(DataAvailabilityMode::L1), + 1 => Ok(DataAvailabilityMode::L2), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(v), + &"0 or 1", + )), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/starknet-providers/src/sequencer/models/transaction_receipt.rs b/starknet-providers/src/sequencer/models/transaction_receipt.rs index a1ffb76a..78fb6565 100644 --- a/starknet-providers/src/sequencer/models/transaction_receipt.rs +++ b/starknet-providers/src/sequencer/models/transaction_receipt.rs @@ -87,6 +87,7 @@ pub struct BuiltinInstanceCounter { pub ec_op_builtin: Option, pub poseidon_builtin: Option, pub keccak_builtin: Option, + pub segment_arena_builtin: Option, } #[serde_as] diff --git a/starknet-providers/src/sequencer/models/transaction_request.rs b/starknet-providers/src/sequencer/models/transaction_request.rs index 63b7d033..ff294141 100644 --- a/starknet-providers/src/sequencer/models/transaction_request.rs +++ b/starknet-providers/src/sequencer/models/transaction_request.rs @@ -8,6 +8,8 @@ use std::sync::Arc; use super::{ contract::{CompressedLegacyContractClass, CompressedSierraClass}, + serde_impls::u64_hex, + transaction::{DataAvailabilityMode, ResourceBoundsMapping}, L1Address, }; @@ -70,6 +72,7 @@ pub enum TransactionRequest { pub enum DeclareTransaction { V1(DeclareV1Transaction), V2(DeclareV2Transaction), + V3(DeclareV3Transaction), } #[derive(Debug)] @@ -106,7 +109,37 @@ pub struct DeclareV2Transaction { } #[derive(Debug)] -pub struct InvokeFunctionTransaction { +pub struct DeclareV3Transaction { + pub contract_class: Arc, + /// Hash of the compiled class obtained by running `starknet-sierra-compile` on the Sierra + /// class. This is required because at the moment, Sierra compilation is not proven, allowing + /// the sequencer to run arbitrary code if this is not signed. It's expected that in the future + /// this will no longer be required. + pub compiled_class_hash: FieldElement, + /// The address of the account contract sending the declaration transaction. + pub sender_address: FieldElement, + /// Additional information given by the caller that represents the signature of the transaction. + pub signature: Vec, + /// A sequential integer used to distinguish between transactions and order them. + pub nonce: FieldElement, + pub nonce_data_availability_mode: DataAvailabilityMode, + pub fee_data_availability_mode: DataAvailabilityMode, + pub resource_bounds: ResourceBoundsMapping, + pub tip: u64, + pub paymaster_data: Vec, + pub account_deployment_data: Vec, + pub is_query: bool, +} + +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum InvokeFunctionTransaction { + V1(InvokeFunctionV1Transaction), + V3(InvokeFunctionV3Transaction), +} + +#[derive(Debug)] +pub struct InvokeFunctionV1Transaction { pub sender_address: FieldElement, pub calldata: Vec, pub signature: Vec, @@ -116,7 +149,29 @@ pub struct InvokeFunctionTransaction { } #[derive(Debug)] -pub struct DeployAccountTransaction { +pub struct InvokeFunctionV3Transaction { + pub sender_address: FieldElement, + pub calldata: Vec, + pub signature: Vec, + pub nonce: FieldElement, + pub nonce_data_availability_mode: DataAvailabilityMode, + pub fee_data_availability_mode: DataAvailabilityMode, + pub resource_bounds: ResourceBoundsMapping, + pub tip: u64, + pub paymaster_data: Vec, + pub account_deployment_data: Vec, + pub is_query: bool, +} + +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum DeployAccountTransaction { + V1(DeployAccountV1Transaction), + V3(DeployAccountV3Transaction), +} + +#[derive(Debug)] +pub struct DeployAccountV1Transaction { pub class_hash: FieldElement, pub contract_address_salt: FieldElement, pub constructor_calldata: Vec, @@ -129,6 +184,23 @@ pub struct DeployAccountTransaction { pub is_query: bool, } +#[derive(Debug)] +pub struct DeployAccountV3Transaction { + pub class_hash: FieldElement, + pub contract_address_salt: FieldElement, + pub constructor_calldata: Vec, + // The signature of the transaction. + pub signature: Vec, + // The nonce of the transaction. + pub nonce: FieldElement, + pub nonce_data_availability_mode: DataAvailabilityMode, + pub fee_data_availability_mode: DataAvailabilityMode, + pub resource_bounds: ResourceBoundsMapping, + pub tip: u64, + pub paymaster_data: Vec, + pub is_query: bool, +} + impl Serialize for DeclareV1Transaction { fn serialize(&self, serializer: S) -> Result where @@ -206,7 +278,60 @@ impl Serialize for DeclareV2Transaction { } } -impl Serialize for InvokeFunctionTransaction { +impl Serialize for DeclareV3Transaction { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + #[serde_as] + #[derive(Serialize)] + struct Versioned<'a> { + #[serde_as(as = "UfeHex")] + version: FieldElement, + contract_class: &'a CompressedSierraClass, + #[serde_as(as = "UfeHex")] + compiled_class_hash: &'a FieldElement, + #[serde_as(as = "UfeHex")] + sender_address: &'a FieldElement, + #[serde_as(as = "Vec")] + signature: &'a Vec, + #[serde_as(as = "UfeHex")] + nonce: &'a FieldElement, + nonce_data_availability_mode: &'a DataAvailabilityMode, + fee_data_availability_mode: &'a DataAvailabilityMode, + resource_bounds: &'a ResourceBoundsMapping, + #[serde(with = "u64_hex")] + tip: &'a u64, + #[serde_as(as = "Vec")] + paymaster_data: &'a Vec, + #[serde_as(as = "Vec")] + account_deployment_data: &'a Vec, + } + + let versioned = Versioned { + version: if self.is_query { + QUERY_VERSION_THREE + } else { + FieldElement::THREE + }, + contract_class: &self.contract_class, + compiled_class_hash: &self.compiled_class_hash, + sender_address: &self.sender_address, + signature: &self.signature, + nonce: &self.nonce, + nonce_data_availability_mode: &self.nonce_data_availability_mode, + fee_data_availability_mode: &self.fee_data_availability_mode, + resource_bounds: &self.resource_bounds, + tip: &self.tip, + paymaster_data: &self.paymaster_data, + account_deployment_data: &self.account_deployment_data, + }; + + Versioned::serialize(&versioned, serializer) + } +} + +impl Serialize for InvokeFunctionV1Transaction { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -243,7 +368,57 @@ impl Serialize for InvokeFunctionTransaction { } } -impl Serialize for DeployAccountTransaction { +impl Serialize for InvokeFunctionV3Transaction { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + #[serde_as] + #[derive(Serialize)] + struct Versioned<'a> { + #[serde_as(as = "UfeHex")] + version: FieldElement, + #[serde_as(as = "UfeHex")] + sender_address: &'a FieldElement, + calldata: &'a Vec, + #[serde_as(as = "Vec")] + signature: &'a Vec, + #[serde_as(as = "UfeHex")] + nonce: &'a FieldElement, + nonce_data_availability_mode: &'a DataAvailabilityMode, + fee_data_availability_mode: &'a DataAvailabilityMode, + resource_bounds: &'a ResourceBoundsMapping, + #[serde(with = "u64_hex")] + tip: &'a u64, + #[serde_as(as = "Vec")] + paymaster_data: &'a Vec, + #[serde_as(as = "Vec")] + account_deployment_data: &'a Vec, + } + + let versioned = Versioned { + version: if self.is_query { + QUERY_VERSION_THREE + } else { + FieldElement::THREE + }, + sender_address: &self.sender_address, + calldata: &self.calldata, + signature: &self.signature, + nonce: &self.nonce, + nonce_data_availability_mode: &self.nonce_data_availability_mode, + fee_data_availability_mode: &self.fee_data_availability_mode, + resource_bounds: &self.resource_bounds, + tip: &self.tip, + paymaster_data: &self.paymaster_data, + account_deployment_data: &self.account_deployment_data, + }; + + Versioned::serialize(&versioned, serializer) + } +} + +impl Serialize for DeployAccountV1Transaction { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -283,6 +458,57 @@ impl Serialize for DeployAccountTransaction { } } +impl Serialize for DeployAccountV3Transaction { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + #[serde_as] + #[derive(Serialize)] + struct Versioned<'a> { + #[serde_as(as = "UfeHex")] + version: FieldElement, + #[serde_as(as = "UfeHex")] + class_hash: &'a FieldElement, + #[serde_as(as = "UfeHex")] + contract_address_salt: &'a FieldElement, + #[serde_as(as = "Vec")] + constructor_calldata: &'a Vec, + #[serde_as(as = "Vec")] + signature: &'a Vec, + #[serde_as(as = "UfeHex")] + nonce: &'a FieldElement, + nonce_data_availability_mode: &'a DataAvailabilityMode, + fee_data_availability_mode: &'a DataAvailabilityMode, + resource_bounds: &'a ResourceBoundsMapping, + #[serde(with = "u64_hex")] + tip: &'a u64, + #[serde_as(as = "Vec")] + paymaster_data: &'a Vec, + } + + let versioned = Versioned { + version: if self.is_query { + QUERY_VERSION_THREE + } else { + FieldElement::THREE + }, + class_hash: &self.class_hash, + contract_address_salt: &self.contract_address_salt, + constructor_calldata: &self.constructor_calldata, + signature: &self.signature, + nonce: &self.nonce, + nonce_data_availability_mode: &self.nonce_data_availability_mode, + fee_data_availability_mode: &self.fee_data_availability_mode, + resource_bounds: &self.resource_bounds, + tip: &self.tip, + paymaster_data: &self.paymaster_data, + }; + + Versioned::serialize(&versioned, serializer) + } +} + fn l1_addr_as_dec(value: &L1Address, serializer: S) -> Result where S: Serializer,