@@ -3,10 +3,13 @@ use primitives::{
33 AssetId , Chain , SimulationHeader , SimulationPayloadField , SimulationPayloadFieldDisplay , SimulationPayloadFieldKind , SimulationPayloadFieldType , SimulationResult ,
44 SimulationSeverity , SimulationWarning , SimulationWarningType ,
55} ;
6+ use std:: time:: { Duration , SystemTime , UNIX_EPOCH } ;
67
78use super :: { approval_method:: ApprovalMethod , approval_value:: ApprovalValue } ;
89use gem_evm:: ethereum_address_checksum;
910
11+ const EXCESSIVE_EXPIRATION_WINDOW : Duration = Duration :: from_secs ( 60 * 60 * 24 * 30 ) ;
12+
1013#[ derive( Debug , Clone , PartialEq ) ]
1114pub ( crate ) struct ApprovalRequest {
1215 asset_id : AssetId ,
@@ -15,36 +18,41 @@ pub(crate) struct ApprovalRequest {
1518 pub ( crate ) spender_address : String ,
1619 method : ApprovalMethod ,
1720 approval_value : Option < ApprovalValue > ,
18- expiration : Option < String > ,
21+ display_expiration : Option < u64 > ,
22+ warning_expiration : Option < u64 > ,
1923}
2024
2125impl ApprovalRequest {
2226 pub ( crate ) fn erc20 ( chain : Chain , contract_address : & str , spender_address : String , approval_value : String ) -> Option < Self > {
23- let contract_address = ethereum_address_checksum ( contract_address) . ok ( ) ?;
24- let spender_address = ethereum_address_checksum ( & spender_address) . ok ( ) ?;
25- Some ( Self {
26- asset_id : AssetId :: from_token ( chain, & contract_address) ,
27- contract_address,
28- token_address : None ,
29- spender_address,
30- method : ApprovalMethod :: Approve ,
31- approval_value : ApprovalValue :: from_raw ( & approval_value) ,
32- expiration : None ,
33- } )
27+ Self :: new (
28+ chain,
29+ ApprovalContext {
30+ contract_address : contract_address. to_string ( ) ,
31+ spender_address,
32+ token_address : None ,
33+ approval_value : ApprovalValue :: from_raw ( & approval_value) ,
34+ display_expiration : None ,
35+ warning_expiration : None ,
36+ method : ApprovalMethod :: Approve ,
37+ token_field : TokenField :: AlwaysHide ,
38+ } ,
39+ )
3440 }
3541
3642 pub ( crate ) fn nft_collection ( chain : Chain , contract_address : & str , spender_address : String ) -> Option < Self > {
37- let contract_address = ethereum_address_checksum ( contract_address) . ok ( ) ?;
38- let spender_address = ethereum_address_checksum ( & spender_address) . ok ( ) ?;
39- Some ( Self {
40- asset_id : AssetId :: from_token ( chain, & contract_address) ,
41- contract_address,
42- token_address : None ,
43- spender_address,
44- method : ApprovalMethod :: SetApprovalForAll ,
45- approval_value : None ,
46- expiration : None ,
47- } )
43+ Self :: new (
44+ chain,
45+ ApprovalContext {
46+ contract_address : contract_address. to_string ( ) ,
47+ spender_address,
48+ token_address : None ,
49+ approval_value : None ,
50+ display_expiration : None ,
51+ warning_expiration : None ,
52+ method : ApprovalMethod :: SetApprovalForAll ,
53+ token_field : TokenField :: AlwaysHide ,
54+ } ,
55+ )
4856 }
4957
5058 pub ( crate ) fn permit (
@@ -56,42 +64,70 @@ impl ApprovalRequest {
5664 token_address : Option < String > ,
5765 method : ApprovalMethod ,
5866 ) -> Option < Self > {
59- let contract_address = ethereum_address_checksum ( & contract_address) . ok ( ) ?;
60- let spender_address = ethereum_address_checksum ( & spender_address) . ok ( ) ?;
61- let token_address = token_address. map ( |value| ethereum_address_checksum ( & value) ) . transpose ( ) . ok ( ) ?;
62- let asset_address = token_address. clone ( ) . unwrap_or_else ( || contract_address. clone ( ) ) ;
63- let token_address = ( asset_address != contract_address) . then_some ( asset_address. clone ( ) ) ;
64- Some ( Self {
65- asset_id : AssetId :: from_token ( chain, & asset_address) ,
66- contract_address,
67- token_address,
68- spender_address,
69- method,
70- approval_value : ApprovalValue :: from_raw ( & approval_value) ,
71- expiration,
72- } )
67+ Self :: new (
68+ chain,
69+ ApprovalContext {
70+ contract_address,
71+ spender_address,
72+ token_address,
73+ approval_value : ApprovalValue :: from_raw ( & approval_value) ,
74+ display_expiration : expiration. as_deref ( ) . map ( str:: parse) . transpose ( ) . ok ( ) ?,
75+ warning_expiration : expiration. as_deref ( ) . map ( str:: parse) . transpose ( ) . ok ( ) ?,
76+ method,
77+ token_field : TokenField :: HideWhenMatchingContract ,
78+ } ,
79+ )
7380 }
7481
75- pub ( crate ) fn permit_batch ( chain : Chain , contract_address : String , spender_address : String , approval_value : ApprovalValue , token_address : Option < String > ) -> Option < Self > {
76- let contract_address = ethereum_address_checksum ( & contract_address) . ok ( ) ?;
77- let spender_address = ethereum_address_checksum ( & spender_address) . ok ( ) ?;
78- let token_address = token_address. map ( |value| ethereum_address_checksum ( & value) ) . transpose ( ) . ok ( ) ?;
82+ pub ( crate ) fn permit_batch (
83+ chain : Chain ,
84+ contract_address : String ,
85+ spender_address : String ,
86+ approval_value : ApprovalValue ,
87+ token_address : Option < String > ,
88+ warning_expiration : Option < u64 > ,
89+ ) -> Option < Self > {
90+ Self :: new (
91+ chain,
92+ ApprovalContext {
93+ contract_address,
94+ spender_address,
95+ token_address,
96+ approval_value : Some ( approval_value) ,
97+ display_expiration : None ,
98+ warning_expiration,
99+ method : ApprovalMethod :: PermitBatch ,
100+ token_field : TokenField :: ShowWhenPresent ,
101+ } ,
102+ )
103+ }
104+
105+ fn new ( chain : Chain , context : ApprovalContext ) -> Option < Self > {
106+ let contract_address = ethereum_address_checksum ( & context. contract_address ) . ok ( ) ?;
107+ let spender_address = ethereum_address_checksum ( & context. spender_address ) . ok ( ) ?;
108+ let token_address = context. token_address . map ( |value| ethereum_address_checksum ( & value) ) . transpose ( ) . ok ( ) ?;
79109 let asset_address = token_address. clone ( ) . unwrap_or_else ( || contract_address. clone ( ) ) ;
110+ let token_address = match context. token_field {
111+ TokenField :: AlwaysHide => None ,
112+ TokenField :: HideWhenMatchingContract if asset_address == contract_address => None ,
113+ TokenField :: HideWhenMatchingContract | TokenField :: ShowWhenPresent => token_address,
114+ } ;
80115
81116 Some ( Self {
82117 asset_id : AssetId :: from_token ( chain, & asset_address) ,
83118 contract_address,
84119 token_address,
85120 spender_address,
86- method : ApprovalMethod :: PermitBatch ,
87- approval_value : Some ( approval_value) ,
88- expiration : None ,
121+ method : context. method ,
122+ approval_value : context. approval_value ,
123+ display_expiration : context. display_expiration ,
124+ warning_expiration : context. warning_expiration ,
89125 } )
90126 }
91127
92128 pub ( crate ) fn simulate ( self ) -> SimulationResult {
93- let warning = self . primary_warning ( ) ;
94- self . build_simulation_result ( vec ! [ warning ] )
129+ let warnings = self . warnings ( ) ;
130+ self . build_simulation_result ( warnings )
95131 }
96132
97133 pub ( crate ) fn build_simulation_result ( self , warnings : Vec < SimulationWarning > ) -> SimulationResult {
@@ -135,13 +171,35 @@ impl ApprovalRequest {
135171 )
136172 }
137173
174+ pub ( crate ) fn warnings ( & self ) -> Vec < SimulationWarning > {
175+ let mut warnings = vec ! [ self . primary_warning( ) ] ;
176+ if let Some ( warning) = self . expiration_warning ( ) {
177+ warnings. push ( warning) ;
178+ }
179+ warnings
180+ }
181+
138182 fn warning_approval_value ( & self ) -> Option < BigInt > {
139183 match self . approval_value . as_ref ( ) {
140184 Some ( ApprovalValue :: Exact ( value) ) => Some ( BigInt :: from ( value. clone ( ) ) ) ,
141185 Some ( ApprovalValue :: Unlimited ) | None => None ,
142186 }
143187 }
144188
189+ pub ( crate ) fn expiration_warning ( & self ) -> Option < SimulationWarning > {
190+ let expiration = self . warning_expiration ?;
191+ let now = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . ok ( ) ?. as_secs ( ) ;
192+ if expiration <= now. saturating_add ( EXCESSIVE_EXPIRATION_WINDOW . as_secs ( ) ) {
193+ return None ;
194+ }
195+
196+ Some ( SimulationWarning :: new (
197+ SimulationSeverity :: Warning ,
198+ SimulationWarningType :: ValidationError ,
199+ Some ( "Excessive expiration" . to_string ( ) ) ,
200+ ) )
201+ }
202+
145203 fn payload ( & self ) -> Vec < SimulationPayloadField > {
146204 let mut payload = vec ! [
147205 SimulationPayloadField :: standard(
@@ -185,12 +243,10 @@ impl ApprovalRequest {
185243 ) ) ;
186244 }
187245
188- if self . method . supports_value_display ( )
189- && let Some ( expiration) = self . expiration . as_deref ( )
190- {
246+ if let Some ( expiration) = self . display_expiration {
191247 payload. push ( SimulationPayloadField :: custom (
192248 "expiration" ,
193- expiration,
249+ expiration. to_string ( ) ,
194250 SimulationPayloadFieldType :: Timestamp ,
195251 SimulationPayloadFieldDisplay :: Secondary ,
196252 ) ) ;
@@ -199,3 +255,22 @@ impl ApprovalRequest {
199255 payload
200256 }
201257}
258+
259+ #[ derive( Debug , Clone ) ]
260+ struct ApprovalContext {
261+ contract_address : String ,
262+ spender_address : String ,
263+ token_address : Option < String > ,
264+ approval_value : Option < ApprovalValue > ,
265+ display_expiration : Option < u64 > ,
266+ warning_expiration : Option < u64 > ,
267+ method : ApprovalMethod ,
268+ token_field : TokenField ,
269+ }
270+
271+ #[ derive( Debug , Clone , Copy ) ]
272+ enum TokenField {
273+ AlwaysHide ,
274+ HideWhenMatchingContract ,
275+ ShowWhenPresent ,
276+ }
0 commit comments