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
3
#[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: From<pallet_identity::Call<Runtime>>,
83
	BalanceOf<Runtime>: TryFrom<U256> + Into<U256> + solidity::Codec,
84
	<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
85
{
86
	// Note: addRegistrar(address) & killIdentity(address) are not supported since they use a
87
	// force origin.
88

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

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

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

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

            
107
12
		let origin = Runtime::AddressMapping::into_account_id(caller);
108
12
		RuntimeHelper::<Runtime>::try_dispatch(
109
12
			handle,
110
12
			frame_system::RawOrigin::Signed(origin).into(),
111
12
			call,
112
			0,
113
		)?;
114

            
115
12
		event.record(handle)?;
116

            
117
12
		Ok(())
118
12
	}
119

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

            
136
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
137
2
		RuntimeHelper::<Runtime>::try_dispatch(
138
2
			handle,
139
2
			frame_system::RawOrigin::Signed(origin).into(),
140
2
			call,
141
			0,
142
1
		)?;
143

            
144
1
		Ok(())
145
2
	}
146

            
147
	#[precompile::public("clearIdentity()")]
148
2
	fn clear_identity(handle: &mut impl PrecompileHandle) -> EvmResult {
149
2
		let caller = handle.context().caller;
150

            
151
2
		let event = log1(
152
2
			handle.context().address,
153
			SELECTOR_LOG_IDENTITY_CLEARED,
154
2
			solidity::encode_event_data(Address(caller)),
155
		);
156
2
		handle.record_log_costs(&[&event])?;
157

            
158
2
		let call = pallet_identity::Call::<Runtime>::clear_identity {};
159

            
160
2
		let origin = Runtime::AddressMapping::into_account_id(caller);
161
2
		RuntimeHelper::<Runtime>::try_dispatch(
162
2
			handle,
163
2
			frame_system::RawOrigin::Signed(origin).into(),
164
2
			call,
165
			0,
166
1
		)?;
167

            
168
1
		event.record(handle)?;
169

            
170
1
		Ok(())
171
2
	}
172

            
173
	#[precompile::public("requestJudgement(uint32,uint256)")]
174
3
	fn request_judgement(
175
3
		handle: &mut impl PrecompileHandle,
176
3
		reg_index: u32,
177
3
		max_fee: U256,
178
3
	) -> EvmResult {
179
3
		let caller = handle.context().caller;
180

            
181
3
		let event = log1(
182
3
			handle.context().address,
183
			SELECTOR_LOG_JUDGEMENT_REQUESTED,
184
3
			solidity::encode_event_data((Address(caller), reg_index)),
185
		);
186
3
		handle.record_log_costs(&[&event])?;
187

            
188
3
		let max_fee = max_fee
189
3
			.try_into()
190
3
			.map_err(|_| RevertReason::value_is_too_large("max_fee"))?;
191
3
		let call = pallet_identity::Call::<Runtime>::request_judgement { reg_index, max_fee };
192

            
193
3
		let origin = Runtime::AddressMapping::into_account_id(caller);
194
3
		RuntimeHelper::<Runtime>::try_dispatch(
195
3
			handle,
196
3
			frame_system::RawOrigin::Signed(origin).into(),
197
3
			call,
198
			0,
199
		)?;
200

            
201
3
		event.record(handle)?;
202

            
203
3
		Ok(())
204
3
	}
205

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

            
210
1
		let event = log1(
211
1
			handle.context().address,
212
			SELECTOR_LOG_JUDGEMENT_UNREQUESTED,
213
1
			solidity::encode_event_data((Address(caller), reg_index)),
214
		);
215
1
		handle.record_log_costs(&[&event])?;
216

            
217
1
		let call = pallet_identity::Call::<Runtime>::cancel_request { reg_index };
218

            
219
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
220
1
		RuntimeHelper::<Runtime>::try_dispatch(
221
1
			handle,
222
1
			frame_system::RawOrigin::Signed(origin).into(),
223
1
			call,
224
			0,
225
		)?;
226

            
227
1
		event.record(handle)?;
228

            
229
1
		Ok(())
230
1
	}
231

            
232
	#[precompile::public("setFee(uint32,uint256)")]
