pallet_evm_precompile_balances_erc20/
eip2612.rs1use super::*;
18use frame_support::{
19 ensure,
20 traits::{Get, Time},
21};
22use sp_core::H256;
23use sp_io::hashing::keccak_256;
24use sp_runtime::traits::UniqueSaturatedInto;
25use sp_std::vec::Vec;
26
27pub const PERMIT_TYPEHASH: [u8; 32] = keccak256!(
29 "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
30);
31
32const PERMIT_DOMAIN: [u8; 32] = keccak256!(
34 "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
35);
36
37pub struct Eip2612<Runtime, Metadata, Instance = ()>(PhantomData<(Runtime, Metadata, Instance)>);
38
39impl<Runtime, Metadata, Instance> Eip2612<Runtime, Metadata, Instance>
40where
41 Runtime: pallet_balances::Config<Instance> + pallet_evm::Config,
42 Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
43 Runtime::RuntimeCall: From<pallet_balances::Call<Runtime, Instance>>,
44 BalanceOf<Runtime, Instance>: TryFrom<U256> + Into<U256>,
45 Metadata: Erc20Metadata,
46 Instance: InstanceToPrefix + 'static,
47 <Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
48{
49 pub fn compute_domain_separator(address: H160) -> [u8; 32] {
50 let name: H256 = keccak_256(Metadata::name().as_bytes()).into();
51 let version: H256 = keccak256!("1").into();
52 let chain_id: U256 = Runtime::ChainId::get().into();
53
54 let domain_separator_inner = solidity::encode_arguments((
55 H256::from(PERMIT_DOMAIN),
56 name,
57 version,
58 chain_id,
59 Address(address),
60 ));
61
62 keccak_256(&domain_separator_inner)
63 }
64
65 pub fn generate_permit(
66 address: H160,
67 owner: H160,
68 spender: H160,
69 value: U256,
70 nonce: U256,
71 deadline: U256,
72 ) -> [u8; 32] {
73 let domain_separator = Self::compute_domain_separator(address);
74
75 let permit_content = solidity::encode_arguments((
76 H256::from(PERMIT_TYPEHASH),
77 Address(owner),
78 Address(spender),
79 value,
80 nonce,
81 deadline,
82 ));
83 let permit_content = keccak_256(&permit_content);
84
85 let mut pre_digest = Vec::with_capacity(2 + 32 + 32);
86 pre_digest.extend_from_slice(b"\x19\x01");
87 pre_digest.extend_from_slice(&domain_separator);
88 pre_digest.extend_from_slice(&permit_content);
89 keccak_256(&pre_digest)
90 }
91
92 #[allow(clippy::too_many_arguments)]
95 pub(crate) fn permit(
96 handle: &mut impl PrecompileHandle,
97 owner: Address,
98 spender: Address,
99 value: U256,
100 deadline: U256,
101 v: u8,
102 r: H256,
103 s: H256,
104 ) -> EvmResult {
105 handle.record_db_read::<Runtime>(104)?;
107
108 let owner: H160 = owner.into();
109 let spender: H160 = spender.into();
110
111 let timestamp: u128 =
113 <Runtime as pallet_evm::Config>::Timestamp::now().unique_saturated_into();
114 let timestamp: U256 = U256::from(timestamp / 1000);
115
116 ensure!(deadline >= timestamp, revert("Permit expired"));
117
118 let nonce = NoncesStorage::<Instance>::get(owner);
119
120 let permit = Self::generate_permit(
121 handle.context().address,
122 owner,
123 spender,
124 value,
125 nonce,
126 deadline,
127 );
128
129 let mut sig = [0u8; 65];
130 sig[0..32].copy_from_slice(r.as_bytes());
131 sig[32..64].copy_from_slice(s.as_bytes());
132 sig[64] = v;
133
134 let signer = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &permit)
135 .map_err(|_| revert("Invalid permit"))?;
136 let signer = H160::from(H256::from_slice(keccak_256(&signer).as_slice()));
137
138 ensure!(
139 signer != H160::zero() && signer == owner,
140 revert("Invalid permit")
141 );
142
143 NoncesStorage::<Instance>::insert(owner, nonce + U256::one());
144
145 {
146 let amount =
147 Erc20BalancesPrecompile::<Runtime, Metadata, Instance>::u256_to_amount(value)
148 .unwrap_or_else(|_| Bounded::max_value());
149
150 let owner: Runtime::AccountId = Runtime::AddressMapping::into_account_id(owner);
151 let spender: Runtime::AccountId = Runtime::AddressMapping::into_account_id(spender);
152 ApprovesStorage::<Runtime, Instance>::insert(owner, spender, amount);
153 }
154
155 log3(
156 handle.context().address,
157 SELECTOR_LOG_APPROVAL,
158 owner,
159 spender,
160 solidity::encode_event_data(value),
161 )
162 .record(handle)?;
163
164 Ok(())
165 }
166
167 pub(crate) fn nonces(handle: &mut impl PrecompileHandle, owner: Address) -> EvmResult<U256> {
168 handle.record_db_read::<Runtime>(104)?;
170
171 let owner: H160 = owner.into();
172
173 Ok(NoncesStorage::<Instance>::get(owner))
174 }
175
176 pub(crate) fn domain_separator(handle: &mut impl PrecompileHandle) -> EvmResult<H256> {
177 handle.record_db_read::<Runtime>(8)?;
179
180 Ok(Self::compute_domain_separator(handle.context().address).into())
181 }
182}