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
#![cfg_attr(not(feature = "std"), no_std)]
18

            
19
use account::SYSTEM_ACCOUNT_SIZE;
20
use fp_evm::PrecompileHandle;
21
use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo};
22
use frame_support::traits::{Currency, Polling};
23
use frame_system::pallet_prelude::BlockNumberFor;
24
use pallet_conviction_voting::Call as ConvictionVotingCall;
25
use pallet_conviction_voting::{
26
	AccountVote, Casting, ClassLocksFor, Conviction, Delegating, Tally, TallyOf, Vote, Voting,
27
	VotingFor,
28
};
29
use pallet_evm::{AddressMapping, Log};
30
use precompile_utils::prelude::*;
31
use sp_core::{Get, MaxEncodedLen, H160, H256, U256};
32
use sp_runtime::traits::{Dispatchable, StaticLookup};
33
use sp_std::marker::PhantomData;
34
use sp_std::vec::Vec;
35

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

            
41
type BalanceOf<Runtime> = <<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
42
	<Runtime as frame_system::Config>::AccountId,
43
>>::Balance;
44
type IndexOf<Runtime> = <<Runtime as pallet_conviction_voting::Config>::Polls as Polling<
45
	Tally<
46
		<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
47
			<Runtime as frame_system::Config>::AccountId,
48
		>>::Balance,
49
		<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
50
	>,
51
>>::Index;
52
type ClassOf<Runtime> = <<Runtime as pallet_conviction_voting::Config>::Polls as Polling<
53
	Tally<
54
		<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
55
			<Runtime as frame_system::Config>::AccountId,
56
		>>::Balance,
57
		<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
58
	>,
59
>>::Class;
60
type VotingOf<Runtime> = Voting<
61
	BalanceOf<Runtime>,
62
	<Runtime as frame_system::Config>::AccountId,
63
	BlockNumberFor<Runtime>,
64
	<<Runtime as pallet_conviction_voting::Config>::Polls as Polling<TallyOf<Runtime>>>::Index,
65
	<Runtime as pallet_conviction_voting::Config>::MaxVotes,
66
>;
67

            
68
/// Solidity selector of the Vote log, which is the Keccak of the Log signature.
69
pub(crate) const SELECTOR_LOG_VOTED: [u8; 32] =
70
	keccak256!("Voted(uint32,address,bool,uint256,uint8)");
71

            
72
/// Solidity selector of the Vote Split log, which is the Keccak of the Log signature.
73
pub(crate) const SELECTOR_LOG_VOTE_SPLIT: [u8; 32] =
74
	keccak256!("VoteSplit(uint32,address,uint256,uint256)");
75

            
76
/// Solidity selector of the Vote Split Abstained log, which is the Keccak of the Log signature.
77
pub(crate) const SELECTOR_LOG_VOTE_SPLIT_ABSTAINED: [u8; 32] =
78
	keccak256!("VoteSplitAbstained(uint32,address,uint256,uint256,uint256)");
79

            
80
/// Solidity selector of the VoteRemove log, which is the Keccak of the Log signature.
81
pub(crate) const SELECTOR_LOG_VOTE_REMOVED: [u8; 32] = keccak256!("VoteRemoved(uint32,address)");
82

            
83
/// Solidity selector of the SomeVoteRemove log, which is the Keccak of the Log signature.
84
pub(crate) const SELECTOR_LOG_VOTE_REMOVED_FOR_TRACK: [u8; 32] =
85
	keccak256!("VoteRemovedForTrack(uint32,uint16,address)");
86

            
87
/// Solidity selector of the VoteRemoveOther log, which is the Keccak of the Log signature.
88
pub(crate) const SELECTOR_LOG_VOTE_REMOVED_OTHER: [u8; 32] =
89
	keccak256!("VoteRemovedOther(uint32,address,address,uint16)");
90

            
91
/// Solidity selector of the Delegate log, which is the Keccak of the Log signature.
92
pub(crate) const SELECTOR_LOG_DELEGATED: [u8; 32] =
93
	keccak256!("Delegated(uint16,address,address,uint256,uint8)");
