pallet_evm_precompile_conviction_voting/
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#![cfg_attr(not(feature = "std"), no_std)]
18
19use account::SYSTEM_ACCOUNT_SIZE;
20use fp_evm::PrecompileHandle;
21use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo};
22use frame_support::traits::{Currency, Polling};
23use pallet_conviction_voting::Call as ConvictionVotingCall;
24use pallet_conviction_voting::{
25	AccountVote, Casting, ClassLocksFor, Conviction, Delegating, Tally, TallyOf, Vote, Voting,
26	VotingFor,
27};
28use pallet_evm::{AddressMapping, Log};
29use precompile_utils::prelude::*;
30use sp_core::{Get, MaxEncodedLen, H160, H256, U256};
31use sp_runtime::traits::{Dispatchable, StaticLookup};
32use sp_std::marker::PhantomData;
33use sp_std::vec::Vec;
34
35#[cfg(test)]
36mod mock;
37#[cfg(test)]
38mod tests;
39
40type BalanceOf<Runtime> = <<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
41	<Runtime as frame_system::Config>::AccountId,
42>>::Balance;
43type IndexOf<Runtime> = <<Runtime as pallet_conviction_voting::Config>::Polls as Polling<
44	Tally<
45		<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
46			<Runtime as frame_system::Config>::AccountId,
47		>>::Balance,
48		<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
49	>,
50>>::Index;
51type ClassOf<Runtime> = <<Runtime as pallet_conviction_voting::Config>::Polls as Polling<
52	Tally<
53		<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
54			<Runtime as frame_system::Config>::AccountId,
55		>>::Balance,
56		<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
57	>,
58>>::Class;
59type VotingOf<Runtime, Instance = ()> = Voting<
60	BalanceOf<Runtime>,
61	<Runtime as frame_system::Config>::AccountId,
62	pallet_conviction_voting::BlockNumberFor<Runtime, Instance>,
63	<<Runtime as pallet_conviction_voting::Config>::Polls as Polling<TallyOf<Runtime>>>::Index,
64	<Runtime as pallet_conviction_voting::Config>::MaxVotes,
65>;
66
67/// Solidity selector of the Vote log, which is the Keccak of the Log signature.
68pub(crate) const SELECTOR_LOG_VOTED: [u8; 32] =
69	keccak256!("Voted(uint32,address,bool,uint256,uint8)");
70
71/// Solidity selector of the Vote Split log, which is the Keccak of the Log signature.
72pub(crate) const SELECTOR_LOG_VOTE_SPLIT: [u8; 32] =
73	keccak256!("VoteSplit(uint32,address,uint256,uint256)");
74
75/// Solidity selector of the Vote Split Abstained log, which is the Keccak of the Log signature.
76pub(crate) const SELECTOR_LOG_VOTE_SPLIT_ABSTAINED: [u8; 32] =
77	keccak256!("VoteSplitAbstained(uint32,address,uint256,uint256,uint256)");
78
79/// Solidity selector of the VoteRemove log, which is the Keccak of the Log signature.
80pub(crate) const SELECTOR_LOG_VOTE_REMOVED: [u8; 32] = keccak256!("VoteRemoved(uint32,address)");
81
82/// Solidity selector of the SomeVoteRemove log, which is the Keccak of the Log signature.
83pub(crate) const SELECTOR_LOG_VOTE_REMOVED_FOR_TRACK: [u8; 32] =
84	keccak256!("VoteRemovedForTrack(uint32,uint16,address)");
85
86/// Solidity selector of the VoteRemoveOther log, which is the Keccak of the Log signature.
87pub(crate) const SELECTOR_LOG_VOTE_REMOVED_OTHER: [u8; 32] =
88	keccak256!("VoteRemovedOther(uint32,address,address,uint16)");
89
90/// Solidity selector of the Delegate log, which is the Keccak of the Log signature.
91pub(crate) const SELECTOR_LOG_DELEGATED: [u8; 32] =
92	keccak256!("Delegated(uint16,address,address,uint256,uint8)");
93
94/// Solidity selector of the Undelegate log, which is the Keccak of the Log signature.
95pub(crate) const SELECTOR_LOG_UNDELEGATED: [u8; 32] = keccak256!("Undelegated(uint16,address)");
96
97/// Solidity selector of the Unlock log, which is the Keccak of the Log signature.
98pub(crate) const SELECTOR_LOG_UNLOCKED: [u8; 32] = keccak256!("Unlocked(uint16,address)");
99
100/// A precompile to wrap the functionality from pallet-conviction-voting.
101pub struct ConvictionVotingPrecompile<Runtime>(PhantomData<Runtime>);
102
103#[precompile_utils::precompile]
104impl<Runtime> ConvictionVotingPrecompile<Runtime>
105where
106	Runtime: pallet_conviction_voting::Config + pallet_evm::Config + frame_system::Config,
107	BalanceOf<Runtime>: TryFrom<U256> + Into<U256>,
108	<Runtime as frame_system::Config>::RuntimeCall:
109		Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
110	<<Runtime as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin:
111		From<Option<Runtime::AccountId>>,
112	Runtime::AccountId: Into<H160>,
113	<Runtime as frame_system::Config>::RuntimeCall: From<ConvictionVotingCall<Runtime>>,
114	IndexOf<Runtime>: TryFrom<u32> + TryInto<u32>,
115	ClassOf<Runtime>: TryFrom<u16> + TryInto<u16>,
116	<Runtime as pallet_conviction_voting::Config>::Polls: Polling<
117		Tally<
118			<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
119				<Runtime as frame_system::Config>::AccountId,
120			>>::Balance,
121			<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
122		>,
123	>,
124	<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
125{
126	/// Internal helper function for vote* extrinsics exposed in this precompile.
127	fn vote(
128		handle: &mut impl PrecompileHandle,
129		poll_index: u32,
130		vote: AccountVote<U256>,
131	) -> EvmResult {
132		let caller = handle.context().caller;
133		let (poll_index, vote, event) = Self::log_vote_event(handle, poll_index, vote)?;
134
135		let origin = Runtime::AddressMapping::into_account_id(caller);
136		let call = ConvictionVotingCall::<Runtime>::vote { poll_index, vote }.into();
137
138		<RuntimeHelper<Runtime>>::try_dispatch(handle, Some(origin).into(), call, 0)?;
139
140		event.record(handle)?;
141
142		Ok(())
143	}
144
145	/// Vote yes in a poll.
146	///
147	/// Parameters:
148	/// * poll_index: Index of poll
149	/// * vote_amount: Balance locked for vote
150	/// * conviction: Conviction multiplier for length of vote lock
151	#[precompile::public("voteYes(uint32,uint256,uint8)")]
152	fn vote_yes(
153		handle: &mut impl PrecompileHandle,
154		poll_index: u32,
155		vote_amount: U256,
156		conviction: u8,
157	) -> EvmResult {
158		Self::vote(
159			handle,
160			poll_index,
161			AccountVote::Standard {
162				vote: Vote {
163					aye: true,
164					conviction: Self::u8_to_conviction(conviction).in_field("conviction")?,
165				},
166				balance: vote_amount,
167			},
168		)
169	}
170
171	/// Vote no in a poll.
172	///
173	/// Parameters:
174	/// * poll_index: Index of poll
175	/// * vote_amount: Balance locked for vote
176	/// * conviction: Conviction multiplier for length of vote lock
177	#[precompile::public("voteNo(uint32,uint256,uint8)")]
178	fn vote_no(
179		handle: &mut impl PrecompileHandle,
180		poll_index: u32,
181		vote_amount: U256,
182		conviction: u8,
183	) -> EvmResult {
184		Self::vote(
185			handle,
186			poll_index,
187			AccountVote::Standard {
188				vote: Vote {
189					aye: false,
190					conviction: Self::u8_to_conviction(conviction).in_field("conviction")?,
191				},
192				balance: vote_amount,
193			},
194		)
195	}
196
197	/// Vote split in a poll.
198	///
199	/// Parameters:
200	/// * poll_index: Index of poll
201	/// * aye: Balance locked for aye vote
202	/// * nay: Balance locked for nay vote
203	#[precompile::public("voteSplit(uint32,uint256,uint256)")]
204	fn vote_split(
205		handle: &mut impl PrecompileHandle,
206		poll_index: u32,
207		aye: U256,
208		nay: U256,
209	) -> EvmResult {
210		Self::vote(handle, poll_index, AccountVote::Split { aye, nay })
211	}
212
213	/// Vote split in a poll.
214	///
215	/// Parameters:
216	/// * poll_index: Index of poll
217	/// * aye: Balance locked for aye vote
218	/// * nay: Balance locked for nay vote
219	#[precompile::public("voteSplitAbstain(uint32,uint256,uint256,uint256)")]
220	fn vote_split_abstain(
221		handle: &mut impl PrecompileHandle,
222		poll_index: u32,
223		aye: U256,
224		nay: U256,
225		abstain: U256,
226	) -> EvmResult {
227		Self::vote(
228			handle,
229			poll_index,
230			AccountVote::SplitAbstain { aye, nay, abstain },
231		)
232	}
233
234	#[precompile::public("removeVote(uint32)")]
235	fn remove_vote(handle: &mut impl PrecompileHandle, poll_index: u32) -> EvmResult {
236		Self::rm_vote(handle, poll_index, None)
237	}
238
239	#[precompile::public("removeVoteForTrack(uint32,uint16)")]
240	fn remove_vote_for_track(
241		handle: &mut impl PrecompileHandle,
242		poll_index: u32,
243		track_id: u16,
244	) -> EvmResult {
245		Self::rm_vote(handle, poll_index, Some(track_id))
246	}
247
248	/// Helper function for common code between remove_vote and remove_some_vote
249	fn rm_vote(
250		handle: &mut impl PrecompileHandle,
251		poll_index: u32,
252		maybe_track_id: Option<u16>,
253	) -> EvmResult {
254		let caller = handle.context().caller;
255		let index = Self::u32_to_index(poll_index).in_field("pollIndex")?;
256		let (event, class) = if let Some(track_id) = maybe_track_id {
257			log::trace!(
258				target: "conviction-voting-precompile",
259				"Removing vote from poll {:?} for track {:?}",
260				index,
261				track_id,
262			);
263			(
264				log2(
265					handle.context().address,
266					SELECTOR_LOG_VOTE_REMOVED_FOR_TRACK,
267					H256::from_low_u64_be(poll_index as u64),
268					solidity::encode_event_data((track_id, Address(caller))),
269				),
270				Some(Self::u16_to_track_id(track_id).in_field("trackId")?),
271			)
272		} else {
273			log::trace!(
274				target: "conviction-voting-precompile",
275				"Removing vote from poll {:?}",
276				index,
277			);
278			(
279				log2(
280					handle.context().address,
281					SELECTOR_LOG_VOTE_REMOVED,
282					H256::from_low_u64_be(poll_index as u64),
283					solidity::encode_event_data(Address(caller)),
284				),
285				None,
286			)
287		};
288
289		let origin = Runtime::AddressMapping::into_account_id(caller);
290		let call = ConvictionVotingCall::<Runtime>::remove_vote { class, index };
291
292		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
293
294		event.record(handle)?;
295
296		Ok(())
297	}
298
299	#[precompile::public("removeOtherVote(address,uint16,uint32)")]
300	fn remove_other_vote(
301		handle: &mut impl PrecompileHandle,
302		target: Address,
303		track_id: u16,
304		poll_index: u32,
305	) -> EvmResult {
306		let caller = handle.context().caller;
307
308		let event = log2(
309			handle.context().address,
310			SELECTOR_LOG_VOTE_REMOVED_OTHER,
311			H256::from_low_u64_be(poll_index as u64), // poll index,
312			solidity::encode_event_data((Address(caller), target, track_id)),
313		);
314		handle.record_log_costs(&[&event])?;
315
316		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
317		let index = Self::u32_to_index(poll_index).in_field("pollIndex")?;
318
319		let target = Runtime::AddressMapping::into_account_id(target.into());
320		let target: <Runtime::Lookup as StaticLookup>::Source =
321			Runtime::Lookup::unlookup(target.clone());
322
323		log::trace!(
324			target: "conviction-voting-precompile",
325			"Removing other vote from poll {:?}",
326			index
327		);
328
329		let origin = Runtime::AddressMapping::into_account_id(caller);
330		let call = ConvictionVotingCall::<Runtime>::remove_other_vote {
331			target,
332			class,
333			index,
334		};
335
336		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
337
338		event.record(handle)?;
339
340		Ok(())
341	}
342
343	#[precompile::public("delegate(uint16,address,uint8,uint256)")]
344	fn delegate(
345		handle: &mut impl PrecompileHandle,
346		track_id: u16,
347		representative: Address,
348		conviction: u8,
349		amount: U256,
350	) -> EvmResult {
351		let caller = handle.context().caller;
352
353		let event = log2(
354			handle.context().address,
355			SELECTOR_LOG_DELEGATED,
356			H256::from_low_u64_be(track_id as u64), // track id,
357			solidity::encode_event_data((Address(caller), representative, amount, conviction)),
358		);
359		handle.record_log_costs(&[&event])?;
360
361		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
362		let amount = Self::u256_to_amount(amount).in_field("amount")?;
363		let conviction = Self::u8_to_conviction(conviction).in_field("conviction")?;
364
365		log::trace!(target: "conviction-voting-precompile",
366			"Delegating vote to {:?} with balance {:?} and conviction {:?}",
367			representative, amount, conviction
368		);
369
370		let representative = Runtime::AddressMapping::into_account_id(representative.into());
371		let to: <Runtime::Lookup as StaticLookup>::Source =
372			Runtime::Lookup::unlookup(representative.clone());
373		let origin = Runtime::AddressMapping::into_account_id(caller);
374		let call = ConvictionVotingCall::<Runtime>::delegate {
375			class,
376			to,
377			conviction,
378			balance: amount,
379		};
380
381		RuntimeHelper::<Runtime>::try_dispatch(
382			handle,
383			Some(origin).into(),
384			call,
385			SYSTEM_ACCOUNT_SIZE,
386		)?;
387
388		event.record(handle)?;
389
390		Ok(())
391	}
392
393	#[precompile::public("undelegate(uint16)")]
394	fn undelegate(handle: &mut impl PrecompileHandle, track_id: u16) -> EvmResult {
395		let caller = handle.context().caller;
396
397		let event = log2(
398			handle.context().address,
399			SELECTOR_LOG_UNDELEGATED,
400			H256::from_low_u64_be(track_id as u64), // track id,
401			solidity::encode_event_data(Address(caller)),
402		);
403		handle.record_log_costs(&[&event])?;
404
405		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
406		let origin = Runtime::AddressMapping::into_account_id(caller);
407		let call = ConvictionVotingCall::<Runtime>::undelegate { class };
408
409		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
410
411		event.record(handle)?;
412
413		Ok(())
414	}
415
416	#[precompile::public("unlock(uint16,address)")]
417	fn unlock(handle: &mut impl PrecompileHandle, track_id: u16, target: Address) -> EvmResult {
418		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
419
420		let event = log2(
421			handle.context().address,
422			SELECTOR_LOG_UNLOCKED,
423			H256::from_low_u64_be(track_id as u64), // track id,
424			solidity::encode_event_data(target),
425		);
426		handle.record_log_costs(&[&event])?;
427
428		let target: H160 = target.into();
429		let target = Runtime::AddressMapping::into_account_id(target);
430		let target: <Runtime::Lookup as StaticLookup>::Source =
431			Runtime::Lookup::unlookup(target.clone());
432
433		log::trace!(
434			target: "conviction-voting-precompile",
435			"Unlocking conviction-voting tokens for {:?}", target
436		);
437
438		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
439		let call = ConvictionVotingCall::<Runtime>::unlock { class, target };
440
441		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
442
443		event.record(handle)?;
444
445		Ok(())
446	}
447
448	#[precompile::public("votingFor(address,uint16)")]
449	#[precompile::view]
450	fn voting_for(
451		handle: &mut impl PrecompileHandle,
452		who: Address,
453		track_id: u16,
454	) -> EvmResult<OutputVotingFor> {
455		// VotingFor: Twox64Concat(8) + 20 + Twox64Concat(8) + TransInfo::Id(2) + VotingOf
456		handle.record_db_read::<Runtime>(38 + VotingOf::<Runtime>::max_encoded_len())?;
457
458		let who = Runtime::AddressMapping::into_account_id(who.into());
459		let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
460
461		let voting = <VotingFor<Runtime>>::get(&who, &class);
462
463		Ok(Self::voting_to_output(voting)?)
464	}
465
466	#[precompile::public("classLocksFor(address)")]
467	#[precompile::view]
468	fn class_locks_for(
469		handle: &mut impl PrecompileHandle,
470		who: Address,
471	) -> EvmResult<Vec<OutputClassLock>> {
472		// ClassLocksFor: Twox64Concat(8) + 20 + BoundedVec(TransInfo::Id(2) * ClassCountOf)
473		handle.record_db_read::<Runtime>(
474			28 + ((2 * frame_support::traits::ClassCountOf::<
475				<Runtime as pallet_conviction_voting::Config>::Polls,
476				Tally<
477					<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
478						<Runtime as frame_system::Config>::AccountId,
479					>>::Balance,
480					<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
481				>,
482			>::get()) as usize),
483		)?;
484
485		let who = Runtime::AddressMapping::into_account_id(who.into());
486
487		let class_locks_for = <ClassLocksFor<Runtime>>::get(&who);
488		let mut output = Vec::new();
489		for (track_id, amount) in class_locks_for {
490			output.push(OutputClassLock {
491				track: Self::track_id_to_u16(track_id)?,
492				amount: amount.into(),
493			});
494		}
495
496		Ok(output)
497	}
498
499	fn u8_to_conviction(conviction: u8) -> MayRevert<Conviction> {
500		conviction
501			.try_into()
502			.map_err(|_| RevertReason::custom("Must be an integer between 0 and 6 included").into())
503	}
504
505	fn u32_to_index(index: u32) -> MayRevert<IndexOf<Runtime>> {
506		index
507			.try_into()
508			.map_err(|_| RevertReason::value_is_too_large("index type").into())
509	}
510
511	fn u16_to_track_id(class: u16) -> MayRevert<ClassOf<Runtime>> {
512		class
513			.try_into()
514			.map_err(|_| RevertReason::value_is_too_large("trackId type").into())
515	}
516
517	fn track_id_to_u16(class: ClassOf<Runtime>) -> MayRevert<u16> {
518		class
519			.try_into()
520			.map_err(|_| RevertReason::value_is_too_large("trackId type").into())
521	}
522
523	fn u256_to_amount(value: U256) -> MayRevert<BalanceOf<Runtime>> {
524		value
525			.try_into()
526			.map_err(|_| RevertReason::value_is_too_large("balance type").into())
527	}
528
529	fn log_vote_event(
530		handle: &mut impl PrecompileHandle,
531		poll_index: u32,
532		vote: AccountVote<U256>,
533	) -> EvmResult<(IndexOf<Runtime>, AccountVote<BalanceOf<Runtime>>, Log)> {
534		let (contract_addr, caller) = (handle.context().address, handle.context().caller);
535		let (vote, event) = match vote {
536			AccountVote::Standard { vote, balance } => {
537				let event = log2(
538					contract_addr,
539					SELECTOR_LOG_VOTED,
540					H256::from_low_u64_be(poll_index as u64),
541					solidity::encode_event_data((
542						Address(caller),
543						vote.aye,
544						balance,
545						u8::from(vote.conviction),
546					)),
547				);
548				(
549					AccountVote::Standard {
550						vote,
551						balance: Self::u256_to_amount(balance).in_field("voteAmount")?,
552					},
553					event,
554				)
555			}
556			AccountVote::Split { aye, nay } => {
557				let event = log2(
558					contract_addr,
559					SELECTOR_LOG_VOTE_SPLIT,
560					H256::from_low_u64_be(poll_index as u64),
561					solidity::encode_event_data((Address(caller), aye, nay)),
562				);
563				(
564					AccountVote::Split {
565						aye: Self::u256_to_amount(aye).in_field("aye")?,
566						nay: Self::u256_to_amount(nay).in_field("nay")?,
567					},
568					event,
569				)
570			}
571			AccountVote::SplitAbstain { aye, nay, abstain } => {
572				let event = log2(
573					contract_addr,
574					SELECTOR_LOG_VOTE_SPLIT_ABSTAINED,
575					H256::from_low_u64_be(poll_index as u64),
576					solidity::encode_event_data((Address(caller), aye, nay, abstain)),
577				);
578				(
579					AccountVote::SplitAbstain {
580						aye: Self::u256_to_amount(aye).in_field("aye")?,
581						nay: Self::u256_to_amount(nay).in_field("nay")?,
582						abstain: Self::u256_to_amount(abstain).in_field("abstain")?,
583					},
584					event,
585				)
586			}
587		};
588		handle.record_log_costs(&[&event])?;
589		Ok((Self::u32_to_index(poll_index)?, vote, event))
590	}
591
592	fn voting_to_output(voting: VotingOf<Runtime>) -> MayRevert<OutputVotingFor> {
593		let output = match voting {
594			Voting::Casting(Casting {
595				votes,
596				delegations,
597				prior,
598			}) => {
599				let mut output_votes = Vec::new();
600				for (poll_index, account_vote) in votes {
601					let poll_index: u32 = poll_index
602						.try_into()
603						.map_err(|_| RevertReason::value_is_too_large("index type"))?;
604					let account_vote = match account_vote {
605						AccountVote::Standard { vote, balance } => OutputAccountVote {
606							is_standard: true,
607							standard: StandardVote {
608								vote: OutputVote {
609									aye: vote.aye,
610									conviction: vote.conviction.into(),
611								},
612								balance: balance.into(),
613							},
614							..Default::default()
615						},
616						AccountVote::Split { aye, nay } => OutputAccountVote {
617							is_split: true,
618							split: SplitVote {
619								aye: aye.into(),
620								nay: nay.into(),
621							},
622							..Default::default()
623						},
624						AccountVote::SplitAbstain { aye, nay, abstain } => OutputAccountVote {
625							is_split_abstain: true,
626							split_abstain: SplitAbstainVote {
627								aye: aye.into(),
628								nay: nay.into(),
629								abstain: abstain.into(),
630							},
631							..Default::default()
632						},
633					};
634
635					output_votes.push(PollAccountVote {
636						poll_index,
637						account_vote,
638					});
639				}
640
641				OutputVotingFor {
642					is_casting: true,
643					casting: OutputCasting {
644						votes: output_votes,
645						delegations: Delegations {
646							votes: delegations.votes.into(),
647							capital: delegations.capital.into(),
648						},
649						prior: PriorLock {
650							balance: prior.locked().into(),
651						},
652					},
653					..Default::default()
654				}
655			}
656			Voting::Delegating(Delegating {
657				balance,
658				target,
659				conviction,
660				delegations,
661				prior,
662			}) => OutputVotingFor {
663				is_delegating: true,
664				delegating: OutputDelegating {
665					balance: balance.into(),
666					target: Address(target.into()),
667					conviction: conviction.into(),
668					delegations: Delegations {
669						votes: delegations.votes.into(),
670						capital: delegations.capital.into(),
671					},
672					prior: PriorLock {
673						balance: prior.locked().into(),
674					},
675				},
676				..Default::default()
677			},
678		};
679
680		Ok(output)
681	}
682}
683
684#[derive(Default, solidity::Codec)]
685pub struct OutputClassLock {
686	track: u16,
687	amount: U256,
688}
689
690#[derive(Default, solidity::Codec)]
691pub struct OutputVotingFor {
692	is_casting: bool,
693	is_delegating: bool,
694	casting: OutputCasting,
695	delegating: OutputDelegating,
696}
697
698#[derive(Default, solidity::Codec)]
699pub struct OutputCasting {
700	votes: Vec<PollAccountVote>,
701	delegations: Delegations,
702	prior: PriorLock,
703}
704
705#[derive(Default, solidity::Codec)]
706pub struct PollAccountVote {
707	poll_index: u32,
708	account_vote: OutputAccountVote,
709}
710
711#[derive(Default, solidity::Codec)]
712pub struct OutputDelegating {
713	balance: U256,
714	target: Address,
715	conviction: u8,
716	delegations: Delegations,
717	prior: PriorLock,
718}
719
720#[derive(Default, solidity::Codec)]
721pub struct OutputAccountVote {
722	is_standard: bool,
723	is_split: bool,
724	is_split_abstain: bool,
725	standard: StandardVote,
726	split: SplitVote,
727	split_abstain: SplitAbstainVote,
728}
729
730#[derive(Default, solidity::Codec)]
731pub struct StandardVote {
732	vote: OutputVote,
733	balance: U256,
734}
735
736#[derive(Default, solidity::Codec)]
737pub struct OutputVote {
738	aye: bool,
739	conviction: u8,
740}
741
742#[derive(Default, solidity::Codec)]
743pub struct SplitVote {
744	aye: U256,
745	nay: U256,
746}
747
748#[derive(Default, solidity::Codec)]
749pub struct SplitAbstainVote {
750	aye: U256,
751	nay: U256,
752	abstain: U256,
753}
754
755#[derive(Default, solidity::Codec)]
756pub struct Delegations {
757	votes: U256,
758	capital: U256,
759}
760
761#[derive(Default, solidity::Codec)]
762pub struct PriorLock {
763	balance: U256,
764}