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
//! Precompile to receive GMP callbacks and forward to XCM
18

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

            
21
extern crate alloc;
22

            
23
use fp_evm::PrecompileHandle;
24
use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo};
25
use frame_support::sp_runtime::traits::StaticLookup;
26
use frame_support::traits::Currency;
27
use pallet_evm::AddressMapping;
28
use pallet_identity::legacy::IdentityField;
29
use parity_scale_codec::MaxEncodedLen;
30
use precompile_utils::prelude::*;
31
use sp_core::{ConstU32, Get, H160, H256, U256};
32
use sp_runtime::traits::Dispatchable;
33
use sp_std::boxed::Box;
34
use sp_std::marker::PhantomData;
35
use sp_std::vec::Vec;
36

            
37
#[cfg(test)]
38
mod mock;
39
#[cfg(test)]
40
mod tests;
41

            
42
type BalanceOf<T> = <<T as pallet_identity::Config>::Currency as Currency<
43
	<T as frame_system::Config>::AccountId,
44
>>::Balance;
45

            
46
type IdentityFieldOf<T> = <<T as pallet_identity::Config>::IdentityInformation
47
	as pallet_identity::IdentityInformationProvider>::FieldsIdentifier;
48

            
49
/// Solidity selector of the Vote log, which is the Keccak of the Log signature.
50
pub(crate) const SELECTOR_LOG_IDENTITY_SET: [u8; 32] = keccak256!("IdentitySet(address)");
51
pub(crate) const SELECTOR_LOG_IDENTITY_CLEARED: [u8; 32] = keccak256!("IdentityCleared(address)");
52
pub(crate) const SELECTOR_LOG_JUDGEMENT_REQUESTED: [u8; 32] =
53
	keccak256!("JudgementRequested(address,uint32)");
54
pub(crate) const SELECTOR_LOG_JUDGEMENT_UNREQUESTED: [u8; 32] =
55
	keccak256!("JudgementUnrequested(address,uint32)");
56
pub(crate) const SELECTOR_LOG_JUDGEMENT_GIVEN: [u8; 32] =
57
	keccak256!("JudgementGiven(address,uint32)");
58
pub(crate) const SELECTOR_LOG_SUB_IDENTITY_ADDED: [u8; 32] =
59
	keccak256!("SubIdentityAdded(address,address)");
60
pub(crate) const SELECTOR_LOG_SUB_IDENTITY_REMOVED: [u8; 32] =
61
	keccak256!("SubIdentityRemoved(address,address)");
62
pub(crate) const SELECTOR_LOG_SUB_IDENTITY_REVOKED: [u8; 32] =
63
	keccak256!("SubIdentityRevoked(address)");
64

            
65
/// A precompile to wrap the functionality from pallet-identity
66
pub struct IdentityPrecompile<Runtime, MaxAdditionalFields>(
67
	PhantomData<(Runtime, MaxAdditionalFields)>,
68
);
69

            
70
287
#[precompile_utils::precompile]
71
#[precompile::test_concrete_types(mock::Runtime, mock::MaxAdditionalFields)]
72
impl<Runtime, MaxAdditionalFields> IdentityPrecompile<Runtime, MaxAdditionalFields>
73
where
74
	MaxAdditionalFields: Get<u32> + 'static,
75
	Runtime: pallet_evm::Config
76
		+ pallet_identity::Config<
77
			IdentityInformation = pallet_identity::legacy::IdentityInfo<MaxAdditionalFields>,
78
		>,
79
	Runtime::AccountId: Into<H160>,
80
	Runtime::Hash: From<H256>,
81
	Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
82
	<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
83
	Runtime::RuntimeCall: From<pallet_identity::Call<Runtime>>,
84
	BalanceOf<Runtime>: TryFrom<U256> + Into<U256> + solidity::Codec,
85
	<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
