2021-10-12 12:34:05 -07:00
|
|
|
use anchor_lang::__private::bytemuck::Zeroable;
|
2021-10-05 13:40:01 -07:00
|
|
|
use anchor_lang::prelude::*;
|
2021-10-12 12:34:05 -07:00
|
|
|
use anchor_spl::associated_token::AssociatedToken;
|
2021-10-05 13:40:01 -07:00
|
|
|
use anchor_spl::token::{self, Mint, Token, TokenAccount};
|
2021-10-14 11:31:52 -07:00
|
|
|
use spl_governance::addins::voter_weight::{
|
|
|
|
VoterWeightAccountType, VoterWeightRecord as SplVoterWeightRecord,
|
|
|
|
};
|
2021-10-12 12:34:05 -07:00
|
|
|
use std::mem::size_of;
|
2021-10-14 11:31:52 -07:00
|
|
|
use std::ops::Deref;
|
2021-10-05 13:40:01 -07:00
|
|
|
|
|
|
|
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
|
|
|
|
|
|
|
#[program]
|
|
|
|
pub mod governance_registry {
|
|
|
|
use super::*;
|
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
/// Creates a new voting registrar. There can only be a single regsitrar
|
|
|
|
/// per governance realm.
|
2021-10-05 13:40:01 -07:00
|
|
|
pub fn init_registrar(
|
|
|
|
ctx: Context<InitRegistrar>,
|
|
|
|
registrar_bump: u8,
|
|
|
|
voting_mint_bump: u8,
|
2021-10-12 12:34:05 -07:00
|
|
|
_voting_mint_decimals: u8,
|
2021-10-05 13:40:01 -07:00
|
|
|
) -> Result<()> {
|
2021-10-12 12:34:05 -07:00
|
|
|
let registrar = &mut ctx.accounts.registrar.load_init()?;
|
2021-10-05 13:40:01 -07:00
|
|
|
registrar.registrar_bump = registrar_bump;
|
|
|
|
registrar.voting_mint_bump = voting_mint_bump;
|
|
|
|
registrar.realm = ctx.accounts.realm.key();
|
|
|
|
registrar.voting_mint = ctx.accounts.voting_mint.key();
|
|
|
|
registrar.authority = ctx.accounts.authority.key();
|
2021-10-12 12:34:05 -07:00
|
|
|
|
2021-10-05 13:40:01 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
/// Creates a new voter account. There can only be a single voter per
|
|
|
|
/// user wallet.
|
2021-10-05 13:40:01 -07:00
|
|
|
pub fn init_voter(ctx: Context<InitVoter>, voter_bump: u8) -> Result<()> {
|
2021-10-12 12:34:05 -07:00
|
|
|
let voter = &mut ctx.accounts.voter.load_mut()?;
|
2021-10-05 13:40:01 -07:00
|
|
|
voter.voter_bump = voter_bump;
|
|
|
|
voter.authority = ctx.accounts.authority.key();
|
|
|
|
voter.registrar = ctx.accounts.registrar.key();
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
/// Creates a new exchange rate for a given mint. This allows a voter to
|
|
|
|
/// deposit the mint in exchange for vTokens. There can only be a single
|
|
|
|
/// exchange rate per mint.
|
|
|
|
pub fn add_exchange_rate(ctx: Context<AddExchangeRate>, er: ExchangeRateEntry) -> Result<()> {
|
|
|
|
require!(er.rate > 0, InvalidRate);
|
|
|
|
let registrar = &mut ctx.accounts.registrar.load_mut()?;
|
|
|
|
let idx = registrar
|
|
|
|
.rates
|
|
|
|
.iter()
|
|
|
|
.position(|r| !r.is_used)
|
|
|
|
.ok_or(ErrorCode::RatesFull)?;
|
|
|
|
registrar.rates[idx] = er;
|
2021-10-05 13:40:01 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
/// Deposits tokens into the registrar in exchange for *frozen* voting
|
|
|
|
/// tokens. These tokens are not used for anything other than displaying
|
2021-10-14 11:31:52 -07:00
|
|
|
/// the amount in wallets.
|
2021-10-12 12:34:05 -07:00
|
|
|
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
|
2021-10-14 11:31:52 -07:00
|
|
|
let registrar = &ctx.accounts.registrar.load()?;
|
2021-10-12 12:34:05 -07:00
|
|
|
let voter = &mut ctx.accounts.voter.load_mut()?;
|
|
|
|
|
|
|
|
// Get the exchange rate entry associated with this deposit.
|
|
|
|
let er_idx = registrar
|
|
|
|
.rates
|
|
|
|
.iter()
|
|
|
|
.position(|r| r.mint == ctx.accounts.deposit_mint.key())
|
|
|
|
.ok_or(ErrorCode::ExchangeRateEntryNotFound)?;
|
|
|
|
let er_entry = registrar.rates[er_idx];
|
|
|
|
|
|
|
|
// Get the deposit entry associated with this deposit.
|
|
|
|
let deposit_entry = {
|
|
|
|
match voter.deposits.iter().position(|deposit_entry| {
|
|
|
|
registrar.rates[deposit_entry.rate_idx as usize].mint
|
|
|
|
== ctx.accounts.deposit_mint.key()
|
|
|
|
}) {
|
|
|
|
// Lazily instantiate the deposit if needed.
|
|
|
|
None => {
|
|
|
|
let free_entry_idx = voter
|
|
|
|
.deposits
|
|
|
|
.iter()
|
|
|
|
.position(|deposit_entry| !deposit_entry.is_used)
|
|
|
|
.ok_or(ErrorCode::DepositEntryFull)?;
|
|
|
|
let entry = &mut voter.deposits[free_entry_idx];
|
|
|
|
entry.is_used = true;
|
|
|
|
entry.rate_idx = free_entry_idx as u8;
|
|
|
|
entry
|
|
|
|
}
|
|
|
|
// Use the existing deposit.
|
|
|
|
Some(e) => &mut voter.deposits[e],
|
|
|
|
}
|
|
|
|
};
|
|
|
|
deposit_entry.amount += amount;
|
2021-10-05 13:40:01 -07:00
|
|
|
|
|
|
|
// Calculate the amount of voting tokens to mint.
|
2021-10-12 12:34:05 -07:00
|
|
|
let scaled_amount = er_entry.rate * amount;
|
|
|
|
|
|
|
|
// Deposit tokens into the registrar.
|
|
|
|
token::transfer((&*ctx.accounts).into(), amount)?;
|
2021-10-05 13:40:01 -07:00
|
|
|
|
|
|
|
// Mint vote tokens to the depositor.
|
|
|
|
token::mint_to((&*ctx.accounts).into(), scaled_amount)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
/// Withdraws tokens from a deposit entry, if they are unlocked according
|
|
|
|
/// to a vesting schedule.
|
|
|
|
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
|
|
|
|
// todo
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Updates a vesting schedule. Can only increase the lockup time. If all
|
|
|
|
/// tokens are unlocked, then the period count can also be updated.
|
|
|
|
pub fn update_schedule(ctx: Context<UpdateSchedule>) -> Result<()> {
|
2021-10-05 13:40:01 -07:00
|
|
|
// todo
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-10-14 11:31:52 -07:00
|
|
|
|
|
|
|
/// Calculates the lockup-scaled, time-decayed voting power for the given
|
|
|
|
/// voter and writes it into a `VoteWeightRecord` account to be used by
|
|
|
|
/// the SPL governance program.
|
|
|
|
///
|
|
|
|
/// When a voter locks up tokens with a vesting schedule, the voter's
|
|
|
|
/// voting power is scaled with a linear multiplier, but as time goes on,
|
|
|
|
/// that multiplier is decreased since the remaining lockup decreases.
|
|
|
|
pub fn decay_voting_power(ctx: Context<DecayVotingPower>) -> Result<()> {
|
|
|
|
// todo
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Closes the voter account, allowing one to retrieve rent exemption SOL.
|
|
|
|
pub fn close_voter(ctx: Context<CloseVoter>) -> Result<()> {
|
|
|
|
require!(ctx.accounts.voting_token.amount > 0, VotingTokenNonZero);
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-10-05 13:40:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Contexts.
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
2021-10-12 12:34:05 -07:00
|
|
|
#[instruction(registrar_bump: u8, voting_mint_bump: u8, voting_mint_decimals: u8)]
|
2021-10-05 13:40:01 -07:00
|
|
|
pub struct InitRegistrar<'info> {
|
|
|
|
#[account(
|
|
|
|
init,
|
|
|
|
seeds = [realm.key().as_ref()],
|
|
|
|
bump = registrar_bump,
|
|
|
|
payer = payer,
|
2021-10-14 11:31:52 -07:00
|
|
|
space = 8 + size_of::<Registrar>()
|
2021-10-05 13:40:01 -07:00
|
|
|
)]
|
2021-10-12 12:34:05 -07:00
|
|
|
registrar: AccountLoader<'info, Registrar>,
|
2021-10-05 13:40:01 -07:00
|
|
|
#[account(
|
|
|
|
init,
|
|
|
|
seeds = [registrar.key().as_ref()],
|
|
|
|
bump = voting_mint_bump,
|
|
|
|
payer = payer,
|
|
|
|
mint::authority = registrar,
|
|
|
|
mint::decimals = voting_mint_decimals,
|
2021-10-14 11:31:52 -07:00
|
|
|
mint::freeze_authority = freeze_authority,
|
2021-10-05 13:40:01 -07:00
|
|
|
)]
|
|
|
|
voting_mint: Account<'info, Mint>,
|
|
|
|
realm: UncheckedAccount<'info>,
|
|
|
|
authority: UncheckedAccount<'info>,
|
2021-10-14 11:31:52 -07:00
|
|
|
#[account(seeds = [b"freeze"], bump)]
|
|
|
|
freeze_authority: UncheckedAccount<'info>,
|
2021-10-05 13:40:01 -07:00
|
|
|
payer: Signer<'info>,
|
|
|
|
system_program: Program<'info, System>,
|
|
|
|
token_program: Program<'info, Token>,
|
|
|
|
rent: Sysvar<'info, Rent>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
|
|
|
#[instruction(voter_bump: u8)]
|
|
|
|
pub struct InitVoter<'info> {
|
|
|
|
#[account(
|
|
|
|
init,
|
|
|
|
seeds = [registrar.key().as_ref(), authority.key().as_ref()],
|
|
|
|
bump = voter_bump,
|
|
|
|
payer = authority,
|
2021-10-14 11:31:52 -07:00
|
|
|
space = 8 + size_of::<Voter>()
|
2021-10-05 13:40:01 -07:00
|
|
|
)]
|
2021-10-12 12:34:05 -07:00
|
|
|
voter: AccountLoader<'info, Voter>,
|
2021-10-14 11:31:52 -07:00
|
|
|
// todo: init voting toiken
|
2021-10-12 12:34:05 -07:00
|
|
|
registrar: AccountLoader<'info, Registrar>,
|
2021-10-05 13:40:01 -07:00
|
|
|
authority: Signer<'info>,
|
|
|
|
system_program: Program<'info, System>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
2021-10-12 12:34:05 -07:00
|
|
|
#[instruction(rate: ExchangeRateEntry)]
|
2021-10-05 13:40:01 -07:00
|
|
|
pub struct AddExchangeRate<'info> {
|
|
|
|
#[account(
|
|
|
|
init,
|
|
|
|
payer = payer,
|
2021-10-12 12:34:05 -07:00
|
|
|
associated_token::authority = registrar,
|
|
|
|
associated_token::mint = deposit_mint,
|
2021-10-05 13:40:01 -07:00
|
|
|
)]
|
|
|
|
exchange_vault: Account<'info, TokenAccount>,
|
|
|
|
deposit_mint: Account<'info, Mint>,
|
|
|
|
#[account(has_one = authority)]
|
2021-10-12 12:34:05 -07:00
|
|
|
registrar: AccountLoader<'info, Registrar>,
|
2021-10-05 13:40:01 -07:00
|
|
|
authority: Signer<'info>,
|
|
|
|
payer: Signer<'info>,
|
|
|
|
rent: Sysvar<'info, Rent>,
|
|
|
|
token_program: Program<'info, Token>,
|
2021-10-12 12:34:05 -07:00
|
|
|
associated_token_program: Program<'info, AssociatedToken>,
|
2021-10-05 13:40:01 -07:00
|
|
|
system_program: Program<'info, System>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
2021-10-12 12:34:05 -07:00
|
|
|
pub struct Deposit<'info> {
|
|
|
|
#[account(has_one = authority)]
|
|
|
|
voter: AccountLoader<'info, Voter>,
|
2021-10-05 13:40:01 -07:00
|
|
|
#[account(
|
2021-10-12 12:34:05 -07:00
|
|
|
associated_token::authority = registrar,
|
|
|
|
associated_token::mint = deposit_mint,
|
2021-10-05 13:40:01 -07:00
|
|
|
)]
|
|
|
|
exchange_vault: Account<'info, TokenAccount>,
|
|
|
|
#[account(
|
|
|
|
constraint = deposit_token.mint == deposit_mint.key(),
|
|
|
|
)]
|
|
|
|
deposit_token: Account<'info, TokenAccount>,
|
|
|
|
#[account(
|
2021-10-12 12:34:05 -07:00
|
|
|
constraint = registrar.load()?.voting_mint == voting_token.mint,
|
2021-10-05 13:40:01 -07:00
|
|
|
)]
|
|
|
|
voting_token: Account<'info, TokenAccount>,
|
|
|
|
authority: Signer<'info>,
|
2021-10-12 12:34:05 -07:00
|
|
|
registrar: AccountLoader<'info, Registrar>,
|
2021-10-05 13:40:01 -07:00
|
|
|
deposit_mint: Account<'info, Mint>,
|
|
|
|
voting_mint: Account<'info, Mint>,
|
|
|
|
token_program: Program<'info, Token>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
2021-10-12 12:34:05 -07:00
|
|
|
pub struct Withdraw {
|
|
|
|
// todo
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct UpdateSchedule {
|
|
|
|
// todo
|
2021-10-05 13:40:01 -07:00
|
|
|
}
|
|
|
|
|
2021-10-14 11:31:52 -07:00
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct DecayVotingPower<'info> {
|
|
|
|
vote_weight_record: Account<'info, VoterWeightRecord>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct CloseVoter<'info> {
|
|
|
|
#[account(mut, has_one = authority, close = sol_destination)]
|
|
|
|
voter: AccountLoader<'info, Voter>,
|
|
|
|
authority: Signer<'info>,
|
|
|
|
voting_token: Account<'info, TokenAccount>,
|
|
|
|
sol_destination: UncheckedAccount<'info>,
|
|
|
|
}
|
|
|
|
|
2021-10-05 13:40:01 -07:00
|
|
|
// Accounts.
|
|
|
|
|
|
|
|
/// Instance of a voting rights distributor.
|
2021-10-12 12:34:05 -07:00
|
|
|
#[account(zero_copy)]
|
2021-10-05 13:40:01 -07:00
|
|
|
pub struct Registrar {
|
|
|
|
pub authority: Pubkey,
|
|
|
|
pub realm: Pubkey,
|
|
|
|
pub voting_mint: Pubkey,
|
|
|
|
pub voting_mint_bump: u8,
|
|
|
|
pub registrar_bump: u8,
|
2021-10-12 12:34:05 -07:00
|
|
|
pub rates: [ExchangeRateEntry; 32],
|
2021-10-05 13:40:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// User account for minting voting rights.
|
2021-10-12 12:34:05 -07:00
|
|
|
#[account(zero_copy)]
|
2021-10-05 13:40:01 -07:00
|
|
|
pub struct Voter {
|
|
|
|
pub authority: Pubkey,
|
|
|
|
pub registrar: Pubkey,
|
|
|
|
pub voter_bump: u8,
|
2021-10-12 12:34:05 -07:00
|
|
|
pub deposits: [DepositEntry; 32],
|
2021-10-05 13:40:01 -07:00
|
|
|
}
|
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
/// Exchange rate for an asset that can be used to mint voting rights.
|
|
|
|
#[zero_copy]
|
|
|
|
#[derive(AnchorSerialize, AnchorDeserialize)]
|
|
|
|
pub struct ExchangeRateEntry {
|
|
|
|
// True if the exchange rate entry is being used.
|
|
|
|
pub is_used: bool,
|
2021-10-05 13:40:01 -07:00
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
pub mint: Pubkey,
|
|
|
|
pub rate: u64,
|
2021-10-05 13:40:01 -07:00
|
|
|
}
|
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
unsafe impl Zeroable for ExchangeRateEntry {}
|
|
|
|
|
2021-10-14 11:31:52 -07:00
|
|
|
/// Bookkeeping for a single deposit for a given mint and lockup schedule.
|
2021-10-12 12:34:05 -07:00
|
|
|
#[zero_copy]
|
|
|
|
pub struct DepositEntry {
|
|
|
|
// True if the deposit entry is being used.
|
|
|
|
pub is_used: bool,
|
|
|
|
|
|
|
|
// Points to the ExchangeRate this deposit uses.
|
|
|
|
pub rate_idx: u8,
|
|
|
|
pub amount: u64,
|
2021-10-05 13:40:01 -07:00
|
|
|
|
|
|
|
// Locked state.
|
2021-10-12 12:34:05 -07:00
|
|
|
pub period_count: u64,
|
|
|
|
pub start_ts: i64,
|
|
|
|
pub end_ts: i64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DepositEntry {
|
|
|
|
/// Returns the voting power given by this deposit, scaled to account for
|
|
|
|
/// a lockup.
|
|
|
|
pub fn voting_power(&self) -> u64 {
|
|
|
|
let locked_multiplier = 1; // todo
|
|
|
|
self.amount * locked_multiplier
|
|
|
|
}
|
2021-10-05 13:40:01 -07:00
|
|
|
}
|
|
|
|
|
2021-10-14 11:31:52 -07:00
|
|
|
/// Anchor wrapper for the SPL governance program's VoterWeightRecord type.
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct VoterWeightRecord(SplVoterWeightRecord);
|
|
|
|
|
|
|
|
impl anchor_lang::AccountDeserialize for VoterWeightRecord {
|
|
|
|
fn try_deserialize(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
|
|
|
|
let mut data = buf;
|
|
|
|
let vwr: SplVoterWeightRecord = anchor_lang::AnchorDeserialize::deserialize(&mut data)
|
|
|
|
.map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotDeserialize)?;
|
|
|
|
if vwr.account_type != VoterWeightAccountType::VoterWeightRecord {
|
|
|
|
return Err(anchor_lang::__private::ErrorCode::AccountDidNotSerialize.into());
|
|
|
|
}
|
|
|
|
Ok(VoterWeightRecord(vwr))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_deserialize_unchecked(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
|
|
|
|
let mut data = buf;
|
|
|
|
let vwr: SplVoterWeightRecord = anchor_lang::AnchorDeserialize::deserialize(&mut data)
|
|
|
|
.map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotDeserialize)?;
|
|
|
|
if vwr.account_type != VoterWeightAccountType::Uninitialized {
|
|
|
|
return Err(anchor_lang::__private::ErrorCode::AccountDidNotSerialize.into());
|
|
|
|
}
|
|
|
|
Ok(VoterWeightRecord(vwr))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl anchor_lang::AccountSerialize for VoterWeightRecord {
|
|
|
|
fn try_serialize<W: std::io::Write>(
|
|
|
|
&self,
|
|
|
|
writer: &mut W,
|
|
|
|
) -> std::result::Result<(), ProgramError> {
|
|
|
|
AnchorSerialize::serialize(&self.0, writer)
|
|
|
|
.map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotSerialize)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl anchor_lang::Owner for VoterWeightRecord {
|
|
|
|
fn owner() -> Pubkey {
|
|
|
|
ID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Deref for VoterWeightRecord {
|
|
|
|
type Target = SplVoterWeightRecord;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-05 13:40:01 -07:00
|
|
|
// Error.
|
|
|
|
|
|
|
|
#[error]
|
|
|
|
pub enum ErrorCode {
|
|
|
|
#[msg("Exchange rate must be greater than zero")]
|
|
|
|
InvalidRate,
|
2021-10-12 12:34:05 -07:00
|
|
|
#[msg("")]
|
|
|
|
RatesFull,
|
|
|
|
#[msg("")]
|
|
|
|
ExchangeRateEntryNotFound,
|
|
|
|
#[msg("")]
|
|
|
|
DepositEntryNotFound,
|
|
|
|
DepositEntryFull,
|
2021-10-14 11:31:52 -07:00
|
|
|
VotingTokenNonZero,
|
2021-10-05 13:40:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// CpiContext.
|
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
impl<'info> From<&Deposit<'info>> for CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
|
|
|
|
fn from(accs: &Deposit<'info>) -> Self {
|
2021-10-05 13:40:01 -07:00
|
|
|
let program = accs.token_program.to_account_info();
|
|
|
|
let accounts = token::Transfer {
|
|
|
|
from: accs.deposit_token.to_account_info(),
|
|
|
|
to: accs.exchange_vault.to_account_info(),
|
|
|
|
authority: accs.registrar.to_account_info(),
|
|
|
|
};
|
|
|
|
CpiContext::new(program, accounts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-12 12:34:05 -07:00
|
|
|
impl<'info> From<&Deposit<'info>> for CpiContext<'_, '_, '_, 'info, token::MintTo<'info>> {
|
|
|
|
fn from(accs: &Deposit<'info>) -> Self {
|
2021-10-05 13:40:01 -07:00
|
|
|
let program = accs.token_program.to_account_info();
|
|
|
|
let accounts = token::MintTo {
|
|
|
|
mint: accs.voting_mint.to_account_info(),
|
|
|
|
to: accs.voting_token.to_account_info(),
|
|
|
|
authority: accs.registrar.to_account_info(),
|
|
|
|
};
|
|
|
|
CpiContext::new(program, accounts)
|
|
|
|
}
|
|
|
|
}
|