233
5
	fn set_fee(handle: &mut impl PrecompileHandle, index: u32, fee: U256) -> EvmResult {
234
5
		let fee = fee
235
5
			.try_into()
236
5
			.map_err(|_| RevertReason::value_is_too_large("fee"))?;
237
5
		let call = pallet_identity::Call::<Runtime>::set_fee { index, fee };
238

            
239
5
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
240
5
		RuntimeHelper::<Runtime>::try_dispatch(
241
5
			handle,
242
5
			frame_system::RawOrigin::Signed(origin).into(),
243
5
			call,
244
			0,
245
1
		)?;
246

            
247
4
		Ok(())
248
5
	}
249

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

            
255
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
256
2
		RuntimeHelper::<Runtime>::try_dispatch(
257
2
			handle,
258
2
			frame_system::RawOrigin::Signed(origin).into(),
259
2
			call,
260
			0,
261
1
		)?;
262

            
263
1
		Ok(())
264
2
	}
265

            
266
	#[precompile::public("setFields(uint32,(bool,bool,bool,bool,bool,bool,bool,bool))")]
267
2
	fn set_fields(
268
2
		handle: &mut impl PrecompileHandle,
269
2
		index: u32,
270
2
		fields: IdentityFields,
271
2
	) -> EvmResult {
272
2
		let fields = Self::identity_fields_to_input(fields);
273
2
		let call = pallet_identity::Call::<Runtime>::set_fields { index, fields };
274

            
275
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
276
2
		RuntimeHelper::<Runtime>::try_dispatch(
277
2
			handle,
278
2
			frame_system::RawOrigin::Signed(origin).into(),
279
2
			call,
280
			0,
281
1
		)?;
282

            
283
1
		Ok(())
284
2
	}
285

            
286
	#[precompile::public(
287
		"provideJudgement(uint32,address,(bool,bool,uint256,bool,bool,bool,bool,bool),bytes32)"
288
	)]
289
1
	fn provide_judgement(
290
1
		handle: &mut impl PrecompileHandle,
291
1
		reg_index: u32,
292
1
		target: Address,
293
1
		judgement: Judgement,
294
1
		identity: H256,
295
1
	) -> EvmResult {
296
1
		let caller = handle.context().caller;
297

            
298
1
		let event = log1(
299
1
			handle.context().address,
300
			SELECTOR_LOG_JUDGEMENT_GIVEN,
301
1
			solidity::encode_event_data((target, reg_index)),
302
		);
303
1
		handle.record_log_costs(&[&event])?;
304

            
305
1
		let target = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(target.0));
306
1
		let judgement = Self::judgment_to_input(judgement)?;
307
1
		let identity: Runtime::Hash = identity.into();
308
1
		let call = pallet_identity::Call::<Runtime>::provide_judgement {
309
1
			reg_index,
310
1
			target,
311
1
			judgement,
312
1
			identity,
313
1
		};
314

            
315
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
316
1
		RuntimeHelper::<Runtime>::try_dispatch(
317
1
			handle,
318
1
			frame_system::RawOrigin::Signed(origin).into(),
319
1
			call,
320
			0,
321
		)?;
322

            
323
1
		event.record(handle)?;
324

            
325
1
		Ok(())
326
1
	}
327

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

            
332
4
		let event = log1(
333
4
			handle.context().address,
334
			SELECTOR_LOG_SUB_IDENTITY_ADDED,
335
4
			solidity::encode_event_data((sub, Address(caller))),
336
		);
337
4
		handle.record_log_costs(&[&event])?;
338

            
339
4
		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
340
4
		let data: pallet_identity::Data = data
341
4
			.try_into()
342
4
			.map_err(|e| RevertReason::custom(e).in_field("data"))?;
343
4
		let call = pallet_identity::Call::<Runtime>::add_sub { sub, data };
344

            
345
4
		let origin = Runtime::AddressMapping::into_account_id(caller);
346
4
		RuntimeHelper::<Runtime>::try_dispatch(
347
4
			handle,
348
4
			frame_system::RawOrigin::Signed(origin).into(),
349
4
			call,
350
			0,
351
		)?;
352

            
353
4
		event.record(handle)?;
354

            
355
4
		Ok(())
356
4
	}
357

            
358
	#[precompile::public("renameSub(address,(bool,bytes))")]
359
1
	fn rename_sub(handle: &mut impl PrecompileHandle, sub: Address, data: Data) -> EvmResult {
360
1
		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
361
1
		let data: pallet_identity::Data = data
362
1
			.try_into()
363
1
			.map_err(|e| RevertReason::custom(e).in_field("data"))?;
364
1
		let call = pallet_identity::Call::<Runtime>::rename_sub { sub, data };
365

            
366
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
367
1
		RuntimeHelper::<Runtime>::try_dispatch(
368
1
			handle,
369
1
			frame_system::RawOrigin::Signed(origin).into(),
370
1
			call,
371
			0,
372
		)?;
373

            
374
1
		Ok(())
375
1
	}