86
{
87
	// Note: addRegistrar(address) & killIdentity(address) are not supported since they use a
88
	// force origin.
89

            
90
	#[precompile::public("setIdentity((((bool,bytes),(bool,bytes))[],(bool,bytes),(bool,bytes),(bool,bytes),(bool,bytes),(bool,bytes),bool,bytes,(bool,bytes),(bool,bytes)))")]
91
12
	fn set_identity(
92
12
		handle: &mut impl PrecompileHandle,
93
12
		info: IdentityInfo<MaxAdditionalFields>,
94
12
	) -> EvmResult {
95
12
		let caller = handle.context().caller;
96
12

            
97
12
		let event = log1(
98
12
			handle.context().address,
99
12
			SELECTOR_LOG_IDENTITY_SET,
100
12
			solidity::encode_event_data(Address(caller)),
101
12
		);
102
12
		handle.record_log_costs(&[&event])?;
103

            
104
12
		let info: Box<Runtime::IdentityInformation> = Self::identity_to_input(info)?;
105

            
106
12
		let call = pallet_identity::Call::<Runtime>::set_identity { info };
107
12

            
108
12
		let origin = Runtime::AddressMapping::into_account_id(caller);
109
12
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
110

            
111
12
		event.record(handle)?;
112

            
113
12
		Ok(())
114
12
	}
115

            
116
	#[precompile::public("setSubs((address,(bool,bytes))[])")]
117
2
	fn set_subs(
118
2
		handle: &mut impl PrecompileHandle,
119
2
		subs: BoundedVec<(Address, Data), Runtime::MaxSubAccounts>,
120
2
	) -> EvmResult {
121
2
		let subs: Vec<_> = subs.into();
122
2
		let mut call_subs = Vec::with_capacity(subs.len());
123
4
		for (i, (addr, data)) in subs.into_iter().enumerate() {
124
4
			let addr = Runtime::AddressMapping::into_account_id(addr.into());
125
4
			let data: pallet_identity::Data = data
126
4
				.try_into()
127
4
				.map_err(|e| RevertReason::custom(e).in_field(alloc::format!("index {i}")))?;
128
4
			call_subs.push((addr, data));
129
		}
130
2
		let call = pallet_identity::Call::<Runtime>::set_subs { subs: call_subs };
131
2

            
132
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
133
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
134

            
135
1
		Ok(())
136
2
	}
137

            
138
	#[precompile::public("clearIdentity()")]
139
2
	fn clear_identity(handle: &mut impl PrecompileHandle) -> EvmResult {
140
2
		let caller = handle.context().caller;
141
2

            
142
2
		let event = log1(
143
2
			handle.context().address,
144
2
			SELECTOR_LOG_IDENTITY_CLEARED,
145
2
			solidity::encode_event_data(Address(caller)),
146
2
		);
147
2
		handle.record_log_costs(&[&event])?;
148

            
149
2
		let call = pallet_identity::Call::<Runtime>::clear_identity {};
150
2

            
151
2
		let origin = Runtime::AddressMapping::into_account_id(caller);
152
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
153

            
154
1
		event.record(handle)?;
155

            
156
1
		Ok(())
157
2
	}
158

            
159
	#[precompile::public("requestJudgement(uint32,uint256)")]
160
3
	fn request_judgement(
161
3
		handle: &mut impl PrecompileHandle,
162
3
		reg_index: u32,
163
3
		max_fee: U256,
164
3
	) -> EvmResult {
165
3
		let caller = handle.context().caller;
166
3

            
167
3
		let event = log1(
168
3
			handle.context().address,
169
3
			SELECTOR_LOG_JUDGEMENT_REQUESTED,
170
3
			solidity::encode_event_data((Address(caller), reg_index)),
171
3
		);
172
3
		handle.record_log_costs(&[&event])?;
173

            
174
3
		let max_fee = max_fee
175
3
			.try_into()
176
3
			.map_err(|_| RevertReason::value_is_too_large("max_fee"))?;
177
3
		let call = pallet_identity::Call::<Runtime>::request_judgement { reg_index, max_fee };
178
3

            
179
3
		let origin = Runtime::AddressMapping::into_account_id(caller);
180
3
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
181

            
182
3
		event.record(handle)?;
183

            
184
3
		Ok(())
185
3
	}
186

            
187
	#[precompile::public("cancelRequest(uint32)")]
188
1
	fn cancel_request(handle: &mut impl PrecompileHandle, reg_index: u32) -> EvmResult {
189
1
		let caller = handle.context().caller;
190
1

            
191
1
		let event = log1(
192
1
			handle.context().address,
193
1
			SELECTOR_LOG_JUDGEMENT_UNREQUESTED,
194
1
			solidity::encode_event_data((Address(caller), reg_index)),
195
1
		);
196
1
		handle.record_log_costs(&[&event])?;
197

            
198
1
		let call = pallet_identity::Call::<Runtime>::cancel_request { reg_index };
199
1

            
200
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
201
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
202

            
203
1
		event.record(handle)?;
204

            
205
1
		Ok(())
206
1
	}
207

            
208
	#[precompile::public("setFee(uint32,uint256)")]
209
5
	fn set_fee(handle: &mut impl PrecompileHandle, index: u32, fee: U256) -> EvmResult {
210
5
		let fee = fee
211
5
			.try_into()
212
5
			.map_err(|_| RevertReason::value_is_too_large("fee"))?;
213
5
		let call = pallet_identity::Call::<Runtime>::set_fee { index, fee };
214
5

            
215
5
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
216
5
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
217

            
218
4
		Ok(())
219
5
	}
220

            
221
	#[precompile::public("setAccountId(uint32,address)")]
222
2
	fn set_account_id(handle: &mut impl PrecompileHandle, index: u32, new: Address) -> EvmResult {
223
2
		let new = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(new.0));
224
2
		let call = pallet_identity::Call::<Runtime>::set_account_id { index, new };
225
2

            
226
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
227
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
228

            
229
1
		Ok(())
230
2
	}
