1
// Copyright 2019-2022 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Precompile to interact with pallet_balances instances using the ERC20 interface standard.
18

            
19
#![cfg_attr(not(feature = "std"), no_std)]
20

            
21
use account::SYSTEM_ACCOUNT_SIZE;
22
use fp_evm::PrecompileHandle;
23
use frame_support::{
24
	dispatch::{GetDispatchInfo, PostDispatchInfo},
25
	sp_runtime::traits::{Bounded, CheckedSub, Dispatchable, StaticLookup},
26
	storage::types::{StorageDoubleMap, StorageMap, ValueQuery},
27
	traits::StorageInstance,
28
	Blake2_128Concat,
29
};
30
use pallet_balances::pallet::{
31
	Instance1, Instance10, Instance11, Instance12, Instance13, Instance14, Instance15, Instance16,
32
	Instance2, Instance3, Instance4, Instance5, Instance6, Instance7, Instance8, Instance9,
33
};
34
use pallet_evm::AddressMapping;
35
use precompile_utils::prelude::*;
36
use sp_core::{H160, H256, U256};
37
use sp_std::{
38
	convert::{TryFrom, TryInto},
39
	marker::PhantomData,
40
};
41

            
42
mod eip2612;
43
use eip2612::Eip2612;
44

            
45
#[cfg(test)]
46
mod mock;
47
#[cfg(test)]
48
mod tests;
49

            
50
/// Solidity selector of the Transfer log, which is the Keccak of the Log signature.
51
pub const SELECTOR_LOG_TRANSFER: [u8; 32] = keccak256!("Transfer(address,address,uint256)");
52

            
53
/// Solidity selector of the Approval log, which is the Keccak of the Log signature.
54
pub const SELECTOR_LOG_APPROVAL: [u8; 32] = keccak256!("Approval(address,address,uint256)");
55

            
56
/// Solidity selector of the Deposit log, which is the Keccak of the Log signature.
57
pub const SELECTOR_LOG_DEPOSIT: [u8; 32] = keccak256!("Deposit(address,uint256)");
58

            
59
/// Solidity selector of the Withdraw log, which is the Keccak of the Log signature.
60
pub const SELECTOR_LOG_WITHDRAWAL: [u8; 32] = keccak256!("Withdrawal(address,uint256)");
61

            
62
/// Associates pallet Instance to a prefix used for the Approves storage.
63
/// This trait is implemented for () and the 16 substrate Instance.
64
pub trait InstanceToPrefix {
65
	/// Prefix used for the Approves storage.
66
	type ApprovesPrefix: StorageInstance;
67

            
68
	/// Prefix used for the Approves storage.
69
	type NoncesPrefix: StorageInstance;
70
}
71

            
72
// We use a macro to implement the trait for () and the 16 substrate Instance.
73
macro_rules! impl_prefix {
74
	($instance:ident, $name:literal) => {
75
		// Using `paste!` we generate a dedicated module to avoid collisions
76
		// between each instance `Approves` struct.
77
		paste::paste! {
78
			mod [<_impl_prefix_ $instance:snake>] {
79
				use super::*;
80

            
81
				pub struct Approves;
82

            
83
				impl StorageInstance for Approves {
84
					const STORAGE_PREFIX: &'static str = "Approves";
85

            
86
17
					fn pallet_prefix() -> &'static str {
87
17
						$name
88
17
					}
89
				}
90

            
91
				pub struct Nonces;
92

            
93
				impl StorageInstance for Nonces {
94
					const STORAGE_PREFIX: &'static str = "Nonces";
95

            
96
14
					fn pallet_prefix() -> &'static str {
97
14
						$name
98
14
					}
99
				}
100

            
101
				impl InstanceToPrefix for $instance {
102
					type ApprovesPrefix = Approves;
103
					type NoncesPrefix = Nonces;
104
				}
105
			}
106
		}
107
	};