376

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

            
381
1
		let event = log1(
382
1
			handle.context().address,
383
			SELECTOR_LOG_SUB_IDENTITY_REMOVED,
384
1
			solidity::encode_event_data((sub, Address(caller))),
385
		);
386
1
		handle.record_log_costs(&[&event])?;
387

            
388
1
		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
389
1
		let call = pallet_identity::Call::<Runtime>::remove_sub { sub };
390

            
391
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
392
1
		RuntimeHelper::<Runtime>::try_dispatch(
393
1
			handle,
394
1
			frame_system::RawOrigin::Signed(origin).into(),
395
1
			call,
396
			0,
397
		)?;
398

            
399
1
		event.record(handle)?;
400

            
401
1
		Ok(())
402
1
	}
403

            
404
	#[precompile::public("quitSub()")]
405
1
	fn quit_sub(handle: &mut impl PrecompileHandle) -> EvmResult {
406
1
		let caller = handle.context().caller;
407

            
408
1
		let event = log1(
409
1
			handle.context().address,
410
			SELECTOR_LOG_SUB_IDENTITY_REVOKED,
411
1
			solidity::encode_event_data(Address(caller)),
412
		);
413
1
		handle.record_log_costs(&[&event])?;
414

            
415
1
		let call = pallet_identity::Call::<Runtime>::quit_sub {};
416

            
417
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
418
1
		RuntimeHelper::<Runtime>::try_dispatch(
419
1
			handle,
420
1
			frame_system::RawOrigin::Signed(origin).into(),
421
1
			call,
422
			0,
423
		)?;
424

            
425
1
		event.record(handle)?;
426

            
427
1
		Ok(())
428
1
	}
429

            
430
	#[precompile::public("identity(address)")]
431
	#[precompile::view]
432
9
	fn identity(
433
9
		handle: &mut impl PrecompileHandle,
434
9
		who: Address,
435
9
	) -> EvmResult<Registration<MaxAdditionalFields>> {
436
		// Storage item: IdentityOf ->
437
		//		Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>
438
9
		handle.record_db_read::<Runtime>(pallet_identity::Registration::<
439
9
			BalanceOf<Runtime>,
440
9
			Runtime::MaxRegistrars,
441
9
			Runtime::IdentityInformation,
442
9
		>::max_encoded_len())?;
443

            
444
9
		let who: H160 = who.into();
445
9
		let who = Runtime::AddressMapping::into_account_id(who);
446
9
		let identity = pallet_identity::IdentityOf::<Runtime>::get(who);
447

            
448
9
		Ok(Self::identity_to_output(identity)?)
449
9
	}
450

            
451
	#[precompile::public("superOf(address)")]
452
	#[precompile::view]
453
2
	fn super_of(handle: &mut impl PrecompileHandle, who: Address) -> EvmResult<SuperOf> {
454
		// Storage item: SuperOf -> (T::AccountId, Data)
455
2
		handle.record_db_read::<Runtime>(
456
2
			Runtime::AccountId::max_encoded_len()
457
2
				.saturating_add(pallet_identity::Data::max_encoded_len()),
458
		)?;
459

            
460
2
		let who: H160 = who.into();
461
2
		let who = Runtime::AddressMapping::into_account_id(who);
462
2
		if let Some((account, data)) = pallet_identity::SuperOf::<Runtime>::get(who) {
463
1
			Ok(SuperOf {
464
1
				is_valid: true,
465
1
				account: Address(account.into()),
466
1
				data: Self::data_to_output(data),
467
1
			})
468
		} else {
469
1
			Ok(SuperOf::default())
470
		}
471
2
	}
472

            
473
	#[precompile::public("subsOf(address)")]
474
	#[precompile::view]