231

            
232
	#[precompile::public("setFields(uint32,(bool,bool,bool,bool,bool,bool,bool,bool))")]
233
2
	fn set_fields(
234
2
		handle: &mut impl PrecompileHandle,
235
2
		index: u32,
236
2
		fields: IdentityFields,
237
2
	) -> EvmResult {
238
2
		let fields = Self::identity_fields_to_input(fields);
239
2
		let call = pallet_identity::Call::<Runtime>::set_fields { index, fields };
240
2

            
241
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
242
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
243

            
244
1
		Ok(())
245
2
	}
246

            
247
	#[precompile::public(
248
		"provideJudgement(uint32,address,(bool,bool,uint256,bool,bool,bool,bool,bool),bytes32)"
249
	)]
250
1
	fn provide_judgement(
251
1
		handle: &mut impl PrecompileHandle,
252
1
		reg_index: u32,
253
1
		target: Address,
254
1
		judgement: Judgement,
255
1
		identity: H256,
256
1
	) -> EvmResult {
257
1
		let caller = handle.context().caller;
258
1

            
259
1
		let event = log1(
260
1
			handle.context().address,
261
1
			SELECTOR_LOG_JUDGEMENT_GIVEN,
262
1
			solidity::encode_event_data((target, reg_index)),
263
1
		);
264
1
		handle.record_log_costs(&[&event])?;
265

            
266
1
		let target = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(target.0));
267
1
		let judgement = Self::judgment_to_input(judgement)?;
268
1
		let identity: Runtime::Hash = identity.into();
269
1
		let call = pallet_identity::Call::<Runtime>::provide_judgement {
270
1
			reg_index,
271
1
			target,
272
1
			judgement,
273
1
			identity,
274
1
		};
275
1

            
276
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
277
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
278

            
279
1
		event.record(handle)?;
280

            
281
1
		Ok(())
282
1
	}
283

            
284
	#[precompile::public("addSub(address,(bool,bytes))")]
285
4
	fn add_sub(handle: &mut impl PrecompileHandle, sub: Address, data: Data) -> EvmResult {
286
4
		let caller = handle.context().caller;
287
4

            
288
4
		let event = log1(
289
4
			handle.context().address,
290
4
			SELECTOR_LOG_SUB_IDENTITY_ADDED,
291
4
			solidity::encode_event_data((sub, Address(caller))),
292
4
		);
293
4
		handle.record_log_costs(&[&event])?;
294

            
295
4
		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
296
4
		let data: pallet_identity::Data = data
297
4
			.try_into()
298
4
			.map_err(|e| RevertReason::custom(e).in_field("data"))?;
299
4
		let call = pallet_identity::Call::<Runtime>::add_sub { sub, data };
300
4

            
301
4
		let origin = Runtime::AddressMapping::into_account_id(caller);
302
4
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
303

            
304
4
		event.record(handle)?;
305

            
306
4
		Ok(())
307
4
	}
308

            
309
	#[precompile::public("renameSub(address,(bool,bytes))")]
310
1
	fn rename_sub(handle: &mut impl PrecompileHandle, sub: Address, data: Data) -> EvmResult {
311
1
		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
312
1
		let data: pallet_identity::Data = data
313
1
			.try_into()
314
1
			.map_err(|e| RevertReason::custom(e).in_field("data"))?;
315
1
		let call = pallet_identity::Call::<Runtime>::rename_sub { sub, data };
316
1

            
317
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
318
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
319

            
320
1
		Ok(())
321
1
	}
