@@ -7,13 +7,13 @@ use alloy::{
77 primitives:: { Address , Bytes , U256 } ,
88 rpc:: types:: TransactionRequest ,
99} ;
10+ use alloy_primitives:: { FixedBytes , utils:: parse_units} ;
1011use semver:: VersionReq ;
11- use serde:: Deserialize ;
12- use serde_json:: Value ;
12+ use serde:: { Deserialize , Serialize } ;
1313
1414use revive_dt_node_interaction:: EthereumNode ;
1515
16- use crate :: metadata:: ContractInstance ;
16+ use crate :: { define_wrapper_type , metadata:: ContractInstance } ;
1717
1818#[ derive( Clone , Debug , Default , Deserialize , Eq , PartialEq ) ]
1919pub struct Input {
@@ -26,7 +26,7 @@ pub struct Input {
2626 #[ serde( default ) ]
2727 pub calldata : Calldata ,
2828 pub expected : Option < Expected > ,
29- pub value : Option < String > ,
29+ pub value : Option < EtherValue > ,
3030 pub storage : Option < HashMap < String , Calldata > > ,
3131}
3232
@@ -40,16 +40,24 @@ pub enum Expected {
4040
4141#[ derive( Clone , Debug , Default , Deserialize , Eq , PartialEq ) ]
4242pub struct ExpectedOutput {
43- compiler_version : Option < VersionReq > ,
44- return_data : Option < Calldata > ,
45- events : Option < Value > ,
46- exception : Option < bool > ,
43+ pub compiler_version : Option < VersionReq > ,
44+ pub return_data : Option < Calldata > ,
45+ pub events : Option < Vec < Event > > ,
46+ #[ serde( default ) ]
47+ pub exception : bool ,
48+ }
49+
50+ #[ derive( Clone , Debug , Default , Deserialize , Eq , PartialEq ) ]
51+ pub struct Event {
52+ pub address : Option < Address > ,
53+ pub topics : Vec < String > ,
54+ pub values : Calldata ,
4755}
4856
4957#[ derive( Clone , Debug , Deserialize , Eq , PartialEq ) ]
5058#[ serde( untagged) ]
5159pub enum Calldata {
52- Single ( String ) ,
60+ Single ( Bytes ) ,
5361 Compound ( Vec < String > ) ,
5462}
5563
@@ -74,6 +82,58 @@ pub enum Method {
7482 FunctionName ( String ) ,
7583}
7684
85+ define_wrapper_type ! (
86+ #[ derive( Clone , Copy , Debug , Default , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
87+ EtherValue ( U256 ) ;
88+ ) ;
89+
90+ impl Serialize for EtherValue {
91+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
92+ where
93+ S : serde:: Serializer ,
94+ {
95+ format ! ( "{} wei" , self . 0 ) . serialize ( serializer)
96+ }
97+ }
98+
99+ impl < ' de > Deserialize < ' de > for EtherValue {
100+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
101+ where
102+ D : serde:: Deserializer < ' de > ,
103+ {
104+ let string = String :: deserialize ( deserializer) ?;
105+ let mut splitted = string. split ( ' ' ) ;
106+ let ( Some ( value) , Some ( unit) ) = ( splitted. next ( ) , splitted. next ( ) ) else {
107+ return Err ( serde:: de:: Error :: custom ( "Failed to parse the value" ) ) ;
108+ } ;
109+ let parsed = parse_units ( value, unit. replace ( "eth" , "ether" ) )
110+ . map_err ( |_| serde:: de:: Error :: custom ( "Failed to parse units" ) ) ?
111+ . into ( ) ;
112+ Ok ( Self ( parsed) )
113+ }
114+ }
115+
116+ impl ExpectedOutput {
117+ pub fn new ( ) -> Self {
118+ Default :: default ( )
119+ }
120+
121+ pub fn with_success ( mut self ) -> Self {
122+ self . exception = false ;
123+ self
124+ }
125+
126+ pub fn with_failure ( mut self ) -> Self {
127+ self . exception = true ;
128+ self
129+ }
130+
131+ pub fn with_calldata ( mut self , calldata : Calldata ) -> Self {
132+ self . return_data = Some ( calldata) ;
133+ self
134+ }
135+ }
136+
77137impl Default for Calldata {
78138 fn default ( ) -> Self {
79139 Self :: Compound ( Default :: default ( ) )
@@ -91,15 +151,25 @@ impl Calldata {
91151 }
92152 }
93153
94- pub fn construct_call_data (
154+ pub fn calldata (
155+ & self ,
156+ deployed_contracts : & HashMap < ContractInstance , ( Address , JsonAbi ) > ,
157+ chain_state_provider : & impl EthereumNode ,
158+ ) -> anyhow:: Result < Vec < u8 > > {
159+ let mut buffer = Vec :: < u8 > :: with_capacity ( self . size_requirement ( ) ) ;
160+ self . calldata_into_slice ( & mut buffer, deployed_contracts, chain_state_provider) ?;
161+ Ok ( buffer)
162+ }
163+
164+ pub fn calldata_into_slice (
95165 & self ,
96166 buffer : & mut Vec < u8 > ,
97167 deployed_contracts : & HashMap < ContractInstance , ( Address , JsonAbi ) > ,
98168 chain_state_provider : & impl EthereumNode ,
99169 ) -> anyhow:: Result < ( ) > {
100170 match self {
101- Calldata :: Single ( string ) => {
102- alloy :: hex :: decode_to_slice ( string , buffer) ? ;
171+ Calldata :: Single ( bytes ) => {
172+ buffer. extend_from_slice ( bytes ) ;
103173 }
104174 Calldata :: Compound ( items) => {
105175 for ( arg_idx, arg) in items. iter ( ) . enumerate ( ) {
@@ -120,16 +190,46 @@ impl Calldata {
120190
121191 pub fn size_requirement ( & self ) -> usize {
122192 match self {
123- Calldata :: Single ( single) => ( single. len ( ) - 2 ) / 2 ,
193+ Calldata :: Single ( single) => single. len ( ) ,
124194 Calldata :: Compound ( items) => items. len ( ) * 32 ,
125195 }
126196 }
127- }
128197
129- impl ExpectedOutput {
130- pub fn find_all_contract_instances ( & self , vec : & mut Vec < ContractInstance > ) {
131- if let Some ( ref cd) = self . return_data {
132- cd. find_all_contract_instances ( vec) ;
198+ /// Checks if this [`Calldata`] is equivalent to the passed calldata bytes.
199+ pub fn is_equivalent (
200+ & self ,
201+ other : & [ u8 ] ,
202+ deployed_contracts : & HashMap < ContractInstance , ( Address , JsonAbi ) > ,
203+ chain_state_provider : & impl EthereumNode ,
204+ ) -> anyhow:: Result < bool > {
205+ match self {
206+ Calldata :: Single ( calldata) => Ok ( calldata == other) ,
207+ Calldata :: Compound ( items) => {
208+ // Chunking the "other" calldata into 32 byte chunks since each
209+ // one of the items in the compound calldata represents 32 bytes
210+ for ( this, other) in items. iter ( ) . zip ( other. chunks ( 32 ) ) {
211+ // The matterlabs format supports wildcards and therefore we
212+ // also need to support them.
213+ if this == "*" {
214+ continue ;
215+ }
216+
217+ let other = if other. len ( ) < 32 {
218+ let mut vec = other. to_vec ( ) ;
219+ vec. resize ( 32 , 0 ) ;
220+ std:: borrow:: Cow :: Owned ( vec)
221+ } else {
222+ std:: borrow:: Cow :: Borrowed ( other)
223+ } ;
224+
225+ let this = resolve_argument ( this, deployed_contracts, chain_state_provider) ?;
226+ let other = U256 :: from_be_slice ( & other) ;
227+ if this != other {
228+ return Ok ( false ) ;
229+ }
230+ }
231+ Ok ( true )
232+ }
133233 }
134234 }
135235}
@@ -153,12 +253,9 @@ impl Input {
153253 ) -> anyhow:: Result < Bytes > {
154254 match self . method {
155255 Method :: Deployer | Method :: Fallback => {
156- let mut calldata = Vec :: < u8 > :: with_capacity ( self . calldata . size_requirement ( ) ) ;
157- self . calldata . construct_call_data (
158- & mut calldata,
159- deployed_contracts,
160- chain_state_provider,
161- ) ?;
256+ let calldata = self
257+ . calldata
258+ . calldata ( deployed_contracts, chain_state_provider) ?;
162259
163260 Ok ( calldata. into ( ) )
164261 }
@@ -204,7 +301,7 @@ impl Input {
204301 // a new buffer for each one of the resolved arguments.
205302 let mut calldata = Vec :: < u8 > :: with_capacity ( 4 + self . calldata . size_requirement ( ) ) ;
206303 calldata. extend ( function. selector ( ) . 0 ) ;
207- self . calldata . construct_call_data (
304+ self . calldata . calldata_into_slice (
208305 & mut calldata,
209306 deployed_contracts,
210307 chain_state_provider,
@@ -222,7 +319,11 @@ impl Input {
222319 chain_state_provider : & impl EthereumNode ,
223320 ) -> anyhow:: Result < TransactionRequest > {
224321 let input_data = self . encoded_input ( deployed_contracts, chain_state_provider) ?;
225- let transaction_request = TransactionRequest :: default ( ) ;
322+ let transaction_request = TransactionRequest :: default ( ) . from ( self . caller ) . value (
323+ self . value
324+ . map ( |value| value. into_inner ( ) )
325+ . unwrap_or_default ( ) ,
326+ ) ;
226327 match self . method {
227328 Method :: Deployer => Ok ( transaction_request. with_deploy_code ( input_data) ) ,
228329 _ => Ok ( transaction_request
@@ -236,20 +337,6 @@ impl Input {
236337 vec. push ( self . instance . clone ( ) ) ;
237338
238339 self . calldata . find_all_contract_instances ( & mut vec) ;
239- match & self . expected {
240- Some ( Expected :: Calldata ( cd) ) => {
241- cd. find_all_contract_instances ( & mut vec) ;
242- }
243- Some ( Expected :: Expected ( expected) ) => {
244- expected. find_all_contract_instances ( & mut vec) ;
245- }
246- Some ( Expected :: ExpectedMany ( expected) ) => {
247- for expected in expected {
248- expected. find_all_contract_instances ( & mut vec) ;
249- }
250- }
251- None => { }
252- }
253340
254341 vec
255342 }
@@ -259,8 +346,10 @@ fn default_instance() -> ContractInstance {
259346 ContractInstance :: new_from ( "Test" )
260347}
261348
262- fn default_caller ( ) -> Address {
263- "90F8bf6A479f320ead074411a4B0e7944Ea8c9C1" . parse ( ) . unwrap ( )
349+ pub const fn default_caller ( ) -> Address {
350+ Address ( FixedBytes ( alloy:: hex!(
351+ "90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"
352+ ) ) )
264353}
265354
266355/// This function takes in the string calldata argument provided in the JSON input and resolves it
@@ -355,22 +444,19 @@ mod tests {
355444
356445 fn trace_transaction (
357446 & self ,
358- _: alloy:: rpc:: types:: TransactionReceipt ,
447+ _: & alloy:: rpc:: types:: TransactionReceipt ,
448+ _: alloy:: rpc:: types:: trace:: geth:: GethDebugTracingOptions ,
359449 ) -> anyhow:: Result < alloy:: rpc:: types:: trace:: geth:: GethTrace > {
360450 unimplemented ! ( )
361451 }
362452
363453 fn state_diff (
364454 & self ,
365- _: alloy:: rpc:: types:: TransactionReceipt ,
455+ _: & alloy:: rpc:: types:: TransactionReceipt ,
366456 ) -> anyhow:: Result < alloy:: rpc:: types:: trace:: geth:: DiffMode > {
367457 unimplemented ! ( )
368458 }
369459
370- fn fetch_add_nonce ( & self , _: Address ) -> anyhow:: Result < u64 > {
371- unimplemented ! ( )
372- }
373-
374460 fn chain_id ( & self ) -> anyhow:: Result < alloy_primitives:: ChainId > {
375461 Ok ( 0x123 )
376462 }
0 commit comments