475
2
	fn subs_of(handle: &mut impl PrecompileHandle, who: Address) -> EvmResult<SubsOf> {
476
		// Storage item: SubsOf -> (BalanceOf<T>, BoundedVec<T::AccountId, T::MaxSubAccounts>)
477
2
		handle.record_db_read::<Runtime>(
478
2
			BalanceOf::<Runtime>::max_encoded_len().saturating_add(
479
2
				Runtime::AccountId::max_encoded_len()
480
2
					.saturating_mul(Runtime::MaxSubAccounts::get() as usize),
481
			),
482
		)?;
483

            
484
2
		let who: H160 = who.into();
485
2
		let who = Runtime::AddressMapping::into_account_id(who);
486
2
		let (deposit, accounts) = pallet_identity::SubsOf::<Runtime>::get(who);
487

            
488
2
		let accounts = accounts
489
2
			.into_iter()
490
2
			.map(|account| Address(account.into()))
491
2
			.collect();
492

            
493
2
		Ok(SubsOf {
494
2
			deposit: deposit.into(),
495
2
			accounts,
496
2
		})
497
2
	}
498

            
499
	#[precompile::public("registrars()")]
500
	#[precompile::view]
501
2
	fn registrars(handle: &mut impl PrecompileHandle) -> EvmResult<Vec<Registrar>> {
502
		// Storage item: Registrars ->
503
		// 		BoundedVec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>, T::MaxRegistrars>
504
2
		handle.record_db_read::<Runtime>(
505
2
			pallet_identity::RegistrarInfo::<
506
2
				BalanceOf<Runtime>,
507
2
				Runtime::AccountId,
508
2
				IdentityFieldOf<Runtime>,
509
2
			>::max_encoded_len()
510
2
			.saturating_mul(Runtime::MaxRegistrars::get() as usize),
511
		)?;
512

            
513
2
		let registrars = pallet_identity::Registrars::<Runtime>::get()
514
2
			.into_iter()
515
2
			.enumerate()
516
2
			.map(|(index, maybe_reg)| {
517
1
				if let Some(reg) = maybe_reg {
518
1
					let fields: u64 = reg.fields.into();
519
1
					Registrar {
520
1
						is_valid: true,
521
1
						index: index as u32,
522
1
						account: Address(reg.account.into()),
523
1
						fee: reg.fee.into(),
524
1
						fields: IdentityFields {
525
1
							display: fields & (IdentityField::Display as u64)
526
1
								== (IdentityField::Display as u64),
527
1
							legal: fields & (IdentityField::Legal as u64)
528
1
								== (IdentityField::Legal as u64),
529
1
							web: fields & (IdentityField::Web as u64)
530
1
								== (IdentityField::Web as u64),
531
1
							riot: fields & (IdentityField::Riot as u64)
532
1
								== (IdentityField::Riot as u64),
533
1
							email: fields & (IdentityField::Email as u64)
534
1
								== (IdentityField::Email as u64),
535
1
							pgp_fingerprint: fields & (IdentityField::PgpFingerprint as u64)
536
1
								== (IdentityField::PgpFingerprint as u64),
537
1
							image: fields & (IdentityField::Image as u64)
538
1
								== (IdentityField::Image as u64),
539
1
							twitter: fields & (IdentityField::Twitter as u64)
540
1
								== (IdentityField::Twitter as u64),
541
1
						},
542
1
					}
543
				} else {
544
					Registrar {
545
						is_valid: false,
546
						index: index as u32,
547
						..Default::default()
548
					}
549
				}
550
1
			})
551
2
			.collect();
552

            
553
2
		Ok(registrars)
554
2
	}
555

            
556
2
	fn identity_fields_to_input(fields: IdentityFields) -> IdentityFieldOf<Runtime> {
557
2
		let mut field_bits = 0u64;
558
2
		if fields.display {
559
2
			field_bits = field_bits | IdentityField::Display as u64;
560
2
		}
561
2
		if fields.legal {
562
			field_bits = field_bits | IdentityField::Legal as u64;
563
2
		}
564
2
		if fields.web {
565
2
			field_bits = field_bits | IdentityField::Web as u64;
566
2
		}
567
2
		if fields.riot {
568
			field_bits = field_bits | IdentityField::Riot as u64;
569
2
		}
570
2
		if fields.email {
571
			field_bits = field_bits | IdentityField::Email as u64;
572
2
		}
573
2
		if fields.pgp_fingerprint {
574
			field_bits = field_bits | IdentityField::PgpFingerprint as u64;
575
2
		}
576
2
		if fields.image {
577
			field_bits = field_bits | IdentityField::Image as u64;
578
2
		}
579
2
		if fields.twitter {
580
			field_bits = field_bits | IdentityField::Twitter as u64;
581
2
		}
582

            
583
2
		IdentityFieldOf::<Runtime>::from(field_bits)
584
2
	}
