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::Pallet::<Runtime>::identity(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::Pallet::<Runtime>::super_of(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::Pallet::<Runtime>::subs_of(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::Pallet::<Runtime>::registrars()
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
			Option<
596
9
				frame_support::BoundedVec<
597
9
					u8,
598
9
					<Runtime as pallet_identity::Config>::MaxUsernameLength,
599
9
				>,
600
9
			>,
601
9
		)>,
602
9
	) -> MayRevert<Registration<MaxAdditionalFields>> {
603
9
		if registration.is_none() {
604
1
			return Ok(Registration::<MaxAdditionalFields>::default());
605
8
		}
606
8

            
607
8
		let registration = registration.expect("none case checked above; qed").0;
608
8
		let mut identity_info = IdentityInfo::<MaxAdditionalFields> {
609
8
			additional: Default::default(),
610
8
			display: Self::data_to_output(registration.info.display),
611
8
			legal: Self::data_to_output(registration.info.legal),
612
8
			web: Self::data_to_output(registration.info.web),
613
8
			riot: Self::data_to_output(registration.info.riot),
614
8
			email: Self::data_to_output(registration.info.email),
615
8
			has_pgp_fingerprint: false,
616
8
			pgp_fingerprint: Default::default(),
617
8
			image: Self::data_to_output(registration.info.image),
618
8
			twitter: Self::data_to_output(registration.info.twitter),
619
8
		};
620
8

            
621
8
		let mut additional = Vec::new();
622
8
		for (k, v) in registration.info.additional.into_iter() {
623
2
			let k: Data = Self::data_to_output(k);
624
2
			let v: Data = Self::data_to_output(v);
625
2
			additional.push((k, v));
626
2
		}
627

            
628
8
		if let Some(pgp_fingerprint) = registration.info.pgp_fingerprint {
629
1
			identity_info.has_pgp_fingerprint = true;
630
1
			identity_info.pgp_fingerprint = pgp_fingerprint.into();
631
7
		}
632

            
633
8
		identity_info.additional = additional.into();
634
8

            
635
8
		let mut judgements = Vec::new();
636
8
		for (index, judgement) in registration.judgements.into_iter() {
637
7
			judgements.push((index, Self::judgement_to_output(judgement)));
638
7
		}
639

            
640
8
		let reg = Registration::<MaxAdditionalFields> {
641
8
			is_valid: true,
642
8
			judgements: judgements.into(),
643
8
			deposit: registration.deposit.into(),
644
8
			info: identity_info,
645
8
		};
646
8

            
647
8
		Ok(reg)
648
9
	}
649

            
650
7
	fn judgement_to_output(value: pallet_identity::Judgement<BalanceOf<Runtime>>) -> Judgement {
651
7
		let mut judgement = Judgement::default();
652
7

            
653
7
		match value {
654
1
			pallet_identity::Judgement::Unknown => {
655
1
				judgement.is_unknown = true;
656
1
			}
657
1
			pallet_identity::Judgement::FeePaid(balance) => {
658
1
				judgement.is_fee_paid = true;
659
1
				judgement.fee_paid_deposit = balance.into();
660
1
			}
661
1
			pallet_identity::Judgement::Reasonable => {
662
1
				judgement.is_reasonable = true;
663
1
			}
664
1
			pallet_identity::Judgement::KnownGood => {
665
1
				judgement.is_known_good = true;
666
1
			}
667
1
			pallet_identity::Judgement::OutOfDate => {
668
1
				judgement.is_out_of_date = true;
669
1
			}
670
1
			pallet_identity::Judgement::LowQuality => {
671
1
				judgement.is_low_quality = true;
672
1
			}
673
1
			pallet_identity::Judgement::Erroneous => {
674
1
				judgement.is_erroneous = true;
675
1
			}
676
		};
677

            
678
7
		judgement
679
7
	}
680

            
681
1
	fn judgment_to_input(
682
1
		value: Judgement,
683
1
	) -> Result<pallet_identity::Judgement<BalanceOf<Runtime>>, RevertReason> {
684
1
		if value.is_unknown {
685
			return Ok(pallet_identity::Judgement::Unknown);
686
1
		}
687
1

            
688
1
		if value.is_fee_paid {
689
			let amount: BalanceOf<Runtime> = value
690
				.fee_paid_deposit
691
				.try_into()
692
				.map_err(|_| RevertReason::value_is_too_large("fee_paid_deposit").into())?;
693

            
694
			return Ok(pallet_identity::Judgement::FeePaid(amount));
695
1
		}
696
1

            
697
1
		if value.is_reasonable {
698
1
			return Ok(pallet_identity::Judgement::Reasonable);
699
		}
700

            
701
		if value.is_known_good {
702
			return Ok(pallet_identity::Judgement::KnownGood);
703
		}
704

            
705
		if value.is_out_of_date {
706
			return Ok(pallet_identity::Judgement::OutOfDate);
707
		}
708

            
709
		if value.is_low_quality {
710
			return Ok(pallet_identity::Judgement::LowQuality);
711
		}
712

            
713
		if value.is_erroneous {
714
			return Ok(pallet_identity::Judgement::Erroneous);
715
		}
716

            
717
		return Err(RevertReason::custom("invalid"));
718
1
	}
719

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

            
747
61
		output
748
61
	}