322

            
323
	#[precompile::public("removeSub(address)")]
324
1
	fn remove_sub(handle: &mut impl PrecompileHandle, sub: Address) -> EvmResult {
325
1
		let caller = handle.context().caller;
326
1

            
327
1
		let event = log1(
328
1
			handle.context().address,
329
1
			SELECTOR_LOG_SUB_IDENTITY_REMOVED,
330
1
			solidity::encode_event_data((sub, Address(caller))),
331
1
		);
332
1
		handle.record_log_costs(&[&event])?;
333

            
334
1
		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
335
1
		let call = pallet_identity::Call::<Runtime>::remove_sub { sub };
336
1

            
337
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
338
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
339

            
340
1
		event.record(handle)?;
341

            
342
1
		Ok(())
343
1
	}
344

            
345
	#[precompile::public("quitSub()")]
346
1
	fn quit_sub(handle: &mut impl PrecompileHandle) -> EvmResult {
347
1
		let caller = handle.context().caller;
348
1

            
349
1
		let event = log1(
350
1
			handle.context().address,
351
1
			SELECTOR_LOG_SUB_IDENTITY_REVOKED,
352
1
			solidity::encode_event_data(Address(caller)),
353
1
		);
354
1
		handle.record_log_costs(&[&event])?;
355

            
356
1
		let call = pallet_identity::Call::<Runtime>::quit_sub {};
357
1

            
358
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
359
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
360

            
361
1
		event.record(handle)?;
362

            
363
1
		Ok(())
364
1
	}
365

            
366
	#[precompile::public("identity(address)")]
367
	#[precompile::view]
368
9
	fn identity(
369
9
		handle: &mut impl PrecompileHandle,
370
9
		who: Address,
371
9
	) -> EvmResult<Registration<MaxAdditionalFields>> {
372
9
		// Storage item: IdentityOf ->
373
9
		//		Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>
374
9
		handle.record_db_read::<Runtime>(pallet_identity::Registration::<
375
9
			BalanceOf<Runtime>,
376
9
			Runtime::MaxRegistrars,
377
9
			Runtime::IdentityInformation,
378
9
		>::max_encoded_len())?;
379

            
380
9
		let who: H160 = who.into();
381
9
		let who = Runtime::AddressMapping::into_account_id(who);
382
9
		let identity = pallet_identity::IdentityOf::<Runtime>::get(who);
383
9

            
384
9
		Ok(Self::identity_to_output(identity)?)
385
9
	}
386

            
387
	#[precompile::public("superOf(address)")]
388
	#[precompile::view]
389
2
	fn super_of(handle: &mut impl PrecompileHandle, who: Address) -> EvmResult<SuperOf> {
390
2
		// Storage item: SuperOf -> (T::AccountId, Data)
391
2
		handle.record_db_read::<Runtime>(
392
2
			Runtime::AccountId::max_encoded_len()
393
2
				.saturating_add(pallet_identity::Data::max_encoded_len()),
394
2
		)?;
395

            
396
2
		let who: H160 = who.into();
397
2
		let who = Runtime::AddressMapping::into_account_id(who);
398
2
		if let Some((account, data)) = pallet_identity::SuperOf::<Runtime>::get(who) {
399
1
			Ok(SuperOf {
400
1
				is_valid: true,
401
1
				account: Address(account.into()),
402
1
				data: Self::data_to_output(data),
403
1
			})
404
		} else {
405
1
			Ok(SuperOf::default())
406
		}
407
2
	}
408

            
409
	#[precompile::public("subsOf(address)")]
410
	#[precompile::view]
411
2
	fn subs_of(handle: &mut impl PrecompileHandle, who: Address) -> EvmResult<SubsOf> {
412
2
		// Storage item: SubsOf -> (BalanceOf<T>, BoundedVec<T::AccountId, T::MaxSubAccounts>)
413
2
		handle.record_db_read::<Runtime>(
414
2
			BalanceOf::<Runtime>::max_encoded_len().saturating_add(
415
2
				Runtime::AccountId::max_encoded_len()
416
2
					.saturating_mul(Runtime::MaxSubAccounts::get() as usize),
417
2
			),
418
2
		)?;
419

            
420
2
		let who: H160 = who.into();
421
2
		let who = Runtime::AddressMapping::into_account_id(who);
422
2
		let (deposit, accounts) = pallet_identity::SubsOf::<Runtime>::get(who);
423
2

            
424
2
		let accounts = accounts
425
2
			.into_iter()
426
2
			.map(|account| Address(account.into()))
427
2
			.collect();
428
2

            
429
2
		Ok(SubsOf {
430
2
			deposit: deposit.into(),
431
2
			accounts,
432
2
		})
433
2
	}