585

            
586
12
	fn identity_to_input(
587
12
		info: IdentityInfo<MaxAdditionalFields>,
588
12
	) -> MayRevert<Box<pallet_identity::legacy::IdentityInfo<MaxAdditionalFields>>> {
589
		// let additional: Vec<(pallet_identity::Data, pallet_identity::Data)> = info.additional.into();
590
12
		let mut additional: sp_runtime::BoundedVec<
591
12
			(pallet_identity::Data, pallet_identity::Data),
592
12
			MaxAdditionalFields,
593
12
		> = Default::default();
594
12
		let iter: Vec<_> = info.additional.into();
595
12
		for (i, (k, v)) in iter.into_iter().enumerate() {
596
2
			let k: pallet_identity::Data = k.try_into().map_err(|e| {
597
				RevertReason::custom(e).in_field(alloc::format!("additional.{i}.key"))
598
			})?;
599
2
			let v: pallet_identity::Data = v.try_into().map_err(|e| {
600
				RevertReason::custom(e).in_field(alloc::format!("additional.{i}.value"))
601
			})?;
602
2
			additional
603
2
				.try_push((k, v))
604
2
				.map_err(|_| RevertReason::custom("out of bounds").in_field("additional"))?;
605
		}
606

            
607
12
		let pgp_fingerprint: Option<[u8; 20]> = if info.has_pgp_fingerprint {
608
2
			let fingerprint: Vec<_> = info.pgp_fingerprint.into();
609
2
			let fingerprint = fingerprint
610
2
				.try_into()
611
2
				.map_err(|_| RevertReason::custom("pgp_fingerprint must be 20 bytes long"))?;
612
2
			Some(fingerprint)
613
		} else {
614
10
			None
615
		};
616
12
		let identity_info = pallet_identity::legacy::IdentityInfo::<MaxAdditionalFields> {
617
12
			additional,
618
12
			display: info
619
12
				.display
620
12
				.try_into()
621
12
				.map_err(|e| RevertReason::custom(e).in_field("display"))?,
622
12
			legal: info
623
12
				.legal
624
12
				.try_into()
625
12
				.map_err(|e| RevertReason::custom(e).in_field("legal"))?,
626
12
			web: info
627
12
				.web
628
12
				.try_into()
629
12
				.map_err(|e| RevertReason::custom(e).in_field("web"))?,
630
12
			riot: info
631
12
				.riot
632
12
				.try_into()
633
12
				.map_err(|e| RevertReason::custom(e).in_field("riot"))?,
634
12
			email: info
635
12
				.email
636
12
				.try_into()
637
12
				.map_err(|e| RevertReason::custom(e).in_field("email"))?,
638
12
			pgp_fingerprint,
639
12
			image: info
640
12
				.image
641
12
				.try_into()
642
12
				.map_err(|e| RevertReason::custom(e).in_field("image"))?,
643
12
			twitter: info
644
12
				.twitter
645
12
				.try_into()
646
12
				.map_err(|e| RevertReason::custom(e).in_field("twitter"))?,
647
		};
648

            
649
12
		Ok(Box::new(identity_info))
650
12
	}
651

            
652
9
	fn identity_to_output(
653
9
		registration: Option<
654
9
			pallet_identity::Registration<
655
9
				BalanceOf<Runtime>,
656
9
				Runtime::MaxRegistrars,
657
9
				Runtime::IdentityInformation,
658
9
			>,
659
9
		>,
660
9
	) -> MayRevert<Registration<MaxAdditionalFields>> {
661
9
		let Some(registration) = registration else {
662
1
			return Ok(Registration::<MaxAdditionalFields>::default());
663
		};
664

            
665
8
		let mut identity_info = IdentityInfo::<MaxAdditionalFields> {
666
8
			additional: Default::default(),
667
8
			display: Self::data_to_output(registration.info.display),
668
8
			legal: Self::data_to_output(registration.info.legal),
669
8
			web: Self::data_to_output(registration.info.web),
670
8
			riot: Self::data_to_output(registration.info.riot),
671
8
			email: Self::data_to_output(registration.info.email),
672
8
			has_pgp_fingerprint: false,
673
8
			pgp_fingerprint: Default::default(),
674
8
			image: Self::data_to_output(registration.info.image),
675
8
			twitter: Self::data_to_output(registration.info.twitter),
676
8
		};
677

            
678
8
		let mut additional = Vec::new();
679
8
		for (k, v) in registration.info.additional.into_iter() {
680
2
			let k: Data = Self::data_to_output(k);
681
2
			let v: Data = Self::data_to_output(v);
682
2
			additional.push((k, v));
683
2
		}
684

            
685
8
		if let Some(pgp_fingerprint) = registration.info.pgp_fingerprint {
686
1
			identity_info.has_pgp_fingerprint = true;
687
1
			identity_info.pgp_fingerprint = pgp_fingerprint.into();
688
7
		}
689

            
690
8
		identity_info.additional = additional.into();
691

            
692
8
		let mut judgements = Vec::new();
693
8
		for (index, judgement) in registration.judgements.into_iter() {
694
7
			judgements.push((index, Self::judgement_to_output(judgement)));
695
7
		}
696

            
697
8
		let reg = Registration::<MaxAdditionalFields> {
698
8
			is_valid: true,
699
8
			judgements: judgements.into(),
700
8
			deposit: registration.deposit.into(),
701
8
			info: identity_info,
702
8
		};
703

            
704
8
		Ok(reg)
705
9
	}
