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
//! # Encode Module
18
//!
19
//! ## Overview
20
//!
21
//! Module to provide `StakeEncodeCall`, `HrmpEncodeCall` and `UtilityEncodeCall` implementations
22
//! for the Xcm Transactor pallet
23

            
24
use frame_support::pallet_prelude::*;
25
use sp_runtime::traits::{AccountIdLookup, StaticLookup};
26
use sp_std::prelude::*;
27
use xcm_primitives::{
28
	AvailableStakeCalls, HrmpAvailableCalls, HrmpEncodeCall, StakeEncodeCall,
29
	UtilityAvailableCalls, UtilityEncodeCall,
30
};
31

            
32
pub use crate::pallet::*;
33

            
34
use crate::chain_indices::{AssetHubIndices, ChainIndices, RelayChainIndices};
35
pub use crate::weights::WeightInfo;
36

            
37
impl<T: Config> Pallet<T> {
38
	/// Get relay chain indices from ChainIndicesMap
39
	///
40
	/// This is used by HRMP and Utility encode functions which are relay-only operations.
41
	/// Searches ChainIndicesMap for the Relay variant, falling back to legacy storage.
42
65
	fn get_relay_indices() -> RelayChainIndices {
43
		// Search ChainIndicesMap for the Relay variant
44
		// We can't use a direct key lookup because the pallet doesn't know which
45
		// transactor value represents Relay (that's configured at runtime level)
46
65
		for (_transactor, chain_indices) in ChainIndicesMap::<T>::iter() {
47
9
			if let ChainIndices::Relay(indices) = chain_indices {
48
9
				return indices;
49
			}
50
		}
51

            
52
		// Fallback to old storage for backwards compatibility
53
		// This should only happen during migration or if storage is corrupted
54
56
		RelayIndices::<T>::get()
55
65
	}
56
}
57

            
58
impl<T: Config> UtilityEncodeCall for Pallet<T> {
59
43
	fn encode_call(self, call: UtilityAvailableCalls) -> Vec<u8> {
60
43
		let relay_indices = Self::get_relay_indices();
61
43
		match call {
62
43
			UtilityAvailableCalls::AsDerivative(a, b) => {
63
43
				let mut encoded_call: Vec<u8> = Vec::new();
64
43
				// pallet index
65
43
				encoded_call.push(relay_indices.utility);
66
43
				// call index
67
43
				encoded_call.push(relay_indices.as_derivative);
68
43
				// encoded argument
69
43
				encoded_call.append(&mut a.encode());
70
43
				encoded_call.append(&mut b.clone());
71
43
				encoded_call
72
43
			}
73
43
		}
74
43
	}
75
}
76

            
77
impl<T: Config> HrmpEncodeCall for Pallet<T> {
78
22
	fn hrmp_encode_call(call: HrmpAvailableCalls) -> Result<Vec<u8>, xcm::latest::Error> {
79
22
		let relay_indices = Self::get_relay_indices();
80
22
		match call {
81
9
			HrmpAvailableCalls::InitOpenChannel(a, b, c) => {
82
9
				let mut encoded_call: Vec<u8> = Vec::new();
83
9
				// pallet index
84
9
				encoded_call.push(relay_indices.hrmp);
85
9
				// call index
86
9
				encoded_call.push(relay_indices.init_open_channel);
87
9
				// encoded arguments
88
9
				encoded_call.append(&mut a.encode());
89
9
				encoded_call.append(&mut b.encode());
90
9
				encoded_call.append(&mut c.encode());
91
9
				Ok(encoded_call)
92
			}
93
6
			HrmpAvailableCalls::AcceptOpenChannel(a) => {
94
6
				let mut encoded_call: Vec<u8> = Vec::new();
95
6
				// pallet index
96
6
				encoded_call.push(relay_indices.hrmp);
97
6
				// call index
98
6
				encoded_call.push(relay_indices.accept_open_channel);
99
6
				// encoded argument
100
6
				encoded_call.append(&mut a.encode());
101
6
				Ok(encoded_call)
102
			}
103
5
			HrmpAvailableCalls::CloseChannel(a) => {
104
5
				let mut encoded_call: Vec<u8> = Vec::new();
105
5
				// pallet index
106
5
				encoded_call.push(relay_indices.hrmp);
107
5
				// call index
108
5
				encoded_call.push(relay_indices.close_channel);
109
5
				// encoded argument
110
5
				encoded_call.append(&mut a.encode());
111
5
				Ok(encoded_call)
112
			}
113
2
			HrmpAvailableCalls::CancelOpenRequest(a, b) => {
114
2
				let mut encoded_call: Vec<u8> = Vec::new();
115
2
				// pallet index
116
2
				encoded_call.push(relay_indices.hrmp);
117
2
				// call index
118
2
				encoded_call.push(relay_indices.cancel_open_request);
119
2
				// encoded argument
120
2
				encoded_call.append(&mut a.encode());
121
2
				encoded_call.append(&mut b.encode());
122
2
				Ok(encoded_call)
123
			}
124
		}
125
22
	}
126
}
127

            
128
16
fn encode_compact_arg<T: parity_scale_codec::HasCompact>(input: T) -> Vec<u8> {
129
	#[derive(Encode)]
130
	struct CompactWrapper<T: parity_scale_codec::HasCompact> {
131
		#[codec(compact)]
132
		input: T,
133
	}
134
16
	CompactWrapper { input }.encode()
135
16
}
136

            
137
impl<T: Config> StakeEncodeCall<T::Transactor> for Pallet<T> {
138
48
	fn encode_call(transactor: T::Transactor, call: AvailableStakeCalls) -> Vec<u8> {
139
		// Get the chain indices for the specified transactor
140
48
		let chain_indices = match ChainIndicesMap::<T>::get(&transactor) {
141
24
			Some(indices) => indices,
142
			None => {
143
				// Fallback to legacy RelayIndices if not found
144
24
				let relay_indices = RelayIndices::<T>::get();
145
24
				return Self::encode_relay_stake_call(&relay_indices, call);
146
			}
147
		};
148

            
149
24
		match chain_indices {
150
			ChainIndices::Relay(relay_indices) => {
151
				Self::encode_relay_stake_call(&relay_indices, call)
152
			}
153
24
			ChainIndices::AssetHub(assethub_indices) => {
154
24
				Self::encode_assethub_stake_call(&assethub_indices, call)
155
			}
156
		}
157
48
	}
158
}
159

            
160
// Legacy implementation kept for backwards compatibility
161
impl<T: Config> Pallet<T> {
162
	#[deprecated(note = "Use StakeEncodeCall::encode_call with transactor parameter instead")]
163
	pub fn encode_call_legacy(call: AvailableStakeCalls) -> Vec<u8> {
164
		let relay_indices = Self::get_relay_indices();
165
		match call {
166
			AvailableStakeCalls::Bond(b, c) => {
167
				let mut encoded_call: Vec<u8> = Vec::new();
168
				// pallet index
169
				encoded_call.push(relay_indices.staking);
170
				// call index
171
				encoded_call.push(relay_indices.bond);
172
				// encoded arguments
173
				encoded_call.append(&mut encode_compact_arg(b));
174
				encoded_call.append(&mut c.encode());
175
				encoded_call
176
			}
177

            
178
			AvailableStakeCalls::BondExtra(a) => {
179
				let mut encoded_call: Vec<u8> = Vec::new();
180
				// pallet index
181
				encoded_call.push(relay_indices.staking);
182
				// call index
183
				encoded_call.push(relay_indices.bond_extra);
184
				// encoded argument
185
				encoded_call.append(&mut encode_compact_arg(a));
186
				encoded_call
187
			}
188

            
189
			AvailableStakeCalls::Unbond(a) => {
190
				let mut encoded_call: Vec<u8> = Vec::new();
191
				// pallet index
192
				encoded_call.push(relay_indices.staking);
193
				// call index
194
				encoded_call.push(relay_indices.unbond);
195
				// encoded argument
196
				encoded_call.append(&mut encode_compact_arg(a));
197
				encoded_call
198
			}
199

            
200
			AvailableStakeCalls::WithdrawUnbonded(a) => {
201
				let mut encoded_call: Vec<u8> = Vec::new();
202
				// pallet index
203
				encoded_call.push(relay_indices.staking);
204
				// call index
205
				encoded_call.push(relay_indices.withdraw_unbonded);
206
				// encoded argument
207
				encoded_call.append(&mut a.encode());
208
				encoded_call
209
			}
210

            
211
			AvailableStakeCalls::Validate(a) => {
212
				let mut encoded_call: Vec<u8> = Vec::new();
213
				// pallet index
214
				encoded_call.push(relay_indices.staking);
215
				// call index
216
				encoded_call.push(relay_indices.validate);
217
				// encoded argument
218
				encoded_call.append(&mut a.encode());
219
				encoded_call
220
			}
221

            
222
			AvailableStakeCalls::Chill => {
223
				let mut encoded_call: Vec<u8> = Vec::new();
224
				// pallet index
225
				encoded_call.push(relay_indices.staking);
226
				// call index
227
				encoded_call.push(relay_indices.chill);
228
				encoded_call
229
			}
230

            
231
			AvailableStakeCalls::SetPayee(a) => {
232
				let mut encoded_call: Vec<u8> = Vec::new();
233
				// pallet index
234
				encoded_call.push(relay_indices.staking);
235
				// call index
236
				encoded_call.push(relay_indices.set_payee);
237
				// encoded argument
238
				encoded_call.append(&mut a.encode());
239
				encoded_call
240
			}
241

            
242
			AvailableStakeCalls::SetController => {
243
				let mut encoded_call: Vec<u8> = Vec::new();
244
				// pallet index
245
				encoded_call.push(relay_indices.staking);
246
				// call index
247
				encoded_call.push(relay_indices.set_controller);
248
				encoded_call
249
			}
250

            
251
			AvailableStakeCalls::Rebond(a) => {
252
				let mut encoded_call: Vec<u8> = Vec::new();
253
				// pallet index
254
				encoded_call.push(relay_indices.staking);
255
				// call index
256
				encoded_call.push(relay_indices.rebond);
257
				// encoded argument
258
				encoded_call.append(&mut encode_compact_arg(a));
259
				encoded_call
260
			}
261

            
262
			AvailableStakeCalls::Nominate(a) => {
263
				let mut encoded_call: Vec<u8> = Vec::new();
264
				// pallet index
265
				encoded_call.push(relay_indices.staking);
266
				// call index
267
				encoded_call.push(relay_indices.nominate);
268
				let nominated: Vec<
269
					<AccountIdLookup<sp_runtime::AccountId32, ()> as StaticLookup>::Source,
270
				> = a.iter().map(|add| (*add).clone().into()).collect();
271
				encoded_call.append(&mut nominated.encode());
272
				encoded_call
273
			}
274
		}
275
	}
276
}
277

            
278
impl<T: Config> Pallet<T> {
279
	/// Encode staking call for Relay Chain using specific indices
280
24
	fn encode_relay_stake_call(
281
24
		indices: &crate::chain_indices::RelayChainIndices,
282
24
		call: AvailableStakeCalls,
283
24
	) -> Vec<u8> {
284
24
		match call {
285
2
			AvailableStakeCalls::Bond(b, c) => {
286
2
				let mut encoded_call = Vec::new();
287
2
				encoded_call.push(indices.staking);
288
2
				encoded_call.push(indices.bond);
289
2
				encoded_call.append(&mut encode_compact_arg(b));
290
2
				encoded_call.append(&mut c.encode());
291
2
				encoded_call
292
			}
293
2
			AvailableStakeCalls::BondExtra(a) => {
294
2
				let mut encoded_call = Vec::new();
295
2
				encoded_call.push(indices.staking);
296
2
				encoded_call.push(indices.bond_extra);
297
2
				encoded_call.append(&mut encode_compact_arg(a));
298
2
				encoded_call
299
			}
300
2
			AvailableStakeCalls::Unbond(a) => {
301
2
				let mut encoded_call = Vec::new();
302
2
				encoded_call.push(indices.staking);
303
2
				encoded_call.push(indices.unbond);
304
2
				encoded_call.append(&mut encode_compact_arg(a));
305
2
				encoded_call
306
			}
307
2
			AvailableStakeCalls::WithdrawUnbonded(a) => {
308
2
				let mut encoded_call = Vec::new();
309
2
				encoded_call.push(indices.staking);
310
2
				encoded_call.push(indices.withdraw_unbonded);
311
2
				encoded_call.append(&mut a.encode());
312
2
				encoded_call
313
			}
314
2
			AvailableStakeCalls::Validate(a) => {
315
2
				let mut encoded_call = Vec::new();
316
2
				encoded_call.push(indices.staking);
317
2
				encoded_call.push(indices.validate);
318
2
				encoded_call.append(&mut a.encode());
319
2
				encoded_call
320
			}
321
			AvailableStakeCalls::Chill => {
322
4
				let mut encoded_call = Vec::new();
323
4
				encoded_call.push(indices.staking);
324
4
				encoded_call.push(indices.chill);
325
4
				encoded_call
326
			}
327
2
			AvailableStakeCalls::SetPayee(a) => {
328
2
				let mut encoded_call = Vec::new();
329
2
				encoded_call.push(indices.staking);
330
2
				encoded_call.push(indices.set_payee);
331
2
				encoded_call.append(&mut a.encode());
332
2
				encoded_call
333
			}
334
			AvailableStakeCalls::SetController => {
335
4
				let mut encoded_call = Vec::new();
336
4
				encoded_call.push(indices.staking);
337
4
				encoded_call.push(indices.set_controller);
338
4
				encoded_call
339
			}
340
2
			AvailableStakeCalls::Rebond(a) => {
341
2
				let mut encoded_call = Vec::new();
342
2
				encoded_call.push(indices.staking);
343
2
				encoded_call.push(indices.rebond);
344
2
				encoded_call.append(&mut encode_compact_arg(a));
345
2
				encoded_call
346
			}
347
2
			AvailableStakeCalls::Nominate(a) => {
348
2
				let mut encoded_call = Vec::new();
349
2
				encoded_call.push(indices.staking);
350
2
				encoded_call.push(indices.nominate);
351
2
				let nominated: Vec<
352
2
					<AccountIdLookup<sp_runtime::AccountId32, ()> as StaticLookup>::Source,
353
3
				> = a.iter().map(|add| (*add).clone().into()).collect();
354
2
				encoded_call.append(&mut nominated.encode());
355
2
				encoded_call
356
			}
357
		}
358
24
	}
359

            
360
	/// Encode staking call for AssetHub using specific indices
361
24
	fn encode_assethub_stake_call(indices: &AssetHubIndices, call: AvailableStakeCalls) -> Vec<u8> {
362
24
		match call {
363
2
			AvailableStakeCalls::Bond(b, c) => {
364
2
				let mut encoded_call = Vec::new();
365
2
				encoded_call.push(indices.staking);
366
2
				encoded_call.push(indices.bond);
367
2
				encoded_call.append(&mut encode_compact_arg(b));
368
2
				encoded_call.append(&mut c.encode());
369
2
				encoded_call
370
			}
371
2
			AvailableStakeCalls::BondExtra(a) => {
372
2
				let mut encoded_call = Vec::new();
373
2
				encoded_call.push(indices.staking);
374
2
				encoded_call.push(indices.bond_extra);
375
2
				encoded_call.append(&mut encode_compact_arg(a));
376
2
				encoded_call
377
			}
378
2
			AvailableStakeCalls::Unbond(a) => {
379
2
				let mut encoded_call = Vec::new();
380
2
				encoded_call.push(indices.staking);
381
2
				encoded_call.push(indices.unbond);
382
2
				encoded_call.append(&mut encode_compact_arg(a));
383
2
				encoded_call
384
			}
385
2
			AvailableStakeCalls::WithdrawUnbonded(a) => {
386
2
				let mut encoded_call = Vec::new();
387
2
				encoded_call.push(indices.staking);
388
2
				encoded_call.push(indices.withdraw_unbonded);
389
2
				encoded_call.append(&mut a.encode());
390
2
				encoded_call
391
			}
392
2
			AvailableStakeCalls::Validate(a) => {
393
2
				// Note: Validate may not be supported on AssetHub
394
2
				// This encodes it anyway, but it may fail on the destination chain
395
2
				let mut encoded_call = Vec::new();
396
2
				encoded_call.push(indices.staking);
397
2
				encoded_call.push(indices.validate);
398
2
				encoded_call.append(&mut a.encode());
399
2
				encoded_call
400
			}
401
			AvailableStakeCalls::Chill => {
402
4
				let mut encoded_call = Vec::new();
403
4
				encoded_call.push(indices.staking);
404
4
				encoded_call.push(indices.chill);
405
4
				encoded_call
406
			}
407
2
			AvailableStakeCalls::SetPayee(a) => {
408
2
				let mut encoded_call = Vec::new();
409
2
				encoded_call.push(indices.staking);
410
2
				encoded_call.push(indices.set_payee);
411
2
				encoded_call.append(&mut a.encode());
412
2
				encoded_call
413
			}
414
			AvailableStakeCalls::SetController => {
415
				// Note: SetController is deprecated in newer Substrate versions
416
4
				let mut encoded_call = Vec::new();
417
4
				encoded_call.push(indices.staking);
418
4
				encoded_call.push(indices.set_controller);
419
4
				encoded_call
420
			}
421
2
			AvailableStakeCalls::Rebond(a) => {
422
2
				let mut encoded_call = Vec::new();
423
2
				encoded_call.push(indices.staking);
424
2
				encoded_call.push(indices.rebond);
425
2
				encoded_call.append(&mut encode_compact_arg(a));
426
2
				encoded_call
427
			}
428
2
			AvailableStakeCalls::Nominate(a) => {
429
2
				let mut encoded_call = Vec::new();
430
2
				encoded_call.push(indices.staking);
431
2
				encoded_call.push(indices.nominate);
432
2
				let nominated: Vec<
433
2
					<AccountIdLookup<sp_runtime::AccountId32, ()> as StaticLookup>::Source,
434
4
				> = a.iter().map(|add| (*add).clone().into()).collect();
435
2
				encoded_call.append(&mut nominated.encode());
436
2
				encoded_call
437
			}
438
		}
439
24
	}
440
}