434

            
435
	#[precompile::public("registrars()")]
436
	#[precompile::view]
437
2
	fn registrars(handle: &mut impl PrecompileHandle) -> EvmResult<Vec<Registrar>> {
438
2
		// Storage item: Registrars ->
439
2
		// 		BoundedVec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>, T::MaxRegistrars>
440
2
		handle.record_db_read::<Runtime>(
441
2
			pallet_identity::RegistrarInfo::<
442
2
				BalanceOf<Runtime>,
443
2
				Runtime::AccountId,
444
2
				IdentityFieldOf<Runtime>,
445
2
			>::max_encoded_len()
446
2
			.saturating_mul(Runtime::MaxRegistrars::get() as usize),
447
2
		)?;
448

            
449
2
		let registrars = pallet_identity::Registrars::<Runtime>::get()
450
2
			.into_iter()
451
2
			.enumerate()
452
2
			.map(|(index, maybe_reg)| {
453
1
				if let Some(reg) = maybe_reg {
454
1
					let fields: u64 = reg.fields.into();
455
1
					Registrar {
456
1
						is_valid: true,
457
1
						index: index as u32,
458
1
						account: Address(reg.account.into()),
459
1
						fee: reg.fee.into(),
460
1
						fields: IdentityFields {
461
1
							display: fields & (IdentityField::Display as u64)
462
1
								== (IdentityField::Display as u64),
463
1
							legal: fields & (IdentityField::Legal as u64)
464
1
								== (IdentityField::Legal as u64),
465
1
							web: fields & (IdentityField::Web as u64)
466
1
								== (IdentityField::Web as u64),
467
1
							riot: fields & (IdentityField::Riot as u64)
468
1
								== (IdentityField::Riot as u64),
469
1
							email: fields & (IdentityField::Email as u64)
470
1
								== (IdentityField::Email as u64),
471
1
							pgp_fingerprint: fields & (IdentityField::PgpFingerprint as u64)
472
1
								== (IdentityField::PgpFingerprint as u64),
473
1
							image: fields & (IdentityField::Image as u64)
474
1
								== (IdentityField::Image as u64),
475
1
							twitter: fields & (IdentityField::Twitter as u64)
476
1
								== (IdentityField::Twitter as u64),
477
1
						},
478
1
					}
479
				} else {
480
					Registrar {
481
						is_valid: false,
482
						index: index as u32,
483
						..Default::default()
484
					}
485
				}
486
2
			})
487
2
			.collect();
488
2

            
489
2
		Ok(registrars)
490
2
	}
491

            
492
2
	fn identity_fields_to_input(fields: IdentityFields) -> IdentityFieldOf<Runtime> {
493
2
		let mut field_bits = 0u64;
494
2
		if fields.display {
495
2
			field_bits = field_bits | IdentityField::Display as u64;
496
2
		}
497
2
		if fields.legal {
498
			field_bits = field_bits | IdentityField::Legal as u64;
499
2
		}
500
2
		if fields.web {
501
2
			field_bits = field_bits | IdentityField::Web as u64;
502
2
		}
503
2
		if fields.riot {
504
			field_bits = field_bits | IdentityField::Riot as u64;
505
2
		}
506
2
		if fields.email {
507
			field_bits = field_bits | IdentityField::Email as u64;
508
2
		}
509
2
		if fields.pgp_fingerprint {
510
			field_bits = field_bits | IdentityField::PgpFingerprint as u64;
511
2
		}
512
2
		if fields.image {
513
			field_bits = field_bits | IdentityField::Image as u64;
514
2
		}
515
2
		if fields.twitter {
516
			field_bits = field_bits | IdentityField::Twitter as u64;
517
2
		}
518

            
519
2
		IdentityFieldOf::<Runtime>::from(field_bits)
520
2
	}