94

            
95
/// Solidity selector of the Undelegate log, which is the Keccak of the Log signature.
96
pub(crate) const SELECTOR_LOG_UNDELEGATED: [u8; 32] = keccak256!("Undelegated(uint16,address)");
97

            
98
/// Solidity selector of the Unlock log, which is the Keccak of the Log signature.
99
pub(crate) const SELECTOR_LOG_UNLOCKED: [u8; 32] = keccak256!("Unlocked(uint16,address)");
100

            
101
/// A precompile to wrap the functionality from pallet-conviction-voting.
102
pub struct ConvictionVotingPrecompile<Runtime>(PhantomData<Runtime>);
103

            
104
137
#[precompile_utils::precompile]
105
impl<Runtime> ConvictionVotingPrecompile<Runtime>
106
where
107
	Runtime: pallet_conviction_voting::Config + pallet_evm::Config + frame_system::Config,
108
	BalanceOf<Runtime>: TryFrom<U256> + Into<U256>,
109
	<Runtime as frame_system::Config>::RuntimeCall:
110
		Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
111
	<<Runtime as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin:
112
		From<Option<Runtime::AccountId>>,
113
	Runtime::AccountId: Into<H160>,
114
	<Runtime as frame_system::Config>::RuntimeCall: From<ConvictionVotingCall<Runtime>>,
115
	IndexOf<Runtime>: TryFrom<u32> + TryInto<u32>,
116
	ClassOf<Runtime>: TryFrom<u16> + TryInto<u16>,
117
	<Runtime as pallet_conviction_voting::Config>::Polls: Polling<
118
		Tally<
119
			<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
120
				<Runtime as frame_system::Config>::AccountId,
121
			>>::Balance,
122
			<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
123
		>,
124
	>,
125
	<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
