pallet_evm_precompile_identity/
lib.rs

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
21extern crate alloc;
22
23use fp_evm::PrecompileHandle;
24use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo};
25use frame_support::sp_runtime::traits::StaticLookup;
26use frame_support::traits::Currency;
27use pallet_evm::AddressMapping;
28use pallet_identity::legacy::IdentityField;
29use parity_scale_codec::MaxEncodedLen;
30use precompile_utils::prelude::*;
31use sp_core::{ConstU32, Get, H160, H256, U256};
32use sp_runtime::traits::Dispatchable;
33use sp_std::boxed::Box;
34use sp_std::marker::PhantomData;
35use sp_std::vec::Vec;
36
37#[cfg(test)]
38mod mock;
39#[cfg(test)]
40mod tests;
41
42type BalanceOf<T> = <<T as pallet_identity::Config>::Currency as Currency<
43	<T as frame_system::Config>::AccountId,
44>>::Balance;
45
46type 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.
50pub(crate) const SELECTOR_LOG_IDENTITY_SET: [u8; 32] = keccak256!("IdentitySet(address)");
51pub(crate) const SELECTOR_LOG_IDENTITY_CLEARED: [u8; 32] = keccak256!("IdentityCleared(address)");
52pub(crate) const SELECTOR_LOG_JUDGEMENT_REQUESTED: [u8; 32] =
53	keccak256!("JudgementRequested(address,uint32)");
54pub(crate) const SELECTOR_LOG_JUDGEMENT_UNREQUESTED: [u8; 32] =
55	keccak256!("JudgementUnrequested(address,uint32)");
56pub(crate) const SELECTOR_LOG_JUDGEMENT_GIVEN: [u8; 32] =
57	keccak256!("JudgementGiven(address,uint32)");
58pub(crate) const SELECTOR_LOG_SUB_IDENTITY_ADDED: [u8; 32] =
59	keccak256!("SubIdentityAdded(address,address)");
60pub(crate) const SELECTOR_LOG_SUB_IDENTITY_REMOVED: [u8; 32] =
61	keccak256!("SubIdentityRemoved(address,address)");
62pub(crate) const SELECTOR_LOG_SUB_IDENTITY_REVOKED: [u8; 32] =
63	keccak256!("SubIdentityRevoked(address)");
64
65/// A precompile to wrap the functionality from pallet-identity
66pub struct IdentityPrecompile<Runtime, MaxAdditionalFields>(
67	PhantomData<(Runtime, MaxAdditionalFields)>,
68);
69
70#[precompile_utils::precompile]
71#[precompile::test_concrete_types(mock::Runtime, mock::MaxAdditionalFields)]
72impl<Runtime, MaxAdditionalFields> IdentityPrecompile<Runtime, MaxAdditionalFields>
73where
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	fn set_identity(
91		handle: &mut impl PrecompileHandle,
92		info: IdentityInfo<MaxAdditionalFields>,
93	) -> EvmResult {
94		let caller = handle.context().caller;
95
96		let event = log1(
97			handle.context().address,
98			SELECTOR_LOG_IDENTITY_SET,
99			solidity::encode_event_data(Address(caller)),
100		);
101		handle.record_log_costs(&[&event])?;
102
103		let info: Box<Runtime::IdentityInformation> = Self::identity_to_input(info)?;
104
105		let call = pallet_identity::Call::<Runtime>::set_identity { info };
106
107		let origin = Runtime::AddressMapping::into_account_id(caller);
108		RuntimeHelper::<Runtime>::try_dispatch(
109			handle,
110			frame_system::RawOrigin::Signed(origin).into(),
111			call,
112			0,
113		)?;
114
115		event.record(handle)?;
116
117		Ok(())
118	}
119
120	#[precompile::public("setSubs((address,(bool,bytes))[])")]
121	fn set_subs(
122		handle: &mut impl PrecompileHandle,
123		subs: BoundedVec<(Address, Data), Runtime::MaxSubAccounts>,
124	) -> EvmResult {
125		let subs: Vec<_> = subs.into();
126		let mut call_subs = Vec::with_capacity(subs.len());
127		for (i, (addr, data)) in subs.into_iter().enumerate() {
128			let addr = Runtime::AddressMapping::into_account_id(addr.into());
129			let data: pallet_identity::Data = data
130				.try_into()
131				.map_err(|e| RevertReason::custom(e).in_field(alloc::format!("index {i}")))?;
132			call_subs.push((addr, data));
133		}
134		let call = pallet_identity::Call::<Runtime>::set_subs { subs: call_subs };
135
136		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
137		RuntimeHelper::<Runtime>::try_dispatch(
138			handle,
139			frame_system::RawOrigin::Signed(origin).into(),
140			call,
141			0,
142		)?;
143
144		Ok(())
145	}
146
147	#[precompile::public("clearIdentity()")]
148	fn clear_identity(handle: &mut impl PrecompileHandle) -> EvmResult {
149		let caller = handle.context().caller;
150
151		let event = log1(
152			handle.context().address,
153			SELECTOR_LOG_IDENTITY_CLEARED,
154			solidity::encode_event_data(Address(caller)),
155		);
156		handle.record_log_costs(&[&event])?;
157
158		let call = pallet_identity::Call::<Runtime>::clear_identity {};
159
160		let origin = Runtime::AddressMapping::into_account_id(caller);
161		RuntimeHelper::<Runtime>::try_dispatch(
162			handle,
163			frame_system::RawOrigin::Signed(origin).into(),
164			call,
165			0,
166		)?;
167
168		event.record(handle)?;
169
170		Ok(())
171	}
172
173	#[precompile::public("requestJudgement(uint32,uint256)")]
174	fn request_judgement(
175		handle: &mut impl PrecompileHandle,
176		reg_index: u32,
177		max_fee: U256,
178	) -> EvmResult {
179		let caller = handle.context().caller;
180
181		let event = log1(
182			handle.context().address,
183			SELECTOR_LOG_JUDGEMENT_REQUESTED,
184			solidity::encode_event_data((Address(caller), reg_index)),
185		);
186		handle.record_log_costs(&[&event])?;
187
188		let max_fee = max_fee
189			.try_into()
190			.map_err(|_| RevertReason::value_is_too_large("max_fee"))?;
191		let call = pallet_identity::Call::<Runtime>::request_judgement { reg_index, max_fee };
192
193		let origin = Runtime::AddressMapping::into_account_id(caller);
194		RuntimeHelper::<Runtime>::try_dispatch(
195			handle,
196			frame_system::RawOrigin::Signed(origin).into(),
197			call,
198			0,
199		)?;
200
201		event.record(handle)?;
202
203		Ok(())
204	}
205
206	#[precompile::public("cancelRequest(uint32)")]
207	fn cancel_request(handle: &mut impl PrecompileHandle, reg_index: u32) -> EvmResult {
208		let caller = handle.context().caller;
209
210		let event = log1(
211			handle.context().address,
212			SELECTOR_LOG_JUDGEMENT_UNREQUESTED,
213			solidity::encode_event_data((Address(caller), reg_index)),
214		);
215		handle.record_log_costs(&[&event])?;
216
217		let call = pallet_identity::Call::<Runtime>::cancel_request { reg_index };
218
219		let origin = Runtime::AddressMapping::into_account_id(caller);
220		RuntimeHelper::<Runtime>::try_dispatch(
221			handle,
222			frame_system::RawOrigin::Signed(origin).into(),
223			call,
224			0,
225		)?;
226
227		event.record(handle)?;
228
229		Ok(())
230	}
231
232	#[precompile::public("setFee(uint32,uint256)")]
233	fn set_fee(handle: &mut impl PrecompileHandle, index: u32, fee: U256) -> EvmResult {
234		let fee = fee
235			.try_into()
236			.map_err(|_| RevertReason::value_is_too_large("fee"))?;
237		let call = pallet_identity::Call::<Runtime>::set_fee { index, fee };
238
239		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
240		RuntimeHelper::<Runtime>::try_dispatch(
241			handle,
242			frame_system::RawOrigin::Signed(origin).into(),
243			call,
244			0,
245		)?;
246
247		Ok(())
248	}
249
250	#[precompile::public("setAccountId(uint32,address)")]
251	fn set_account_id(handle: &mut impl PrecompileHandle, index: u32, new: Address) -> EvmResult {
252		let new = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(new.0));
253		let call = pallet_identity::Call::<Runtime>::set_account_id { index, new };
254
255		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
256		RuntimeHelper::<Runtime>::try_dispatch(
257			handle,
258			frame_system::RawOrigin::Signed(origin).into(),
259			call,
260			0,
261		)?;
262
263		Ok(())
264	}
265
266	#[precompile::public("setFields(uint32,(bool,bool,bool,bool,bool,bool,bool,bool))")]
267	fn set_fields(
268		handle: &mut impl PrecompileHandle,
269		index: u32,
270		fields: IdentityFields,
271	) -> EvmResult {
272		let fields = Self::identity_fields_to_input(fields);
273		let call = pallet_identity::Call::<Runtime>::set_fields { index, fields };
274
275		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
276		RuntimeHelper::<Runtime>::try_dispatch(
277			handle,
278			frame_system::RawOrigin::Signed(origin).into(),
279			call,
280			0,
281		)?;
282
283		Ok(())
284	}
285
286	#[precompile::public(
287		"provideJudgement(uint32,address,(bool,bool,uint256,bool,bool,bool,bool,bool),bytes32)"
288	)]
289	fn provide_judgement(
290		handle: &mut impl PrecompileHandle,
291		reg_index: u32,
292		target: Address,
293		judgement: Judgement,
294		identity: H256,
295	) -> EvmResult {
296		let caller = handle.context().caller;
297
298		let event = log1(
299			handle.context().address,
300			SELECTOR_LOG_JUDGEMENT_GIVEN,
301			solidity::encode_event_data((target, reg_index)),
302		);
303		handle.record_log_costs(&[&event])?;
304
305		let target = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(target.0));
306		let judgement = Self::judgment_to_input(judgement)?;
307		let identity: Runtime::Hash = identity.into();
308		let call = pallet_identity::Call::<Runtime>::provide_judgement {
309			reg_index,
310			target,
311			judgement,
312			identity,
313		};
314
315		let origin = Runtime::AddressMapping::into_account_id(caller);
316		RuntimeHelper::<Runtime>::try_dispatch(
317			handle,
318			frame_system::RawOrigin::Signed(origin).into(),
319			call,
320			0,
321		)?;
322
323		event.record(handle)?;
324
325		Ok(())
326	}
327
328	#[precompile::public("addSub(address,(bool,bytes))")]
329	fn add_sub(handle: &mut impl PrecompileHandle, sub: Address, data: Data) -> EvmResult {
330		let caller = handle.context().caller;
331
332		let event = log1(
333			handle.context().address,
334			SELECTOR_LOG_SUB_IDENTITY_ADDED,
335			solidity::encode_event_data((sub, Address(caller))),
336		);
337		handle.record_log_costs(&[&event])?;
338
339		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
340		let data: pallet_identity::Data = data
341			.try_into()
342			.map_err(|e| RevertReason::custom(e).in_field("data"))?;
343		let call = pallet_identity::Call::<Runtime>::add_sub { sub, data };
344
345		let origin = Runtime::AddressMapping::into_account_id(caller);
346		RuntimeHelper::<Runtime>::try_dispatch(
347			handle,
348			frame_system::RawOrigin::Signed(origin).into(),
349			call,
350			0,
351		)?;
352
353		event.record(handle)?;
354
355		Ok(())
356	}
357
358	#[precompile::public("renameSub(address,(bool,bytes))")]
359	fn rename_sub(handle: &mut impl PrecompileHandle, sub: Address, data: Data) -> EvmResult {
360		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
361		let data: pallet_identity::Data = data
362			.try_into()
363			.map_err(|e| RevertReason::custom(e).in_field("data"))?;
364		let call = pallet_identity::Call::<Runtime>::rename_sub { sub, data };
365
366		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
367		RuntimeHelper::<Runtime>::try_dispatch(
368			handle,
369			frame_system::RawOrigin::Signed(origin).into(),
370			call,
371			0,
372		)?;
373
374		Ok(())
375	}
376
377	#[precompile::public("removeSub(address)")]
378	fn remove_sub(handle: &mut impl PrecompileHandle, sub: Address) -> EvmResult {
379		let caller = handle.context().caller;
380
381		let event = log1(
382			handle.context().address,
383			SELECTOR_LOG_SUB_IDENTITY_REMOVED,
384			solidity::encode_event_data((sub, Address(caller))),
385		);
386		handle.record_log_costs(&[&event])?;
387
388		let sub = Runtime::Lookup::unlookup(Runtime::AddressMapping::into_account_id(sub.0));
389		let call = pallet_identity::Call::<Runtime>::remove_sub { sub };
390
391		let origin = Runtime::AddressMapping::into_account_id(caller);
392		RuntimeHelper::<Runtime>::try_dispatch(
393			handle,
394			frame_system::RawOrigin::Signed(origin).into(),
395			call,
396			0,
397		)?;
398
399		event.record(handle)?;
400
401		Ok(())
402	}
403
404	#[precompile::public("quitSub()")]
405	fn quit_sub(handle: &mut impl PrecompileHandle) -> EvmResult {
406		let caller = handle.context().caller;
407
408		let event = log1(
409			handle.context().address,
410			SELECTOR_LOG_SUB_IDENTITY_REVOKED,
411			solidity::encode_event_data(Address(caller)),
412		);
413		handle.record_log_costs(&[&event])?;
414
415		let call = pallet_identity::Call::<Runtime>::quit_sub {};
416
417		let origin = Runtime::AddressMapping::into_account_id(caller);
418		RuntimeHelper::<Runtime>::try_dispatch(
419			handle,
420			frame_system::RawOrigin::Signed(origin).into(),
421			call,
422			0,
423		)?;
424
425		event.record(handle)?;
426
427		Ok(())
428	}
429
430	#[precompile::public("identity(address)")]
431	#[precompile::view]
432	fn identity(
433		handle: &mut impl PrecompileHandle,
434		who: Address,
435	) -> EvmResult<Registration<MaxAdditionalFields>> {
436		// Storage item: IdentityOf ->
437		//		Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>
438		handle.record_db_read::<Runtime>(pallet_identity::Registration::<
439			BalanceOf<Runtime>,
440			Runtime::MaxRegistrars,
441			Runtime::IdentityInformation,
442		>::max_encoded_len())?;
443
444		let who: H160 = who.into();
445		let who = Runtime::AddressMapping::into_account_id(who);
446		let identity = pallet_identity::IdentityOf::<Runtime>::get(who);
447
448		Ok(Self::identity_to_output(identity)?)
449	}
450
451	#[precompile::public("superOf(address)")]
452	#[precompile::view]
453	fn super_of(handle: &mut impl PrecompileHandle, who: Address) -> EvmResult<SuperOf> {
454		// Storage item: SuperOf -> (T::AccountId, Data)
455		handle.record_db_read::<Runtime>(
456			Runtime::AccountId::max_encoded_len()
457				.saturating_add(pallet_identity::Data::max_encoded_len()),
458		)?;
459
460		let who: H160 = who.into();
461		let who = Runtime::AddressMapping::into_account_id(who);
462		if let Some((account, data)) = pallet_identity::SuperOf::<Runtime>::get(who) {
463			Ok(SuperOf {
464				is_valid: true,
465				account: Address(account.into()),
466				data: Self::data_to_output(data),
467			})
468		} else {
469			Ok(SuperOf::default())
470		}
471	}
472
473	#[precompile::public("subsOf(address)")]
474	#[precompile::view]
475	fn subs_of(handle: &mut impl PrecompileHandle, who: Address) -> EvmResult<SubsOf> {
476		// Storage item: SubsOf -> (BalanceOf<T>, BoundedVec<T::AccountId, T::MaxSubAccounts>)
477		handle.record_db_read::<Runtime>(
478			BalanceOf::<Runtime>::max_encoded_len().saturating_add(
479				Runtime::AccountId::max_encoded_len()
480					.saturating_mul(Runtime::MaxSubAccounts::get() as usize),
481			),
482		)?;
483
484		let who: H160 = who.into();
485		let who = Runtime::AddressMapping::into_account_id(who);
486		let (deposit, accounts) = pallet_identity::SubsOf::<Runtime>::get(who);
487
488		let accounts = accounts
489			.into_iter()
490			.map(|account| Address(account.into()))
491			.collect();
492
493		Ok(SubsOf {
494			deposit: deposit.into(),
495			accounts,
496		})
497	}
498
499	#[precompile::public("registrars()")]
500	#[precompile::view]
501	fn registrars(handle: &mut impl PrecompileHandle) -> EvmResult<Vec<Registrar>> {
502		// Storage item: Registrars ->
503		// 		BoundedVec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>, T::MaxRegistrars>
504		handle.record_db_read::<Runtime>(
505			pallet_identity::RegistrarInfo::<
506				BalanceOf<Runtime>,
507				Runtime::AccountId,
508				IdentityFieldOf<Runtime>,
509			>::max_encoded_len()
510			.saturating_mul(Runtime::MaxRegistrars::get() as usize),
511		)?;
512
513		let registrars = pallet_identity::Registrars::<Runtime>::get()
514			.into_iter()
515			.enumerate()
516			.map(|(index, maybe_reg)| {
517				if let Some(reg) = maybe_reg {
518					let fields: u64 = reg.fields.into();
519					Registrar {
520						is_valid: true,
521						index: index as u32,
522						account: Address(reg.account.into()),
523						fee: reg.fee.into(),
524						fields: IdentityFields {
525							display: fields & (IdentityField::Display as u64)
526								== (IdentityField::Display as u64),
527							legal: fields & (IdentityField::Legal as u64)
528								== (IdentityField::Legal as u64),
529							web: fields & (IdentityField::Web as u64)
530								== (IdentityField::Web as u64),
531							riot: fields & (IdentityField::Riot as u64)
532								== (IdentityField::Riot as u64),
533							email: fields & (IdentityField::Email as u64)
534								== (IdentityField::Email as u64),
535							pgp_fingerprint: fields & (IdentityField::PgpFingerprint as u64)
536								== (IdentityField::PgpFingerprint as u64),
537							image: fields & (IdentityField::Image as u64)
538								== (IdentityField::Image as u64),
539							twitter: fields & (IdentityField::Twitter as u64)
540								== (IdentityField::Twitter as u64),
541						},
542					}
543				} else {
544					Registrar {
545						is_valid: false,
546						index: index as u32,
547						..Default::default()
548					}
549				}
550			})
551			.collect();
552
553		Ok(registrars)
554	}
555
556	fn identity_fields_to_input(fields: IdentityFields) -> IdentityFieldOf<Runtime> {
557		let mut field_bits = 0u64;
558		if fields.display {
559			field_bits = field_bits | IdentityField::Display as u64;
560		}
561		if fields.legal {
562			field_bits = field_bits | IdentityField::Legal as u64;
563		}
564		if fields.web {
565			field_bits = field_bits | IdentityField::Web as u64;
566		}
567		if fields.riot {
568			field_bits = field_bits | IdentityField::Riot as u64;
569		}
570		if fields.email {
571			field_bits = field_bits | IdentityField::Email as u64;
572		}
573		if fields.pgp_fingerprint {
574			field_bits = field_bits | IdentityField::PgpFingerprint as u64;
575		}
576		if fields.image {
577			field_bits = field_bits | IdentityField::Image as u64;
578		}
579		if fields.twitter {
580			field_bits = field_bits | IdentityField::Twitter as u64;
581		}
582
583		IdentityFieldOf::<Runtime>::from(field_bits)
584	}
585
586	fn identity_to_input(
587		info: IdentityInfo<MaxAdditionalFields>,
588	) -> MayRevert<Box<pallet_identity::legacy::IdentityInfo<MaxAdditionalFields>>> {
589		// let additional: Vec<(pallet_identity::Data, pallet_identity::Data)> = info.additional.into();
590		let mut additional: sp_runtime::BoundedVec<
591			(pallet_identity::Data, pallet_identity::Data),
592			MaxAdditionalFields,
593		> = Default::default();
594		let iter: Vec<_> = info.additional.into();
595		for (i, (k, v)) in iter.into_iter().enumerate() {
596			let k: pallet_identity::Data = k.try_into().map_err(|e| {
597				RevertReason::custom(e).in_field(alloc::format!("additional.{i}.key"))
598			})?;
599			let v: pallet_identity::Data = v.try_into().map_err(|e| {
600				RevertReason::custom(e).in_field(alloc::format!("additional.{i}.value"))
601			})?;
602			additional
603				.try_push((k, v))
604				.map_err(|_| RevertReason::custom("out of bounds").in_field("additional"))?;
605		}
606
607		let pgp_fingerprint: Option<[u8; 20]> = if info.has_pgp_fingerprint {
608			let fingerprint: Vec<_> = info.pgp_fingerprint.into();
609			let fingerprint = fingerprint
610				.try_into()
611				.map_err(|_| RevertReason::custom("pgp_fingerprint must be 20 bytes long"))?;
612			Some(fingerprint)
613		} else {
614			None
615		};
616		let identity_info = pallet_identity::legacy::IdentityInfo::<MaxAdditionalFields> {
617			additional,
618			display: info
619				.display
620				.try_into()
621				.map_err(|e| RevertReason::custom(e).in_field("display"))?,
622			legal: info
623				.legal
624				.try_into()
625				.map_err(|e| RevertReason::custom(e).in_field("legal"))?,
626			web: info
627				.web
628				.try_into()
629				.map_err(|e| RevertReason::custom(e).in_field("web"))?,
630			riot: info
631				.riot
632				.try_into()
633				.map_err(|e| RevertReason::custom(e).in_field("riot"))?,
634			email: info
635				.email
636				.try_into()
637				.map_err(|e| RevertReason::custom(e).in_field("email"))?,
638			pgp_fingerprint,
639			image: info
640				.image
641				.try_into()
642				.map_err(|e| RevertReason::custom(e).in_field("image"))?,
643			twitter: info
644				.twitter
645				.try_into()
646				.map_err(|e| RevertReason::custom(e).in_field("twitter"))?,
647		};
648
649		Ok(Box::new(identity_info))
650	}
651
652	fn identity_to_output(
653		registration: Option<
654			pallet_identity::Registration<
655				BalanceOf<Runtime>,
656				Runtime::MaxRegistrars,
657				Runtime::IdentityInformation,
658			>,
659		>,
660	) -> MayRevert<Registration<MaxAdditionalFields>> {
661		let Some(registration) = registration else {
662			return Ok(Registration::<MaxAdditionalFields>::default());
663		};
664
665		let mut identity_info = IdentityInfo::<MaxAdditionalFields> {
666			additional: Default::default(),
667			display: Self::data_to_output(registration.info.display),
668			legal: Self::data_to_output(registration.info.legal),
669			web: Self::data_to_output(registration.info.web),
670			riot: Self::data_to_output(registration.info.riot),
671			email: Self::data_to_output(registration.info.email),
672			has_pgp_fingerprint: false,
673			pgp_fingerprint: Default::default(),
674			image: Self::data_to_output(registration.info.image),
675			twitter: Self::data_to_output(registration.info.twitter),
676		};
677
678		let mut additional = Vec::new();
679		for (k, v) in registration.info.additional.into_iter() {
680			let k: Data = Self::data_to_output(k);
681			let v: Data = Self::data_to_output(v);
682			additional.push((k, v));
683		}
684
685		if let Some(pgp_fingerprint) = registration.info.pgp_fingerprint {
686			identity_info.has_pgp_fingerprint = true;
687			identity_info.pgp_fingerprint = pgp_fingerprint.into();
688		}
689
690		identity_info.additional = additional.into();
691
692		let mut judgements = Vec::new();
693		for (index, judgement) in registration.judgements.into_iter() {
694			judgements.push((index, Self::judgement_to_output(judgement)));
695		}
696
697		let reg = Registration::<MaxAdditionalFields> {
698			is_valid: true,
699			judgements: judgements.into(),
700			deposit: registration.deposit.into(),
701			info: identity_info,
702		};
703
704		Ok(reg)
705	}
706
707	fn judgement_to_output(value: pallet_identity::Judgement<BalanceOf<Runtime>>) -> Judgement {
708		let mut judgement = Judgement::default();
709
710		match value {
711			pallet_identity::Judgement::Unknown => {
712				judgement.is_unknown = true;
713			}
714			pallet_identity::Judgement::FeePaid(balance) => {
715				judgement.is_fee_paid = true;
716				judgement.fee_paid_deposit = balance.into();
717			}
718			pallet_identity::Judgement::Reasonable => {
719				judgement.is_reasonable = true;
720			}
721			pallet_identity::Judgement::KnownGood => {
722				judgement.is_known_good = true;
723			}
724			pallet_identity::Judgement::OutOfDate => {
725				judgement.is_out_of_date = true;
726			}
727			pallet_identity::Judgement::LowQuality => {
728				judgement.is_low_quality = true;
729			}
730			pallet_identity::Judgement::Erroneous => {
731				judgement.is_erroneous = true;
732			}
733		};
734
735		judgement
736	}
737
738	fn judgment_to_input(
739		value: Judgement,
740	) -> Result<pallet_identity::Judgement<BalanceOf<Runtime>>, RevertReason> {
741		if value.is_unknown {
742			return Ok(pallet_identity::Judgement::Unknown);
743		}
744
745		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		}
753
754		if value.is_reasonable {
755			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	}
776
777	fn data_to_output(data: pallet_identity::Data) -> Data {
778		let mut output = Data::default();
779		match data {
780			pallet_identity::Data::None => (),
781			pallet_identity::Data::Raw(bytes) => {
782				let bytes: Vec<_> = bytes.into();
783				output.has_data = true;
784				output.value = bytes.into();
785			}
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		output
805	}
806}
807
808#[derive(Default, Debug, Eq, PartialEq, solidity::Codec)]
809pub struct Data {
810	has_data: bool,
811	value: BoundedBytes<ConstU32<32>>,
812}
813
814impl TryFrom<Data> for pallet_identity::Data {
815	type Error = &'static str;
816
817	fn try_from(value: Data) -> Result<Self, Self::Error> {
818		if !value.has_data {
819			return Ok(pallet_identity::Data::None);
820		}
821
822		let value: Vec<_> = value.value.into();
823		let value: sp_runtime::BoundedVec<_, ConstU32<32>> =
824			value.try_into().map_err(|_| "exceeded bounds")?;
825		Ok(pallet_identity::Data::Raw(value))
826	}
827}
828
829#[derive(Eq, PartialEq, Debug, solidity::Codec)]
830pub struct Additional {
831	key: Data,
832	value: Data,
833}
834
835#[derive(Eq, PartialEq, Debug, solidity::Codec)]
836pub 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
849impl<T> Default for IdentityInfo<T> {
850	fn default() -> Self {
851		Self {
852			additional: Default::default(),
853			display: Default::default(),
854			legal: Default::default(),
855			web: Default::default(),
856			riot: Default::default(),
857			email: Default::default(),
858			has_pgp_fingerprint: Default::default(),
859			pgp_fingerprint: Default::default(),
860			image: Default::default(),
861			twitter: Default::default(),
862		}
863	}
864}
865
866#[derive(Eq, PartialEq, Default, Debug, solidity::Codec)]
867pub 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)]
879pub struct Registration<FieldLimit> {
880	is_valid: bool,
881	judgements: Vec<(u32, Judgement)>,
882	deposit: U256,
883	info: IdentityInfo<FieldLimit>,
884}
885
886impl<T> Default for Registration<T> {
887	fn default() -> Self {
888		Self {
889			is_valid: false,
890			judgements: Vec::new(),
891			deposit: Default::default(),
892			info: Default::default(),
893		}
894	}
895}
896
897#[derive(Default, Debug, solidity::Codec)]
898pub struct SuperOf {
899	is_valid: bool,
900	account: Address,
901	data: Data,
902}
903
904#[derive(Default, Debug, solidity::Codec)]
905pub struct SubsOf {
906	deposit: U256,
907	accounts: Vec<Address>,
908}
909
910#[derive(Default, Debug, solidity::Codec)]
911pub 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)]
923pub struct Registrar {
924	is_valid: bool,
925	index: u32,
926	account: Address,
927	fee: U256,
928	fields: IdentityFields,
929}