706

            
707
7
	fn judgement_to_output(value: pallet_identity::Judgement<BalanceOf<Runtime>>) -> Judgement {
708
7
		let mut judgement = Judgement::default();
709

            
710
7
		match value {
711
1
			pallet_identity::Judgement::Unknown => {
712
1
				judgement.is_unknown = true;
713
1
			}
714
1
			pallet_identity::Judgement::FeePaid(balance) => {
715
1
				judgement.is_fee_paid = true;
716
1
				judgement.fee_paid_deposit = balance.into();
717
1
			}
718
1
			pallet_identity::Judgement::Reasonable => {
719
1
				judgement.is_reasonable = true;
720
1
			}
721
1
			pallet_identity::Judgement::KnownGood => {
722
1
				judgement.is_known_good = true;
723
1
			}
724
1
			pallet_identity::Judgement::OutOfDate => {
725
1
				judgement.is_out_of_date = true;
726
1
			}
727
1
			pallet_identity::Judgement::LowQuality => {
728
1
				judgement.is_low_quality = true;
729
1
			}
730
1
			pallet_identity::Judgement::Erroneous => {
731
1
				judgement.is_erroneous = true;
732
1
			}
733
		};
734

            
735
7
		judgement
736
7
	}
737

            
738
1
	fn judgment_to_input(
739
1
		value: Judgement,
740
1
	) -> Result<pallet_identity::Judgement<BalanceOf<Runtime>>, RevertReason> {
741
1
		if value.is_unknown {
742
			return Ok(pallet_identity::Judgement::Unknown);
743
1
		}
744

            
745
1
		if value.is_fee_paid {
746
			let amount: BalanceOf<Runtime> = value
747
				.fee_paid_deposit
748
				.try_into()
749
				.map_err(|_| RevertReason::value_is_too_large("fee_paid_deposit").into())?;
750

            
751
			return Ok(pallet_identity::Judgement::FeePaid(amount));
752
1
		}
753

            
754
1
		if value.is_reasonable {
755
1
			return Ok(pallet_identity::Judgement::Reasonable);
756
		}
757

            
758
		if value.is_known_good {
759
			return Ok(pallet_identity::Judgement::KnownGood);
760
		}
761

            
762
		if value.is_out_of_date {
763
			return Ok(pallet_identity::Judgement::OutOfDate);
764
		}
765

            
766
		if value.is_low_quality {
767
			return Ok(pallet_identity::Judgement::LowQuality);
768
		}
769

            
770
		if value.is_erroneous {
771
			return Ok(pallet_identity::Judgement::Erroneous);
772
		}
773

            
774
		return Err(RevertReason::custom("invalid"));
775
1
	}
776

            
777
61
	fn data_to_output(data: pallet_identity::Data) -> Data {
778
61
		let mut output = Data::default();
779
61
		match data {
780
42
			pallet_identity::Data::None => (),
781
19
			pallet_identity::Data::Raw(bytes) => {
782
19
				let bytes: Vec<_> = bytes.into();
783
19
				output.has_data = true;
784
19
				output.value = bytes.into();
785
19
			}
786
			pallet_identity::Data::BlakeTwo256(bytes) => {
787
				output.has_data = true;
788
				output.value = bytes.into();
789
			}
790
			pallet_identity::Data::Sha256(bytes) => {
791
				output.has_data = true;
792
				output.value = bytes.into();
793
			}
794
			pallet_identity::Data::Keccak256(bytes) => {
795
				output.has_data = true;
796
				output.value = bytes.into();
797
			}
798
			pallet_identity::Data::ShaThree256(bytes) => {
799
				output.has_data = true;
800
				output.value = bytes.into();
801
			}
802
		}
803

            
804
61
		output
805
61
	}