108
}
109

            
110
// Since the macro expect a `ident` to be used with `paste!` we cannot provide `()` directly.
111
type Instance0 = ();
112

            
113
impl_prefix!(Instance0, "Erc20Instance0Balances");
114
impl_prefix!(Instance1, "Erc20Instance1Balances");
115
impl_prefix!(Instance2, "Erc20Instance2Balances");
116
impl_prefix!(Instance3, "Erc20Instance3Balances");
117
impl_prefix!(Instance4, "Erc20Instance4Balances");
118
impl_prefix!(Instance5, "Erc20Instance5Balances");
119
impl_prefix!(Instance6, "Erc20Instance6Balances");
120
impl_prefix!(Instance7, "Erc20Instance7Balances");
121
impl_prefix!(Instance8, "Erc20Instance8Balances");
122
impl_prefix!(Instance9, "Erc20Instance9Balances");
123
impl_prefix!(Instance10, "Erc20Instance10Balances");
124
impl_prefix!(Instance11, "Erc20Instance11Balances");
125
impl_prefix!(Instance12, "Erc20Instance12Balances");
126
impl_prefix!(Instance13, "Erc20Instance13Balances");
127
impl_prefix!(Instance14, "Erc20Instance14Balances");
128
impl_prefix!(Instance15, "Erc20Instance15Balances");
129
impl_prefix!(Instance16, "Erc20Instance16Balances");
130

            
131
/// Alias for the Balance type for the provided Runtime and Instance.
132
pub type BalanceOf<Runtime, Instance = ()> =
133
	<Runtime as pallet_balances::Config<Instance>>::Balance;
134

            
135
/// Storage type used to store approvals, since `pallet_balances` doesn't
136
/// handle this behavior.
137
/// (Owner => Allowed => Amount)
138
pub type ApprovesStorage<Runtime, Instance> = StorageDoubleMap<
139
	<Instance as InstanceToPrefix>::ApprovesPrefix,
140
	Blake2_128Concat,
141
	<Runtime as frame_system::Config>::AccountId,
142
	Blake2_128Concat,
143
	<Runtime as frame_system::Config>::AccountId,
144
	BalanceOf<Runtime, Instance>,
145
>;
146

            
147
/// Storage type used to store EIP2612 nonces.
148
pub type NoncesStorage<Instance> = StorageMap<
149
	<Instance as InstanceToPrefix>::NoncesPrefix,
150
	// Owner
151
	Blake2_128Concat,
152
	H160,
153
	// Nonce
154
	U256,
155
	ValueQuery,
156
>;
157

            
158
/// Metadata of an ERC20 token.
159
pub trait Erc20Metadata {
160
	/// Returns the name of the token.
161
	fn name() -> &'static str;
162

            
163
	/// Returns the symbol of the token.
164
	fn symbol() -> &'static str;
165

            
166
	/// Returns the decimals places of the token.
167
	fn decimals() -> u8;
168

            
169
	/// Must return `true` only if it represents the main native currency of
170
	/// the network. It must be the currency used in `pallet_evm`.
171
	fn is_native_currency() -> bool;
172
}
173

            
174
/// Precompile exposing a pallet_balance as an ERC20.
175
/// Multiple precompiles can support instances of pallet_balance.
176
/// The precompile uses an additional storage to store approvals.
177
pub struct Erc20BalancesPrecompile<Runtime, Metadata: Erc20Metadata, Instance: 'static = ()>(
178
	PhantomData<(Runtime, Metadata, Instance)>,
179
);
180

            
181
484
#[precompile_utils::precompile]
182
impl<Runtime, Metadata, Instance> Erc20BalancesPrecompile<Runtime, Metadata, Instance>
183
where
184
	Runtime: pallet_balances::Config<Instance> + pallet_evm::Config,
185
	Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
186
	Runtime::RuntimeCall: From<pallet_balances::Call<Runtime, Instance>>,
187
	<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
188
	BalanceOf<Runtime, Instance>: TryFrom<U256> + Into<U256>,
189
	Metadata: Erc20Metadata,
190
	Instance: InstanceToPrefix + 'static,