521

            
522
12
	fn identity_to_input(
523
12
		info: IdentityInfo<MaxAdditionalFields>,
524
12
	) -> MayRevert<Box<pallet_identity::legacy::IdentityInfo<MaxAdditionalFields>>> {
525
12
		// let additional: Vec<(pallet_identity::Data, pallet_identity::Data)> = info.additional.into();
526
12
		let mut additional: sp_runtime::BoundedVec<
527
12
			(pallet_identity::Data, pallet_identity::Data),
528
12
			MaxAdditionalFields,
529
12
		> = Default::default();
530
12
		let iter: Vec<_> = info.additional.into();
531
12
		for (i, (k, v)) in iter.into_iter().enumerate() {
532
2
			let k: pallet_identity::Data = k.try_into().map_err(|e| {
533
				RevertReason::custom(e).in_field(alloc::format!("additional.{i}.key"))
534
2
			})?;
535
2
			let v: pallet_identity::Data = v.try_into().map_err(|e| {
536
				RevertReason::custom(e).in_field(alloc::format!("additional.{i}.value"))
537
2
			})?;
538
2
			additional
539
2
				.try_push((k, v))
540
2
				.map_err(|_| RevertReason::custom("out of bounds").in_field("additional"))?;
541
		}
542

            
543
12
		let pgp_fingerprint: Option<[u8; 20]> = if info.has_pgp_fingerprint {
544
2
			let fingerprint: Vec<_> = info.pgp_fingerprint.into();
545
2
			let fingerprint = fingerprint
546
2
				.try_into()
547
2
				.map_err(|_| RevertReason::custom("pgp_fingerprint must be 20 bytes long"))?;
548
2
			Some(fingerprint)
549
		} else {
550
10
			None
551
		};
552
12
		let identity_info = pallet_identity::legacy::IdentityInfo::<MaxAdditionalFields> {
553
12
			additional,
554
12
			display: info
555
12
				.display
556
12
				.try_into()
557
12
				.map_err(|e| RevertReason::custom(e).in_field("display"))?,
558
12
			legal: info
559
12
				.legal
560
12
				.try_into()
561
12
				.map_err(|e| RevertReason::custom(e).in_field("legal"))?,
562
12
			web: info
563
12
				.web
564
12
				.try_into()
565
12
				.map_err(|e| RevertReason::custom(e).in_field("web"))?,
566
12
			riot: info
567
12
				.riot
568
12
				.try_into()
569
12
				.map_err(|e| RevertReason::custom(e).in_field("riot"))?,
570
12
			email: info
571
12
				.email
572
12
				.try_into()
573
12
				.map_err(|e| RevertReason::custom(e).in_field("email"))?,
574
12
			pgp_fingerprint,
575
12
			image: info
576
12
				.image
577
12
				.try_into()
578
12
				.map_err(|e| RevertReason::custom(e).in_field("image"))?,
579
12
			twitter: info
580
12
				.twitter
581
12
				.try_into()
582
12
				.map_err(|e| RevertReason::custom(e).in_field("twitter"))?,
583
		};
584

            
585
12
		Ok(Box::new(identity_info))
586
12
	}
587

            
588
9
	fn identity_to_output(
589
9
		registration: Option<
590
9
			pallet_identity::Registration<
591
9
				BalanceOf<Runtime>,
592
9
				Runtime::MaxRegistrars,
593
9
				Runtime::IdentityInformation,
594
9
			>,
595
9
		>,
596
9
	) -> MayRevert<Registration<MaxAdditionalFields>> {
597
9
		let Some(registration) = registration else {
598
1
			return Ok(Registration::<MaxAdditionalFields>::default());
599
		};
600

            
601
8
		let mut identity_info = IdentityInfo::<MaxAdditionalFields> {
602
8
			additional: Default::default(),
603
8
			display: Self::data_to_output(registration.info.display),
604
8
			legal: Self::data_to_output(registration.info.legal),
605
8
			web: Self::data_to_output(registration.info.web),
606
8
			riot: Self::data_to_output(registration.info.riot),
607
8
			email: Self::data_to_output(registration.info.email),
608
8
			has_pgp_fingerprint: false,
609
8
			pgp_fingerprint: Default::default(),
610
8
			image: Self::data_to_output(registration.info.image),
611
8
			twitter: Self::data_to_output(registration.info.twitter),
612
8
		};
613
8

            
614
8
		let mut additional = Vec::new();
615
8
		for (k, v) in registration.info.additional.into_iter() {
616
2
			let k: Data = Self::data_to_output(k);
617
2
			let v: Data = Self::data_to_output(v);
618
2
			additional.push((k, v));
619
2
		}
620

            
621
8
		if let Some(pgp_fingerprint) = registration.info.pgp_fingerprint {
622
1
			identity_info.has_pgp_fingerprint = true;
623
1
			identity_info.pgp_fingerprint = pgp_fingerprint.into();
624
7
		}
625

            
626
8
		identity_info.additional = additional.into();
627
8

            
628
8
		let mut judgements = Vec::new();
629
8
		for (index, judgement) in registration.judgements.into_iter() {
630
7
			judgements.push((index, Self::judgement_to_output(judgement)));
631
7
		}
632

            
633
8
		let reg = Registration::<MaxAdditionalFields> {
634
8
			is_valid: true,
635
8
			judgements: judgements.into(),
636
8
			deposit: registration.deposit.into(),
637
8
			info: identity_info,
638
8
		};
639
8

            
640
8
		Ok(reg)
641
9
	}
