1
// Copyright 2019-2025 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
use frame_support::assert_ok;
18

            
19
use sp_std::boxed::Box;
20
use xcm::latest::prelude::{AccountKey20, Location, Parachain, WeightLimit};
21
use xcm::VersionedLocation;
22

            
23
use xcm_simulator::TestExt;
24

            
25
use crate::{xcm_mock::*, xcm_testing::helpers::*};
26

            
27
#[test]
28
1
fn evm_account_receiving_assets_should_handle_sufficients_ref_count() {
29
1
	MockNet::reset();
30
1

            
31
1
	let mut sufficient_account = [0u8; 20];
32
1
	sufficient_account[0..20].copy_from_slice(&evm_account()[..]);
33
1

            
34
1
	let evm_account_id = parachain::AccountId::from(sufficient_account);
35
1

            
36
1
	// Evm account is self sufficient
37
1
	ParaA::execute_with(|| {
38
1
		assert_eq!(parachain::System::account(evm_account_id).sufficients, 1);
39
1
	});
40
1

            
41
1
	register_relay_asset();
42
1

            
43
1
	// Actually send relay asset to parachain
44
1
	let dest: Location = AccountKey20 {
45
1
		network: None,
46
1
		key: sufficient_account,
47
1
	}
48
1
	.into();
49
1
	Relay::execute_with(|| {
50
1
		assert_ok!(RelayChainPalletXcm::limited_reserve_transfer_assets(
51
1
			relay_chain::RuntimeOrigin::signed(RELAYALICE),
52
1
			Box::new(Parachain(1).into()),
53
1
			Box::new(VersionedLocation::from(dest.clone()).clone().into()),
54
1
			Box::new(([], 123).into()),
55
1
			0,
56
1
			WeightLimit::Unlimited
57
1
		));
58
1
	});
59
1

            
60
1
	// Evm account sufficient ref count increased by 1.
61
1
	ParaA::execute_with(|| {
62
1
		// TODO: since the suicided logic was introduced the data of the smart contract is not
63
1
		// removed, it will have to be updated in a future release when there is the ability to
64
1
		// remove contract data
65
1
		// assert_eq!(parachain::System::account(evm_account_id).sufficients, 2);
66
1
	});
67
1

            
68
1
	ParaA::execute_with(|| {
69
1
		// Remove the account from the evm context.
70
1
		parachain::EVM::remove_account(&evm_account());
71
1
		// Evm account sufficient ref count decreased by 1.
72
1
		// TODO: since the suicided logic was introduced the data of the smart contract is not
73
1
		// removed, it will have to be updated in a future release when there is the ability to
74
1
		// remove contract data
75
1
		// assert_eq!(parachain::System::account(evm_account_id).sufficients, 1);
76
1
	});
77
1
}
78

            
79
#[test]
80
1
fn empty_account_should_not_be_reset() {
81
1
	MockNet::reset();
82
1

            
83
1
	// Test account has nonce 1 on genesis.
84
1
	let mut sufficient_account = [0u8; 20];
85
1
	sufficient_account[0..20].copy_from_slice(&evm_account()[..]);
86
1

            
87
1
	let evm_account_id = parachain::AccountId::from(sufficient_account);
88
1

            
89
1
	let source_id = register_relay_asset_non_sufficient();
90
1

            
91
1
	// Send native token to evm_account
92
1
	ParaA::execute_with(|| {
93
1
		assert_ok!(ParaBalances::transfer_allow_death(
94
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
95
1
			evm_account_id,
96
1
			100
97
1
		));
98
1
	});
99
1

            
100
1
	// Actually send relay asset to parachain
101
1
	let dest: Location = AccountKey20 {
102
1
		network: None,
103
1
		key: sufficient_account,
104
1
	}
105
1
	.into();
106
1
	Relay::execute_with(|| {
107
1
		assert_ok!(RelayChainPalletXcm::limited_reserve_transfer_assets(
108
1
			relay_chain::RuntimeOrigin::signed(RELAYALICE),
109
1
			Box::new(Parachain(1).into()),
110
1
			Box::new(VersionedLocation::from(dest.clone()).clone().into()),
111
1
			Box::new(([], 123).into()),
112
1
			0,
113
1
			WeightLimit::Unlimited
114
1
		));
115
1
	});
116
1

            
117
1
	ParaA::execute_with(|| {
118
1
		// Empty the assets from the account.
119
1
		// As this makes the account go below the `min_balance`, the account is considered dead
120
1
		// at eyes of pallet-assets, and the consumer reference is decreased by 1 and is now Zero.
121
1
		assert_ok!(parachain::Assets::transfer(
122
1
			parachain::RuntimeOrigin::signed(evm_account_id),
123
1
			source_id,
124
1
			PARAALICE.into(),
125
1
			123
126
1
		));
127
		// Verify account asset balance is Zero.
128
1
		assert_eq!(
129
1
			parachain::Assets::balance(source_id, &evm_account_id.into()),
130
1
			0
131
1
		);
132
		// Because we no longer have consumer references, we can set the balance to Zero.
133
		// This would reset the account if our ED were to be > than Zero.
134
1
		assert_ok!(ParaBalances::force_set_balance(
135
1
			parachain::RuntimeOrigin::root(),
136
1
			evm_account_id,
137
1
			0,
138
1
		));
139
		// Verify account native balance is Zero.
140
1
		assert_eq!(ParaBalances::free_balance(&evm_account_id), 0);
141
		// Remove the account from the evm context.
142
		// This decreases the sufficients reference by 1 and now is Zero.
143
1
		parachain::EVM::remove_account(&evm_account());
144
1
		// Verify reference count.
145
1
		let account = parachain::System::account(evm_account_id);
146
1
		assert_eq!(account.sufficients, 0);
147
1
		assert_eq!(account.consumers, 0);
148
1
		assert_eq!(account.providers, 1);
149
		// We expect the account to be alive in a Zero ED context.
150
1
		assert_eq!(parachain::System::account_nonce(evm_account_id), 1);
151
1
	});
152
1
}