191
{
192
	#[precompile::public("totalSupply()")]
193
	#[precompile::view]
194
2
	fn total_supply(handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
195
2
		// TotalIssuance: Balance(16)
196
2
		handle.record_db_read::<Runtime>(16)?;
197

            
198
2
		Ok(pallet_balances::Pallet::<Runtime, Instance>::total_issuance().into())
199
2
	}
200

            
201
	#[precompile::public("balanceOf(address)")]
202
	#[precompile::view]
203
24
	fn balance_of(handle: &mut impl PrecompileHandle, owner: Address) -> EvmResult<U256> {
204
24
		// frame_system::Account:
205
24
		// Blake2128(16) + AccountId(20) + AccountInfo ((4 * 4) + AccountData(16 * 4))
206
24
		handle.record_db_read::<Runtime>(116)?;
207

            
208
24
		let owner: H160 = owner.into();
209
24
		let owner: Runtime::AccountId = Runtime::AddressMapping::into_account_id(owner);
210
24

            
211
24
		Ok(pallet_balances::Pallet::<Runtime, Instance>::usable_balance(&owner).into())
212
24
	}
213

            
214
	#[precompile::public("allowance(address,address)")]
215
	#[precompile::view]
216
8
	fn allowance(
217
8
		handle: &mut impl PrecompileHandle,
218
8
		owner: Address,
219
8
		spender: Address,
220
8
	) -> EvmResult<U256> {
221
8
		// frame_system::ApprovesStorage:
222
8
		// (2 * (Blake2128(16) + AccountId(20)) + Balanceof(16)
223
8
		handle.record_db_read::<Runtime>(88)?;
224

            
225
8
		let owner: H160 = owner.into();
226
8
		let spender: H160 = spender.into();
227
8

            
228
8
		let owner: Runtime::AccountId = Runtime::AddressMapping::into_account_id(owner);
229
8
		let spender: Runtime::AccountId = Runtime::AddressMapping::into_account_id(spender);
230
8

            
231
8
		Ok(ApprovesStorage::<Runtime, Instance>::get(owner, spender)
232
8
			.unwrap_or_default()
233
8
			.into())
234
8
	}
235

            
236
	#[precompile::public("approve(address,uint256)")]
237
5
	fn approve(
238
5
		handle: &mut impl PrecompileHandle,
239
5
		spender: Address,
240
5
		value: U256,
241
5
	) -> EvmResult<bool> {
242
5
		handle.record_cost(RuntimeHelper::<Runtime>::db_write_gas_cost())?;
243
5
		handle.record_log_costs_manual(3, 32)?;
244

            
245
5
		let spender: H160 = spender.into();
246
5

            
247
5
		// Write into storage.
248
5
		{
249
5
			let caller: Runtime::AccountId =
250
5
				Runtime::AddressMapping::into_account_id(handle.context().caller);
251
5
			let spender: Runtime::AccountId = Runtime::AddressMapping::into_account_id(spender);
252
5
			// Amount saturate if too high.
253
5
			let value = Self::u256_to_amount(value).unwrap_or_else(|_| Bounded::max_value());
254
5

            
255
5
			ApprovesStorage::<Runtime, Instance>::insert(caller, spender, value);
256
5
		}
257
5

            
258
5
		log3(
259
5
			handle.context().address,
260
5
			SELECTOR_LOG_APPROVAL,
261
5
			handle.context().caller,
262
5
			spender,
263
5
			solidity::encode_event_data(value),
264
5
		)
265
5
		.record(handle)?;
266

            
267
		// Build output.
268
5
		Ok(true)
269
5
	}
270

            
271
	#[precompile::public("transfer(address,uint256)")]
272
2
	fn transfer(handle: &mut impl PrecompileHandle, to: Address, value: U256) -> EvmResult<bool> {
273
2
		handle.record_log_costs_manual(3, 32)?;
274

            
275
2
		let to: H160 = to.into();
276
2

            
277
2
		// Build call with origin.
278
2
		{
279
2
			let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
280
2
			let to = Runtime::AddressMapping::into_account_id(to);
281
2
			let value = Self::u256_to_amount(value).in_field("value")?;
282

            
283
			// Dispatch call (if enough gas).
284
2
			RuntimeHelper::<Runtime>::try_dispatch(
285
2
				handle,
286
2
				Some(origin).into(),
287
2
				pallet_balances::Call::<Runtime, Instance>::transfer_allow_death {
288
2
					dest: Runtime::Lookup::unlookup(to),
289
2
					value: value,
290
2
				},
291
2
				SYSTEM_ACCOUNT_SIZE,
292
2
			)?;
293
		}
294

            
295
1
		log3(
296
1
			handle.context().address,
297
1
			SELECTOR_LOG_TRANSFER,
298
1
			handle.context().caller,
299
1
			to,
300
1
			solidity::encode_event_data(value),
301
1
		)
302
1
		.record(handle)?;
303

            
304
1
		Ok(true)
305
2
	}
306

            
307
	#[precompile::public("transferFrom(address,address,uint256)")]
308
3
	fn transfer_from(
309
3
		handle: &mut impl PrecompileHandle,
310
3
		from: Address,
311
3
		to: Address,
312
3
		value: U256,
313
3
	) -> EvmResult<bool> {
314
3
		// frame_system::ApprovesStorage:
315
3
		// (2 * (Blake2128(16) + AccountId(20)) + Balanceof(16)
316
3
		handle.record_db_read::<Runtime>(88)?;
317
3
		handle.record_cost(RuntimeHelper::<Runtime>::db_write_gas_cost())?;
318
3
		handle.record_log_costs_manual(3, 32)?;
319

            
320
3
		let from: H160 = from.into();
321
3
		let to: H160 = to.into();
322
3

            
323
3
		{
324
3
			let caller: Runtime::AccountId =
325
3
				Runtime::AddressMapping::into_account_id(handle.context().caller);
326
3
			let from: Runtime::AccountId = Runtime::AddressMapping::into_account_id(from);
327
3
			let to: Runtime::AccountId = Runtime::AddressMapping::into_account_id(to);
328
3
			let value = Self::u256_to_amount(value).in_field("value")?;
329

            
330
			// If caller is "from", it can spend as much as it wants.
331
3
			if caller != from {
332
2
				ApprovesStorage::<Runtime, Instance>::mutate(from.clone(), caller, |entry| {
333
					// Get current allowed value, exit if None.
334
2
					let allowed = entry.ok_or(revert("spender not allowed"))?;
335

            
336
					// Remove "value" from allowed, exit if underflow.
337
2
					let allowed = allowed
338
2
						.checked_sub(&value)
339
2
						.ok_or_else(|| revert("trying to spend more than allowed"))?;
340

            
341
					// Update allowed value.
342
1
					*entry = Some(allowed);
343
1

            
344
1
					EvmResult::Ok(())
345
2
				})?;
346
1
			}
347

            
348
			// Build call with origin. Here origin is the "from"/owner field.
349
			// Dispatch call (if enough gas).
350
2
			RuntimeHelper::<Runtime>::try_dispatch(
351
2
				handle,
352
2
				Some(from).into(),
353
2
				pallet_balances::Call::<Runtime, Instance>::transfer_allow_death {
354
2
					dest: Runtime::Lookup::unlookup(to),
355
2
					value: value,
356
2
				},
357
2
				SYSTEM_ACCOUNT_SIZE,
358
2
			)?;
359
		}
360

            
361
2
		log3(
362
2
			handle.context().address,
363
2
			SELECTOR_LOG_TRANSFER,
364
2
			from,
365
2
			to,
366
2
			solidity::encode_event_data(value),
367
2
		)
368
2
		.record(handle)?;
369

            
370
2
		Ok(true)
371
3
	}
372

            
373
	#[precompile::public("name()")]
374
	#[precompile::view]
375
2
	fn name(_handle: &mut impl PrecompileHandle) -> EvmResult<UnboundedBytes> {
376
2
		Ok(Metadata::name().into())
377
2
	}
378

            
379
	#[precompile::public("symbol()")]
380
	#[precompile::view]
381
2
	fn symbol(_handle: &mut impl PrecompileHandle) -> EvmResult<UnboundedBytes> {
382
2
		Ok(Metadata::symbol().into())
383
2
	}
384

            
385
	#[precompile::public("decimals()")]
386
	#[precompile::view]
387
2
	fn decimals(_handle: &mut impl PrecompileHandle) -> EvmResult<u8> {
388
2
		Ok(Metadata::decimals())
389
2
	}
390

            
391
	#[precompile::public("deposit()")]
392
	#[precompile::fallback]
393
	#[precompile::payable]
394
8
	fn deposit(handle: &mut impl PrecompileHandle) -> EvmResult {
395
8
		// Deposit only makes sense for the native currency.
396
8
		if !Metadata::is_native_currency() {
397
			return Err(RevertReason::UnknownSelector.into());
398
8
		}
399
8

            
400
8
		let caller: Runtime::AccountId =
401
8
			Runtime::AddressMapping::into_account_id(handle.context().caller);
402
8
		let precompile = Runtime::AddressMapping::into_account_id(handle.context().address);
403
8
		let amount = Self::u256_to_amount(handle.context().apparent_value)?;
404

            
405
8
		if amount.into() == U256::from(0u32) {
406
4
			return Err(revert("deposited amount must be non-zero"));
407
4
		}
408
4

            
409
4
		handle.record_log_costs_manual(2, 32)?;
410

            
411
		// Send back funds received by the precompile.
412
4
		RuntimeHelper::<Runtime>::try_dispatch(
413
4
			handle,
414
4
			Some(precompile).into(),
415
4
			pallet_balances::Call::<Runtime, Instance>::transfer_allow_death {
416
4
				dest: Runtime::Lookup::unlookup(caller),
417
4
				value: amount,
418
4
			},
419
4
			SYSTEM_ACCOUNT_SIZE,
420
4
		)?;
421

            
422
3
		log2(
423
3
			handle.context().address,
424
3
			SELECTOR_LOG_DEPOSIT,
425
3
			handle.context().caller,
426
3
			solidity::encode_event_data(handle.context().apparent_value),
427
3
		)
428
3
		.record(handle)?;
429

            
430
3
		Ok(())
431
8
	}
432

            
433
	#[precompile::public("withdraw(uint256)")]
434
2
	fn withdraw(handle: &mut impl PrecompileHandle, value: U256) -> EvmResult {
435
2
		// Withdraw only makes sense for the native currency.
436
2
		if !Metadata::is_native_currency() {
437
			return Err(RevertReason::UnknownSelector.into());
438
2
		}
439
2

            
440
2
		handle.record_log_costs_manual(2, 32)?;
441

            
442
2
		let account_amount: U256 = {
443
2
			let owner: Runtime::AccountId =
444
2
				Runtime::AddressMapping::into_account_id(handle.context().caller);
445
2
			pallet_balances::Pallet::<Runtime, Instance>::usable_balance(&owner).into()
446
2
		};
447
2

            
448
2
		if value > account_amount {
449
1
			return Err(revert("Trying to withdraw more than owned"));
450
1
		}
451
1

            
452
1
		log2(
453
1
			handle.context().address,
454
1
			SELECTOR_LOG_WITHDRAWAL,
455
1
			handle.context().caller,
456
1
			solidity::encode_event_data(value),
457
1
		)
458
1
		.record(handle)?;
459

            
460
1
		Ok(())
461
2
	}
462

            
463
	#[precompile::public("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)")]
464
5
	fn eip2612_permit(
465
5
		handle: &mut impl PrecompileHandle,
466
5
		owner: Address,
467
5
		spender: Address,
468
5
		value: U256,
469
5
		deadline: U256,
470
5
		v: u8,
471
5
		r: H256,
472
5
		s: H256,
473
5
	) -> EvmResult {
474
5
		<Eip2612<Runtime, Metadata, Instance>>::permit(
475
5
			handle, owner, spender, value, deadline, v, r, s,
476
5
		)
477
5
	}
478

            
479
	#[precompile::public("nonces(address)")]
480
	#[precompile::view]
481
8
	fn eip2612_nonces(handle: &mut impl PrecompileHandle, owner: Address) -> EvmResult<U256> {
482
8
		<Eip2612<Runtime, Metadata, Instance>>::nonces(handle, owner)
483
8
	}
484

            
485
	#[precompile::public("DOMAIN_SEPARATOR()")]
486
	#[precompile::view]
487
1
	fn eip2612_domain_separator(handle: &mut impl PrecompileHandle) -> EvmResult<H256> {
488
1
		<Eip2612<Runtime, Metadata, Instance>>::domain_separator(handle)
489
1
	}
490

            
491
20
	fn u256_to_amount(value: U256) -> MayRevert<BalanceOf<Runtime, Instance>> {
492
20
		value
493
20
			.try_into()
494
20
			.map_err(|_| RevertReason::value_is_too_large("balance type").into())
495
20
	}
496
}