642

            
643
7
	fn judgement_to_output(value: pallet_identity::Judgement<BalanceOf<Runtime>>) -> Judgement {
644
7
		let mut judgement = Judgement::default();
645
7

            
646
7
		match value {
647
1
			pallet_identity::Judgement::Unknown => {
648
1
				judgement.is_unknown = true;
649
1
			}
650
1
			pallet_identity::Judgement::FeePaid(balance) => {
651
1
				judgement.is_fee_paid = true;
652
1
				judgement.fee_paid_deposit = balance.into();
653
1
			}
654
1
			pallet_identity::Judgement::Reasonable => {
655
1
				judgement.is_reasonable = true;
656
1
			}
657
1
			pallet_identity::Judgement::KnownGood => {
658
1
				judgement.is_known_good = true;
659
1
			}
660
1
			pallet_identity::Judgement::OutOfDate => {
661
1
				judgement.is_out_of_date = true;
662
1
			}
663
1
			pallet_identity::Judgement::LowQuality => {
664
1
				judgement.is_low_quality = true;
665
1
			}
666
1
			pallet_identity::Judgement::Erroneous => {
667
1
				judgement.is_erroneous = true;
668
1
			}
669
		};
670

            
671
7
		judgement
672
7
	}
673

            
674
1
	fn judgment_to_input(
675
1
		value: Judgement,
676
1
	) -> Result<pallet_identity::Judgement<BalanceOf<Runtime>>, RevertReason> {
677
1
		if value.is_unknown {
678
			return Ok(pallet_identity::Judgement::Unknown);
679
1
		}
680
1

            
681
1
		if value.is_fee_paid {
682
			let amount: BalanceOf<Runtime> = value
683
				.fee_paid_deposit
684
				.try_into()
685
				.map_err(|_| RevertReason::value_is_too_large("fee_paid_deposit").into())?;
686

            
687
			return Ok(pallet_identity::Judgement::FeePaid(amount));
688
1
		}
689
1

            
690
1
		if value.is_reasonable {
691
1
			return Ok(pallet_identity::Judgement::Reasonable);
692
		}
693

            
694
		if value.is_known_good {
695
			return Ok(pallet_identity::Judgement::KnownGood);
696
		}
697

            
698
		if value.is_out_of_date {
699
			return Ok(pallet_identity::Judgement::OutOfDate);
700
		}
701

            
702
		if value.is_low_quality {
703
			return Ok(pallet_identity::Judgement::LowQuality);
704
		}
705

            
706
		if value.is_erroneous {
707
			return Ok(pallet_identity::Judgement::Erroneous);
708
		}
709

            
710
		return Err(RevertReason::custom("invalid"));
711
1
	}
712

            
713
61
	fn data_to_output(data: pallet_identity::Data) -> Data {
714
61
		let mut output = Data::default();
715
61
		match data {
716
42
			pallet_identity::Data::None => (),
717
19
			pallet_identity::Data::Raw(bytes) => {
718
19
				let bytes: Vec<_> = bytes.into();
719
19
				output.has_data = true;
720
19
				output.value = bytes.into();
721
19
			}
722
			pallet_identity::Data::BlakeTwo256(bytes) => {
723
				output.has_data = true;
724
				output.value = bytes.into();
725
			}
726
			pallet_identity::Data::Sha256(bytes) => {
727
				output.has_data = true;
728
				output.value = bytes.into();
729
			}
730
			pallet_identity::Data::Keccak256(bytes) => {
731
				output.has_data = true;
732
				output.value = bytes.into();
733
			}
734
			pallet_identity::Data::ShaThree256(bytes) => {
735
				output.has_data = true;
736
				output.value = bytes.into();
737
			}
738
		}
739

            
740
61
		output
741
61
	}