126
{
127
	/// Internal helper function for vote* extrinsics exposed in this precompile.
128
12
	fn vote(
129
12
		handle: &mut impl PrecompileHandle,
130
12
		poll_index: u32,
131
12
		vote: AccountVote<U256>,
132
12
	) -> EvmResult {
133
12
		let caller = handle.context().caller;
134
12
		let (poll_index, vote, event) = Self::log_vote_event(handle, poll_index, vote)?;
135

            
136
12
		let origin = Runtime::AddressMapping::into_account_id(caller);
137
12
		let call = ConvictionVotingCall::<Runtime>::vote { poll_index, vote }.into();
138
12

            
139
12
		<RuntimeHelper<Runtime>>::try_dispatch(handle, Some(origin).into(), call, 0)?;
140

            
141
12
		event.record(handle)?;
142

            
143
12
		Ok(())
144
12
	}
145

            
146
	/// Vote yes in a poll.
147
	///
148
	/// Parameters:
149
	/// * poll_index: Index of poll
150
	/// * vote_amount: Balance locked for vote
151
	/// * conviction: Conviction multiplier for length of vote lock
152
	#[precompile::public("voteYes(uint32,uint256,uint8)")]
153
7
	fn vote_yes(
154
7
		handle: &mut impl PrecompileHandle,
155
7
		poll_index: u32,
156
7
		vote_amount: U256,
157
7
		conviction: u8,
158
7
	) -> EvmResult {
159
7
		Self::vote(
160
7
			handle,
161
7
			poll_index,
162
7
			AccountVote::Standard {
163
7
				vote: Vote {
164
7
					aye: true,
165
7
					conviction: Self::u8_to_conviction(conviction).in_field("conviction")?,
166
				},
167
7
				balance: vote_amount,
168
			},
169
		)
170
7
	}
171

            
172
	/// Vote no in a poll.
173
	///
174
	/// Parameters:
175
	/// * poll_index: Index of poll
176
	/// * vote_amount: Balance locked for vote
177
	/// * conviction: Conviction multiplier for length of vote lock
178
	#[precompile::public("voteNo(uint32,uint256,uint8)")]
179
1
	fn vote_no(
180
1
		handle: &mut impl PrecompileHandle,
181
1
		poll_index: u32,
182
1
		vote_amount: U256,
183
1
		conviction: u8,
184
1
	) -> EvmResult {
185
1
		Self::vote(
186
1
			handle,
187
1
			poll_index,
188
1
			AccountVote::Standard {
189
1
				vote: Vote {
190
1
					aye: false,
191
1
					conviction: Self::u8_to_conviction(conviction).in_field("conviction")?,
192
				},
193
1
				balance: vote_amount,
194
			},
195
		)
196
1
	}
197

            
198
	/// Vote split in a poll.
199
	///
200
	/// Parameters:
201
	/// * poll_index: Index of poll
202
	/// * aye: Balance locked for aye vote
203
	/// * nay: Balance locked for nay vote
204
	#[precompile::public("voteSplit(uint32,uint256,uint256)")]
205
2
	fn vote_split(
206
2
		handle: &mut impl PrecompileHandle,
207
2
		poll_index: u32,
208
2
		aye: U256,
209
2
		nay: U256,
210
2
	) -> EvmResult {
211
2
		Self::vote(handle, poll_index, AccountVote::Split { aye, nay })
212
2
	}
213

            
214
	/// Vote split in a poll.
215
	///
216
	/// Parameters:
217
	/// * poll_index: Index of poll
218
	/// * aye: Balance locked for aye vote
219
	/// * nay: Balance locked for nay vote
220
	#[precompile::public("voteSplitAbstain(uint32,uint256,uint256,uint256)")]
221
2
	fn vote_split_abstain(
222
2
		handle: &mut impl PrecompileHandle,
223
2
		poll_index: u32,
224
2
		aye: U256,
225
2
		nay: U256,
226
2
		abstain: U256,
227
2
	) -> EvmResult {
228
2
		Self::vote(
229
2
			handle,
230
2
			poll_index,
231
2
			AccountVote::SplitAbstain { aye, nay, abstain },
232
2
		)
233
2
	}
234

            
235
	#[precompile::public("removeVote(uint32)")]
236
2
	fn remove_vote(handle: &mut impl PrecompileHandle, poll_index: u32) -> EvmResult {
237
2
		Self::rm_vote(handle, poll_index, None)
238
2
	}
239

            
240
	#[precompile::public("removeVoteForTrack(uint32,uint16)")]
241
1
	fn remove_vote_for_track(
242
1
		handle: &mut impl PrecompileHandle,
243
1
		poll_index: u32,
244
1
		track_id: u16,
245
1
	) -> EvmResult {
246
1
		Self::rm_vote(handle, poll_index, Some(track_id))
247
1
	}
248

            
249
	/// Helper function for common code between remove_vote and remove_some_vote
250
3
	fn rm_vote(
251
3
		handle: &mut impl PrecompileHandle,
252
3
		poll_index: u32,
253
3
		maybe_track_id: Option<u16>,
254
3
	) -> EvmResult {
255
3
		let caller = handle.context().caller;
256
3
		let index = Self::u32_to_index(poll_index).in_field("pollIndex")?;
257
3
		let (event, class) = if let Some(track_id) = maybe_track_id {
258
1
			log::trace!(
259
				target: "conviction-voting-precompile",
260
				"Removing vote from poll {:?} for track {:?}",
261
				index,
262
				track_id,
263
			);
264
			(
265
1
				log2(
266
1
					handle.context().address,
267
1
					SELECTOR_LOG_VOTE_REMOVED_FOR_TRACK,
268
1
					H256::from_low_u64_be(poll_index as u64),
269
1
					solidity::encode_event_data((track_id, Address(caller))),
270
1
				),
271
1
				Some(Self::u16_to_track_id(track_id).in_field("trackId")?),
272
			)
273
		} else {
274
2
			log::trace!(
275
				target: "conviction-voting-precompile",
276
				"Removing vote from poll {:?}",
277
				index,
278
			);
279
2
			(
280
2
				log2(
281
2
					handle.context().address,
282
2
					SELECTOR_LOG_VOTE_REMOVED,
283
2
					H256::from_low_u64_be(poll_index as u64),
284
2
					solidity::encode_event_data(Address(caller)),
285
2
				),
286
2
				None,
287
2
			)
288
		};
289

            
290
3
		let origin = Runtime::AddressMapping::into_account_id(caller);
291
3
		let call = ConvictionVotingCall::<Runtime>::remove_vote { class, index };
292
3

            
293
3
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
294

            
295
3
		event.record(handle)?;
296

            
297
3
		Ok(())
298
3
	}
299

            
300
	#[precompile::public("removeOtherVote(address,uint16,uint32)")]
301
1
	fn remove_other_vote(
302
1
		handle: &mut impl PrecompileHandle,
303
1
		target: Address,
304
1
		track_id: u16,
305
1
		poll_index: u32,
306
1
	) -> EvmResult {
307
1
		let caller = handle.context().caller;
308
1

            
309
1
		let event = log2(
310
1
			handle.context().address,
311
1
			SELECTOR_LOG_VOTE_REMOVED_OTHER,
312
1
			H256::from_low_u64_be(poll_index as u64), // poll index,
313
1
			solidity::encode_event_data((Address(caller), target, track_id)),
314
1
		);
315
1
		handle.record_log_costs(&[&event])?;
316

            
317
1
		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
318
1
		let index = Self::u32_to_index(poll_index).in_field("pollIndex")?;
319

            
320
1
		let target = Runtime::AddressMapping::into_account_id(target.into());
321
1
		let target: <Runtime::Lookup as StaticLookup>::Source =
322
1
			Runtime::Lookup::unlookup(target.clone());
323
1

            
324
1
		log::trace!(
325
			target: "conviction-voting-precompile",
326
			"Removing other vote from poll {:?}",
327
			index
328
		);
329

            
330
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
331
1
		let call = ConvictionVotingCall::<Runtime>::remove_other_vote {
332
1
			target,
333
1
			class,
334
1
			index,
335
1
		};
336
1

            
337
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
338

            
339
1
		event.record(handle)?;
340

            
341
1
		Ok(())
342
1
	}
343

            
344
	#[precompile::public("delegate(uint16,address,uint8,uint256)")]
345
1
	fn delegate(
346
1
		handle: &mut impl PrecompileHandle,
347
1
		track_id: u16,
348
1
		representative: Address,
349
1
		conviction: u8,
350
1
		amount: U256,
351
1
	) -> EvmResult {
352
1
		let caller = handle.context().caller;
353
1

            
354
1
		let event = log2(
355
1
			handle.context().address,
356
1
			SELECTOR_LOG_DELEGATED,
357
1
			H256::from_low_u64_be(track_id as u64), // track id,
358
1
			solidity::encode_event_data((Address(caller), representative, amount, conviction)),
359
1
		);
360
1
		handle.record_log_costs(&[&event])?;
361

            
362
1
		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
363
1
		let amount = Self::u256_to_amount(amount).in_field("amount")?;
364
1
		let conviction = Self::u8_to_conviction(conviction).in_field("conviction")?;
365

            
366
1
		log::trace!(target: "conviction-voting-precompile",
367
			"Delegating vote to {:?} with balance {:?} and conviction {:?}",
368
			representative, amount, conviction
369
		);
370

            
371
1
		let representative = Runtime::AddressMapping::into_account_id(representative.into());
372
1
		let to: <Runtime::Lookup as StaticLookup>::Source =
373
1
			Runtime::Lookup::unlookup(representative.clone());
374
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
375
1
		let call = ConvictionVotingCall::<Runtime>::delegate {
376
1
			class,
377
1
			to,
378
1
			conviction,
379
1
			balance: amount,
380
1
		};
381
1

            
382
1
		RuntimeHelper::<Runtime>::try_dispatch(
383
1
			handle,
384
1
			Some(origin).into(),
385
1
			call,
386
1
			SYSTEM_ACCOUNT_SIZE,
387
1
		)?;
388

            
389
1
		event.record(handle)?;
390

            
391
1
		Ok(())
392
1
	}
393

            
394
	#[precompile::public("undelegate(uint16)")]
395
1
	fn undelegate(handle: &mut impl PrecompileHandle, track_id: u16) -> EvmResult {
396
1
		let caller = handle.context().caller;
397
1

            
398
1
		let event = log2(
399
1
			handle.context().address,
400
1
			SELECTOR_LOG_UNDELEGATED,
401
1
			H256::from_low_u64_be(track_id as u64), // track id,
402
1
			solidity::encode_event_data(Address(caller)),
403
1
		);
404
1
		handle.record_log_costs(&[&event])?;
405

            
406
1
		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
407
1
		let origin = Runtime::AddressMapping::into_account_id(caller);
408
1
		let call = ConvictionVotingCall::<Runtime>::undelegate { class };
409
1

            
410
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
411

            
412
1
		event.record(handle)?;
413

            
414
1
		Ok(())
415
1
	}
416

            
417
	#[precompile::public("unlock(uint16,address)")]
418
1
	fn unlock(handle: &mut impl PrecompileHandle, track_id: u16, target: Address) -> EvmResult {
419
1
		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
420

            
421
1
		let event = log2(
422
1
			handle.context().address,
423
1
			SELECTOR_LOG_UNLOCKED,
424
1
			H256::from_low_u64_be(track_id as u64), // track id,
425
1
			solidity::encode_event_data(target),
426
1
		);
427
1
		handle.record_log_costs(&[&event])?;
428

            
429
1
		let target: H160 = target.into();
430
1
		let target = Runtime::AddressMapping::into_account_id(target);
431
1
		let target: <Runtime::Lookup as StaticLookup>::Source =
432
1
			Runtime::Lookup::unlookup(target.clone());
433
1

            
434
1
		log::trace!(
435
			target: "conviction-voting-precompile",
436
			"Unlocking conviction-voting tokens for {:?}", target
437
		);
438

            
439
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
440
1
		let call = ConvictionVotingCall::<Runtime>::unlock { class, target };
441
1

            
442
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
443

            
444
1
		event.record(handle)?;
445

            
446
1
		Ok(())
447
1
	}
448

            
449
	#[precompile::public("votingFor(address,uint16)")]
450
	#[precompile::view]
451
3
	fn voting_for(
452
3
		handle: &mut impl PrecompileHandle,
453
3
		who: Address,
454
3
		track_id: u16,
455
3
	) -> EvmResult<OutputVotingFor> {
456
3
		// VotingFor: Twox64Concat(8) + 20 + Twox64Concat(8) + TransInfo::Id(2) + VotingOf
457
3
		handle.record_db_read::<Runtime>(38 + VotingOf::<Runtime>::max_encoded_len())?;
458

            
459
3
		let who = Runtime::AddressMapping::into_account_id(who.into());
460
3
		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
461

            
462
3
		let voting = <VotingFor<Runtime>>::get(&who, &class);
463
3

            
464
3
		Ok(Self::voting_to_output(voting)?)
465
3
	}
466

            
467
	#[precompile::public("classLocksFor(address)")]
468
	#[precompile::view]
469
1
	fn class_locks_for(
470
1
		handle: &mut impl PrecompileHandle,
471
1
		who: Address,
472
1
	) -> EvmResult<Vec<OutputClassLock>> {
473
1
		// ClassLocksFor: Twox64Concat(8) + 20 + BoundedVec(TransInfo::Id(2) * ClassCountOf)
474
1
		handle.record_db_read::<Runtime>(
475
1
			28 + ((2 * frame_support::traits::ClassCountOf::<
476
1
				<Runtime as pallet_conviction_voting::Config>::Polls,
477
1
				Tally<
478
1
					<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
479
1
						<Runtime as frame_system::Config>::AccountId,
480
1
					>>::Balance,
481
1
					<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
482
1
				>,
483
1
			>::get()) as usize),
484
1
		)?;
485

            
486
1
		let who = Runtime::AddressMapping::into_account_id(who.into());
487
1

            
488
1
		let class_locks_for = <ClassLocksFor<Runtime>>::get(&who);
489
1
		let mut output = Vec::new();
490
2
		for (track_id, amount) in class_locks_for {
491
1
			output.push(OutputClassLock {
492
1
				track: Self::track_id_to_u16(track_id)?,
493
1
				amount: amount.into(),
494
			});
495
		}
496

            
497
1
		Ok(output)
498
1
	}
499

            
500
9
	fn u8_to_conviction(conviction: u8) -> MayRevert<Conviction> {
501
9
		conviction
502
9
			.try_into()
503
9
			.map_err(|_| RevertReason::custom("Must be an integer between 0 and 6 included").into())
504
9
	}
505

            
506
16
	fn u32_to_index(index: u32) -> MayRevert<IndexOf<Runtime>> {
507
16
		index
508
16
			.try_into()
509
16
			.map_err(|_| RevertReason::value_is_too_large("index type").into())
510
16
	}
511

            
512
8
	fn u16_to_track_id(class: u16) -> MayRevert<ClassOf<Runtime>> {
513
8
		class
514
8
			.try_into()
515
8
			.map_err(|_| RevertReason::value_is_too_large("trackId type").into())
516
8
	}
517

            
518
1
	fn track_id_to_u16(class: ClassOf<Runtime>) -> MayRevert<u16> {
519
1
		class
520
1
			.try_into()
521
1
			.map_err(|_| RevertReason::value_is_too_large("trackId type").into())
522
1
	}
523

            
524
19
	fn u256_to_amount(value: U256) -> MayRevert<BalanceOf<Runtime>> {
525
19
		value
526
19
			.try_into()
527
19
			.map_err(|_| RevertReason::value_is_too_large("balance type").into())
528
19
	}
529

            
530
12
	fn log_vote_event(
531
12
		handle: &mut impl PrecompileHandle,
532
12
		poll_index: u32,
533
12
		vote: AccountVote<U256>,
534
12
	) -> EvmResult<(IndexOf<Runtime>, AccountVote<BalanceOf<Runtime>>, Log)> {
535
12
		let (contract_addr, caller) = (handle.context().address, handle.context().caller);
536
12
		let (vote, event) = match vote {
537
8
			AccountVote::Standard { vote, balance } => {
538
8
				let event = log2(
539
8
					contract_addr,
540
8
					SELECTOR_LOG_VOTED,
541
8
					H256::from_low_u64_be(poll_index as u64),
542
8
					solidity::encode_event_data((
543
8
						Address(caller),
544
8
						vote.aye,
545
8
						balance,
546
8
						u8::from(vote.conviction),
547
8
					)),
548
8
				);
549
8
				(
550
8
					AccountVote::Standard {
551
8
						vote,
552
8
						balance: Self::u256_to_amount(balance).in_field("voteAmount")?,
553
					},
554
8
					event,
555
				)
556
			}
557
2
			AccountVote::Split { aye, nay } => {
558
2
				let event = log2(
559
2
					contract_addr,
560
2
					SELECTOR_LOG_VOTE_SPLIT,
561
2
					H256::from_low_u64_be(poll_index as u64),
562
2
					solidity::encode_event_data((Address(caller), aye, nay)),
563
2
				);
564
2
				(
565
2
					AccountVote::Split {
566
2
						aye: Self::u256_to_amount(aye).in_field("aye")?,
567
2
						nay: Self::u256_to_amount(nay).in_field("nay")?,
568
					},
569
2
					event,
570
				)
571
			}
572
2
			AccountVote::SplitAbstain { aye, nay, abstain } => {
573
2
				let event = log2(
574
2
					contract_addr,
575
2
					SELECTOR_LOG_VOTE_SPLIT_ABSTAINED,
576
2
					H256::from_low_u64_be(poll_index as u64),
577
2
					solidity::encode_event_data((Address(caller), aye, nay, abstain)),
578
2
				);
579
2
				(
580
2
					AccountVote::SplitAbstain {
581
2
						aye: Self::u256_to_amount(aye).in_field("aye")?,
582
2
						nay: Self::u256_to_amount(nay).in_field("nay")?,
583
2
						abstain: Self::u256_to_amount(abstain).in_field("abstain")?,
584
					},
585
2
					event,
586
				)
587
			}
588
		};
589
12
		handle.record_log_costs(&[&event])?;
590
12
		Ok((Self::u32_to_index(poll_index)?, vote, event))
591
12
	}
592

            
593
3
	fn voting_to_output(voting: VotingOf<Runtime>) -> MayRevert<OutputVotingFor> {
594
3
		let output = match voting {
595
			Voting::Casting(Casting {
596
3
				votes,
597
3
				delegations,
598
3
				prior,
599
3
			}) => {
600
3
				let mut output_votes = Vec::new();
601
6
				for (poll_index, account_vote) in votes {
602
3
					let poll_index: u32 = poll_index
603
3
						.try_into()
604
3
						.map_err(|_| RevertReason::value_is_too_large("index type"))?;
605
3
					let account_vote = match account_vote {
606
1
						AccountVote::Standard { vote, balance } => OutputAccountVote {
607
1
							is_standard: true,
608
1
							standard: StandardVote {
609
1
								vote: OutputVote {
610
1
									aye: vote.aye,
611
1
									conviction: vote.conviction.into(),
612
1
								},
613
1
								balance: balance.into(),
614
1
							},
615
1
							..Default::default()
616
1
						},
617
1
						AccountVote::Split { aye, nay } => OutputAccountVote {
618
1
							is_split: true,
619
1
							split: SplitVote {
620
1
								aye: aye.into(),
621
1
								nay: nay.into(),
622
1
							},
623
1
							..Default::default()
624
1
						},
625
1
						AccountVote::SplitAbstain { aye, nay, abstain } => OutputAccountVote {
626
1
							is_split_abstain: true,
627
1
							split_abstain: SplitAbstainVote {
628
1
								aye: aye.into(),
629
1
								nay: nay.into(),
630
1
								abstain: abstain.into(),
631
1
							},
632
1
							..Default::default()
633
1
						},
634
					};
635

            
636
3
					output_votes.push(PollAccountVote {
637
3
						poll_index,
638
3
						account_vote,
639
3
					});
640
				}
641

            
642
3
				OutputVotingFor {
643
3
					is_casting: true,
644
3
					casting: OutputCasting {
645
3
						votes: output_votes,
646
3
						delegations: Delegations {
647
3
							votes: delegations.votes.into(),
648
3
							capital: delegations.capital.into(),
649
3
						},
650
3
						prior: PriorLock {
651
3
							balance: prior.locked().into(),
652
3
						},
653
3
					},
654
3
					..Default::default()
655
3
				}
656
			}
657
			Voting::Delegating(Delegating {
658
				balance,
659
				target,
660
				conviction,
661
				delegations,
662
				prior,
663
			}) => OutputVotingFor {
664
				is_delegating: true,
665
				delegating: OutputDelegating {
666
					balance: balance.into(),
667
					target: Address(target.into()),
668
					conviction: conviction.into(),
669
					delegations: Delegations {
670
						votes: delegations.votes.into(),
671
						capital: delegations.capital.into(),
672
					},
673
					prior: PriorLock {
674
						balance: prior.locked().into(),
675
					},
676
				},
677
				..Default::default()
678
			},
679
		};
680

            
681
3
		Ok(output)
682
3
	}
683
}
684

            
685
2
#[derive(Default, solidity::Codec)]
686
pub struct OutputClassLock {
687
	track: u16,
688
	amount: U256,
689
}
690

            
691
6
#[derive(Default, solidity::Codec)]
692
pub struct OutputVotingFor {
693
	is_casting: bool,
694
	is_delegating: bool,
695
	casting: OutputCasting,
696
	delegating: OutputDelegating,
697
}
698

            
699
12
#[derive(Default, solidity::Codec)]
700
pub struct OutputCasting {
701
	votes: Vec<PollAccountVote>,
702
	delegations: Delegations,
703
	prior: PriorLock,
704
}
705

            
706
6
#[derive(Default, solidity::Codec)]
707
pub struct PollAccountVote {
708
	poll_index: u32,
709
	account_vote: OutputAccountVote,
710
}
711

            
712
12
#[derive(Default, solidity::Codec)]
713
pub struct OutputDelegating {
714
	balance: U256,
715
	target: Address,
716
	conviction: u8,
717
	delegations: Delegations,
718
	prior: PriorLock,
719
}
720

            
721
12
#[derive(Default, solidity::Codec)]
722
pub struct OutputAccountVote {
723
	is_standard: bool,
724
	is_split: bool,
725
	is_split_abstain: bool,
726
	standard: StandardVote,
727
	split: SplitVote,
728
	split_abstain: SplitAbstainVote,
729
}
730

            
731
18
#[derive(Default, solidity::Codec)]
732
pub struct StandardVote {
733
	vote: OutputVote,
734
	balance: U256,
735
}
736

            
737
24
#[derive(Default, solidity::Codec)]
738
pub struct OutputVote {
739
	aye: bool,
740
	conviction: u8,
741
}
742

            
743
18
#[derive(Default, solidity::Codec)]
744
pub struct SplitVote {
745
	aye: U256,
746
	nay: U256,
747
}
748

            
749
18
#[derive(Default, solidity::Codec)]
750
pub struct SplitAbstainVote {
751
	aye: U256,
752
	nay: U256,
753
	abstain: U256,
754
}
755

            
756
36
#[derive(Default, solidity::Codec)]
757
pub struct Delegations {
758
	votes: U256,
759
	capital: U256,
760
}
761

            
762
36
#[derive(Default, solidity::Codec)]
763
pub struct PriorLock {
764
	balance: U256,
765
}