806
}
807

            
808
#[derive(Default, Debug, Eq, PartialEq, solidity::Codec)]
809
pub struct Data {
810
	has_data: bool,
811
	value: BoundedBytes<ConstU32<32>>,
812
}
813

            
814
impl TryFrom<Data> for pallet_identity::Data {
815
	type Error = &'static str;
816

            
817
97
	fn try_from(value: Data) -> Result<Self, Self::Error> {
818
97
		if !value.has_data {
819
60
			return Ok(pallet_identity::Data::None);
820
37
		}
821

            
822
37
		let value: Vec<_> = value.value.into();
823
37
		let value: sp_runtime::BoundedVec<_, ConstU32<32>> =
824
37
			value.try_into().map_err(|_| "exceeded bounds")?;
825
37
		Ok(pallet_identity::Data::Raw(value))
826
97
	}
827
}
828

            
829
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
830
pub struct Additional {
831
	key: Data,
832
	value: Data,
833
}
834

            
835
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
836
pub struct IdentityInfo<FieldLimit> {
837
	additional: BoundedVec<(Data, Data), FieldLimit>,
838
	display: Data,
839
	legal: Data,
840
	web: Data,
841
	riot: Data,
842
	email: Data,
843
	has_pgp_fingerprint: bool,
844
	pgp_fingerprint: BoundedBytes<ConstU32<20>>,
845
	image: Data,
846
	twitter: Data,
847
}
848

            
849
impl<T> Default for IdentityInfo<T> {
850
13
	fn default() -> Self {
851
13
		Self {
852
13
			additional: Default::default(),
853
13
			display: Default::default(),
854
13
			legal: Default::default(),
855
13
			web: Default::default(),
856
13
			riot: Default::default(),
857
13
			email: Default::default(),
858
13
			has_pgp_fingerprint: Default::default(),
859
13
			pgp_fingerprint: Default::default(),
860
13
			image: Default::default(),
861
13
			twitter: Default::default(),
862
13
		}
863
13
	}
864
}
865

            
866
#[derive(Eq, PartialEq, Default, Debug, solidity::Codec)]
867
pub struct Judgement {
868
	is_unknown: bool,
869
	is_fee_paid: bool,
870
	fee_paid_deposit: U256,
871
	is_reasonable: bool,
872
	is_known_good: bool,
873
	is_out_of_date: bool,
874
	is_low_quality: bool,
875
	is_erroneous: bool,
876
}
877

            
878
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
879
pub struct Registration<FieldLimit> {
880
	is_valid: bool,
881
	judgements: Vec<(u32, Judgement)>,
882
	deposit: U256,
883
	info: IdentityInfo<FieldLimit>,
884
}
885

            
886
impl<T> Default for Registration<T> {
887
2
	fn default() -> Self {
888
2
		Self {
889
2
			is_valid: false,
890
2
			judgements: Vec::new(),
891
2
			deposit: Default::default(),
892
2
			info: Default::default(),
893
2
		}
894
2
	}
895
}
896

            
897
#[derive(Default, Debug, solidity::Codec)]
898
pub struct SuperOf {
899
	is_valid: bool,
900
	account: Address,
901
	data: Data,
902
}
903

            
904
#[derive(Default, Debug, solidity::Codec)]
905
pub struct SubsOf {
906
	deposit: U256,
907
	accounts: Vec<Address>,
908
}
909

            
910
#[derive(Default, Debug, solidity::Codec)]
911
pub struct IdentityFields {
912
	display: bool,
913
	legal: bool,
914
	web: bool,
915
	riot: bool,
916
	email: bool,
917
	pgp_fingerprint: bool,
918
	image: bool,
919
	twitter: bool,
920
}
921

            
922
#[derive(Default, Debug, solidity::Codec)]
923
pub struct Registrar {
924
	is_valid: bool,
925
	index: u32,
926
	account: Address,
927
	fee: U256,
928
	fields: IdentityFields,
929
}