742
}
743

            
744
695
#[derive(Default, Debug, Eq, PartialEq, solidity::Codec)]
745
pub struct Data {
746
	has_data: bool,
747
	value: BoundedBytes<ConstU32<32>>,
748
}
749

            
750
impl TryFrom<Data> for pallet_identity::Data {
751
	type Error = &'static str;
752

            
753
97
	fn try_from(value: Data) -> Result<Self, Self::Error> {
754
97
		if !value.has_data {
755
60
			return Ok(pallet_identity::Data::None);
756
37
		}
757
37

            
758
37
		let value: Vec<_> = value.value.into();
759
37
		let value: sp_runtime::BoundedVec<_, ConstU32<32>> =
760
37
			value.try_into().map_err(|_| "exceeded bounds")?;
761
37
		Ok(pallet_identity::Data::Raw(value))
762
97
	}
763
}
764

            
765
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
766
pub struct Additional {
767
	key: Data,
768
	value: Data,
769
}
770

            
771
49
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
772
pub struct IdentityInfo<FieldLimit> {
773
	additional: BoundedVec<(Data, Data), FieldLimit>,
774
	display: Data,
775
	legal: Data,
776
	web: Data,
777
	riot: Data,
778
	email: Data,
779
	has_pgp_fingerprint: bool,
780
	pgp_fingerprint: BoundedBytes<ConstU32<20>>,
781
	image: Data,
782
	twitter: Data,
783
}
784

            
785
impl<T> Default for IdentityInfo<T> {
786
13
	fn default() -> Self {
787
13
		Self {
788
13
			additional: Default::default(),
789
13
			display: Default::default(),
790
13
			legal: Default::default(),
791
13
			web: Default::default(),
792
13
			riot: Default::default(),
793
13
			email: Default::default(),
794
13
			has_pgp_fingerprint: Default::default(),
795
13
			pgp_fingerprint: Default::default(),
796
13
			image: Default::default(),
797
13
			twitter: Default::default(),
798
13
		}
799
13
	}
800
}
801

            
802
30
#[derive(Eq, PartialEq, Default, Debug, solidity::Codec)]
803
pub struct Judgement {
804
	is_unknown: bool,
805
	is_fee_paid: bool,
806
	fee_paid_deposit: U256,
807
	is_reasonable: bool,
808
	is_known_good: bool,
809
	is_out_of_date: bool,
810
	is_low_quality: bool,
811
	is_erroneous: bool,
812
}
813

            
814
18
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
815
pub struct Registration<FieldLimit> {
816
	is_valid: bool,
817
	judgements: Vec<(u32, Judgement)>,
818
	deposit: U256,
819
	info: IdentityInfo<FieldLimit>,
820
}
821

            
822
impl<T> Default for Registration<T> {
823
2
	fn default() -> Self {
824
2
		Self {
825
2
			is_valid: false,
826
2
			judgements: Vec::new(),
827
2
			deposit: Default::default(),
828
2
			info: Default::default(),
829
2
		}
830
2
	}
831
}
832

            
833
4
#[derive(Default, Debug, solidity::Codec)]
834
pub struct SuperOf {
835
	is_valid: bool,
836
	account: Address,
837
	data: Data,
838
}
839

            
840
4
#[derive(Default, Debug, solidity::Codec)]
841
pub struct SubsOf {
842
	deposit: U256,
843
	accounts: Vec<Address>,
844
}
845

            
846
7
#[derive(Default, Debug, solidity::Codec)]
847
pub struct IdentityFields {
848
	display: bool,
849
	legal: bool,
850
	web: bool,
851
	riot: bool,
852
	email: bool,
853
	pgp_fingerprint: bool,
854
	image: bool,
855
	twitter: bool,
856
}
857

            
858
2
#[derive(Default, Debug, solidity::Codec)]
859
pub struct Registrar {
860
	is_valid: bool,
861
	index: u32,
862
	account: Address,
863
	fee: U256,
864
	fields: IdentityFields,
865
}