749
}
750

            
751
97
#[derive(Default, Debug, Eq, PartialEq, solidity::Codec)]
752
pub struct Data {
753
	has_data: bool,
754
	value: BoundedBytes<ConstU32<32>>,
755
}
756

            
757
impl TryFrom<Data> for pallet_identity::Data {
758
	type Error = &'static str;
759

            
760
97
	fn try_from(value: Data) -> Result<Self, Self::Error> {
761
97
		if !value.has_data {
762
60
			return Ok(pallet_identity::Data::None);
763
37
		}
764
37

            
765
37
		let value: Vec<_> = value.value.into();
766
37
		let value: sp_runtime::BoundedVec<_, ConstU32<32>> =
767
37
			value.try_into().map_err(|_| "exceeded bounds")?;
768
37
		Ok(pallet_identity::Data::Raw(value))
769
97
	}
770
}
771

            
772
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
773
pub struct Additional {
774
	key: Data,
775
	value: Data,
776
}
777

            
778
12
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
779
pub struct IdentityInfo<FieldLimit> {
780
	additional: BoundedVec<(Data, Data), FieldLimit>,
781
	display: Data,
782
	legal: Data,
783
	web: Data,
784
	riot: Data,
785
	email: Data,
786
	has_pgp_fingerprint: bool,
787
	pgp_fingerprint: BoundedBytes<ConstU32<20>>,
788
	image: Data,
789
	twitter: Data,
790
}
791

            
792
impl<T> Default for IdentityInfo<T> {
793
13
	fn default() -> Self {
794
13
		Self {
795
13
			additional: Default::default(),
796
13
			display: Default::default(),
797
13
			legal: Default::default(),
798
13
			web: Default::default(),
799
13
			riot: Default::default(),
800
13
			email: Default::default(),
801
13
			has_pgp_fingerprint: Default::default(),
802
13
			pgp_fingerprint: Default::default(),
803
13
			image: Default::default(),
804
13
			twitter: Default::default(),
805
13
		}
806
13
	}
807
}
808

            
809
1
#[derive(Eq, PartialEq, Default, Debug, solidity::Codec)]
810
pub struct Judgement {
811
	is_unknown: bool,
812
	is_fee_paid: bool,
813
	fee_paid_deposit: U256,
814
	is_reasonable: bool,
815
	is_known_good: bool,
816
	is_out_of_date: bool,
817
	is_low_quality: bool,
818
	is_erroneous: bool,
819
}
820

            
821
#[derive(Eq, PartialEq, Debug, solidity::Codec)]
822
pub struct Registration<FieldLimit> {
823
	is_valid: bool,
824
	judgements: Vec<(u32, Judgement)>,
825
	deposit: U256,
826
	info: IdentityInfo<FieldLimit>,
827
}
828

            
829
impl<T> Default for Registration<T> {
830
2
	fn default() -> Self {
831
2
		Self {
832
2
			is_valid: false,
833
2
			judgements: Vec::new(),
834
2
			deposit: Default::default(),
835
2
			info: Default::default(),
836
2
		}
837
2
	}
838
}
839

            
840
#[derive(Default, Debug, solidity::Codec)]
841
pub struct SuperOf {
842
	is_valid: bool,
843
	account: Address,
844
	data: Data,
845
}
846

            
847
#[derive(Default, Debug, solidity::Codec)]
848
pub struct SubsOf {
849
	deposit: U256,
850
	accounts: Vec<Address>,
851
}
852

            
853
2
#[derive(Default, Debug, solidity::Codec)]
854
pub struct IdentityFields {
855
	display: bool,
856
	legal: bool,
857
	web: bool,
858
	riot: bool,
859
	email: bool,
860
	pgp_fingerprint: bool,
861
	image: bool,
862
	twitter: bool,
863
}
864

            
865
#[derive(Default, Debug, solidity::Codec)]
866
pub struct Registrar {
867
	is_valid: bool,
868
	index: u32,
869
	account: Address,
870
	fee: U256,
871
	fields: IdentityFields,
872
}