Scaffold
This commit is contained in:
parent
e4f63f7c8a
commit
ca3aca3ce3
|
@ -25,9 +25,7 @@ checksum = "6b2d54853319fd101b8dd81de382bcbf3e03410a64d8928bbee85a3e7dcde483"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-attribute-access-control"
|
name = "anchor-attribute-access-control"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a4ff7b54a5d232ce177fb5e1d42bb767678e5df0434b205a045afa8fc7a83a23"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-syn",
|
"anchor-syn",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -39,9 +37,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-attribute-account"
|
name = "anchor-attribute-account"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f2c53504cae7e67cd4d7a2d6db6828293923c0a0d80e8ae29e67bf517548d6e"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-syn",
|
"anchor-syn",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -54,9 +50,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-attribute-error"
|
name = "anchor-attribute-error"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8341b2661f13ca6e4dfb55e21f49908774d9fb915c56dbd7b804668c3eb53380"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-syn",
|
"anchor-syn",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -66,9 +60,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-attribute-event"
|
name = "anchor-attribute-event"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7e5e4e868c019d872baa669b43796a877e6a66da4a286a1b8467ba747428804d"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-syn",
|
"anchor-syn",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -79,9 +71,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-attribute-interface"
|
name = "anchor-attribute-interface"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db9d1de704bf2845d9edc7fe368a48943b672f2e770251c4d929d6712bac367d"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-syn",
|
"anchor-syn",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -93,9 +83,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-attribute-program"
|
name = "anchor-attribute-program"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f035ee71ffa1db5f06694a650aa61ba4265f9fa1263fed3b744b2cd8a31f448a"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-syn",
|
"anchor-syn",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -106,9 +94,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-attribute-state"
|
name = "anchor-attribute-state"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e1a2bfe33f0fc0aeffe01dda3aa36417eb96ebcfc39dfdb003ef968d5ed9a70f"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-syn",
|
"anchor-syn",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -119,9 +105,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-derive-accounts"
|
name = "anchor-derive-accounts"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fffb0562db113cd2491e825e0f4df8d050dc14371dec7cccf757efcf86445451"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-syn",
|
"anchor-syn",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -132,9 +116,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-lang"
|
name = "anchor-lang"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9bd220aca840a0067d43da61e802a5e4ac13582f0baef7aa33c79d42a0110086"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-attribute-access-control",
|
"anchor-attribute-access-control",
|
||||||
"anchor-attribute-account",
|
"anchor-attribute-account",
|
||||||
|
@ -153,22 +135,19 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-spl"
|
name = "anchor-spl"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9f34afdc6678b3c2563403f6ca2a6d59b2407cd3334757e2761c4776e0339764"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-lang",
|
"anchor-lang",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"serum_dex",
|
"serum_dex",
|
||||||
"solana-program",
|
"solana-program",
|
||||||
|
"spl-associated-token-account",
|
||||||
"spl-token",
|
"spl-token",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anchor-syn"
|
name = "anchor-syn"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "93cade4d95b7edd8d6be4e8243b935e26dee6eb1a50bd226392cefd0a31e682c"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bs58 0.3.1",
|
"bs58 0.3.1",
|
||||||
|
@ -991,8 +970,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serum_dex"
|
name = "serum_dex"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/project-serum/serum-dex?rev=1be91f2#1be91f2863d8ecede32daaae7e768034e24bbc79"
|
||||||
checksum = "02705854bae4622e552346c8edd43ab90c7425da35d63d2c689f39238f8d8b25"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"arrayref",
|
||||||
"bincode",
|
"bincode",
|
||||||
|
@ -1128,6 +1106,16 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spl-associated-token-account"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "393e2240d521c3dd770806bff25c2c00d761ac962be106e14e22dd912007f428"
|
||||||
|
dependencies = [
|
||||||
|
"solana-program",
|
||||||
|
"spl-token",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spl-token"
|
name = "spl-token"
|
||||||
version = "3.2.0"
|
version = "3.2.0"
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@project-serum/anchor": "^0.16.1"
|
"@project-serum/anchor": "^0.17.1-beta.1",
|
||||||
|
"@solana/spl-token": "^0.1.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/mocha": "^9.0.0",
|
||||||
"chai": "^4.3.4",
|
"chai": "^4.3.4",
|
||||||
"mocha": "^9.0.3",
|
"mocha": "^9.0.3",
|
||||||
"ts-mocha": "^8.0.0",
|
"ts-mocha": "^8.0.0",
|
||||||
"@types/mocha": "^9.0.0",
|
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,5 +15,6 @@ cpi = ["no-entrypoint"]
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anchor-lang = "0.16.1"
|
# TODO: use versions after next Anchor publish.
|
||||||
anchor-spl = "0.16.1"
|
anchor-lang = { git = "https://github.com/project-serum/anchor" }
|
||||||
|
anchor-spl = { git = "https://github.com/project-serum/anchor" }
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
use anchor_lang::__private::bytemuck::Zeroable;
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
|
use anchor_spl::associated_token::AssociatedToken;
|
||||||
use anchor_spl::token::{self, Mint, Token, TokenAccount};
|
use anchor_spl::token::{self, Mint, Token, TokenAccount};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||||
|
|
||||||
|
@ -7,63 +10,94 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||||
pub mod governance_registry {
|
pub mod governance_registry {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Creates a new voting registrar.
|
/// Creates a new voting registrar. There can only be a single regsitrar
|
||||||
|
/// per governance realm.
|
||||||
pub fn init_registrar(
|
pub fn init_registrar(
|
||||||
ctx: Context<InitRegistrar>,
|
ctx: Context<InitRegistrar>,
|
||||||
_voting_mint_decimals: u8,
|
|
||||||
registrar_bump: u8,
|
registrar_bump: u8,
|
||||||
voting_mint_bump: u8,
|
voting_mint_bump: u8,
|
||||||
|
_voting_mint_decimals: u8,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let registrar = &mut ctx.accounts.registrar;
|
let registrar = &mut ctx.accounts.registrar.load_init()?;
|
||||||
registrar.registrar_bump = registrar_bump;
|
registrar.registrar_bump = registrar_bump;
|
||||||
registrar.voting_mint_bump = voting_mint_bump;
|
registrar.voting_mint_bump = voting_mint_bump;
|
||||||
registrar.realm = ctx.accounts.realm.key();
|
registrar.realm = ctx.accounts.realm.key();
|
||||||
registrar.voting_mint = ctx.accounts.voting_mint.key();
|
registrar.voting_mint = ctx.accounts.voting_mint.key();
|
||||||
registrar.authority = ctx.accounts.authority.key();
|
registrar.authority = ctx.accounts.authority.key();
|
||||||
if true {
|
|
||||||
panic!("HELLO WORLD");
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new voter account.
|
/// Creates a new voter account. There can only be a single voter per
|
||||||
|
/// user wallet.
|
||||||
pub fn init_voter(ctx: Context<InitVoter>, voter_bump: u8) -> Result<()> {
|
pub fn init_voter(ctx: Context<InitVoter>, voter_bump: u8) -> Result<()> {
|
||||||
let voter = &mut ctx.accounts.voter;
|
let voter = &mut ctx.accounts.voter.load_mut()?;
|
||||||
voter.voter_bump = voter_bump;
|
voter.voter_bump = voter_bump;
|
||||||
voter.authority = ctx.accounts.authority.key();
|
voter.authority = ctx.accounts.authority.key();
|
||||||
voter.registrar = ctx.accounts.registrar.key();
|
voter.registrar = ctx.accounts.registrar.key();
|
||||||
voter.rights_outstanding = 0;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new exchange rate for a given mint. The exchange rate
|
/// Creates a new exchange rate for a given mint. This allows a voter to
|
||||||
/// allows one to deposit one token into the registrar and receive voting
|
/// deposit the mint in exchange for vTokens. There can only be a single
|
||||||
/// tokens in response. Only the registrar authority can invoke this.
|
/// exchange rate per mint.
|
||||||
pub fn add_exchange_rate(
|
pub fn add_exchange_rate(ctx: Context<AddExchangeRate>, er: ExchangeRateEntry) -> Result<()> {
|
||||||
ctx: Context<AddExchangeRate>,
|
require!(er.rate > 0, InvalidRate);
|
||||||
rate: u64,
|
let registrar = &mut ctx.accounts.registrar.load_mut()?;
|
||||||
rate_bump: u8,
|
let idx = registrar
|
||||||
vault_bump: u8,
|
.rates
|
||||||
) -> Result<()> {
|
.iter()
|
||||||
require!(rate > 0, InvalidRate);
|
.position(|r| !r.is_used)
|
||||||
|
.ok_or(ErrorCode::RatesFull)?;
|
||||||
let rate = &mut ctx.accounts.exchange_rate;
|
registrar.rates[idx] = er;
|
||||||
rate.deposit_mint = ctx.accounts.deposit_mint.key();
|
|
||||||
rate.rate_bump = rate_bump;
|
|
||||||
rate.vault_bump = vault_bump;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deposits tokens into the registrar in exchange for voting rights that
|
/// Deposits tokens into the registrar in exchange for *frozen* voting
|
||||||
/// can be used with a DAO.
|
/// tokens. These tokens are not used for anything other than displaying
|
||||||
pub fn mint_voting_rights(ctx: Context<MintVotingRights>, amount: u64) -> Result<()> {
|
/// the amount in walletes.
|
||||||
// Deposit tokens into the registrar.
|
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
|
||||||
token::transfer((&*ctx.accounts).into(), amount)?;
|
let registrar = &mut ctx.accounts.registrar.load()?;
|
||||||
|
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;
|
||||||
|
|
||||||
// Calculate the amount of voting tokens to mint.
|
// Calculate the amount of voting tokens to mint.
|
||||||
let scaled_amount = { amount };
|
let scaled_amount = er_entry.rate * amount;
|
||||||
|
|
||||||
|
// Deposit tokens into the registrar.
|
||||||
|
token::transfer((&*ctx.accounts).into(), amount)?;
|
||||||
|
|
||||||
// Mint vote tokens to the depositor.
|
// Mint vote tokens to the depositor.
|
||||||
token::mint_to((&*ctx.accounts).into(), scaled_amount)?;
|
token::mint_to((&*ctx.accounts).into(), scaled_amount)?;
|
||||||
|
@ -71,8 +105,16 @@ pub mod governance_registry {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the Voter's voting rights by decaying locked deposits.
|
/// Withdraws tokens from a deposit entry, if they are unlocked according
|
||||||
pub fn decay_voting_rights(ctx: Context<DecayVotingRights>) -> Result<()> {
|
/// 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<()> {
|
||||||
// todo
|
// todo
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -81,15 +123,16 @@ pub mod governance_registry {
|
||||||
// Contexts.
|
// Contexts.
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
#[instruction(voting_mint_decimals: u8, registrar_bump: u8, voting_mint_bump: u8)]
|
#[instruction(registrar_bump: u8, voting_mint_bump: u8, voting_mint_decimals: u8)]
|
||||||
pub struct InitRegistrar<'info> {
|
pub struct InitRegistrar<'info> {
|
||||||
#[account(
|
#[account(
|
||||||
init,
|
init,
|
||||||
seeds = [realm.key().as_ref()],
|
seeds = [realm.key().as_ref()],
|
||||||
bump = registrar_bump,
|
bump = registrar_bump,
|
||||||
payer = payer,
|
payer = payer,
|
||||||
|
space = 8 + size_of::<Registrar>()
|
||||||
)]
|
)]
|
||||||
registrar: Account<'info, Registrar>,
|
registrar: AccountLoader<'info, Registrar>,
|
||||||
#[account(
|
#[account(
|
||||||
init,
|
init,
|
||||||
seeds = [registrar.key().as_ref()],
|
seeds = [registrar.key().as_ref()],
|
||||||
|
@ -115,52 +158,42 @@ pub struct InitVoter<'info> {
|
||||||
seeds = [registrar.key().as_ref(), authority.key().as_ref()],
|
seeds = [registrar.key().as_ref(), authority.key().as_ref()],
|
||||||
bump = voter_bump,
|
bump = voter_bump,
|
||||||
payer = authority,
|
payer = authority,
|
||||||
|
space = 8 + size_of::<Voter>()
|
||||||
)]
|
)]
|
||||||
voter: Account<'info, Voter>,
|
voter: AccountLoader<'info, Voter>,
|
||||||
registrar: Account<'info, Registrar>,
|
registrar: AccountLoader<'info, Registrar>,
|
||||||
authority: Signer<'info>,
|
authority: Signer<'info>,
|
||||||
system_program: Program<'info, System>,
|
system_program: Program<'info, System>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
#[instruction(rate_bump: u8, vault_bump: u8)]
|
#[instruction(rate: ExchangeRateEntry)]
|
||||||
pub struct AddExchangeRate<'info> {
|
pub struct AddExchangeRate<'info> {
|
||||||
#[account(
|
#[account(
|
||||||
init,
|
init,
|
||||||
seeds = [b"exchange-rate", registrar.key().as_ref(), deposit_mint.key().as_ref()],
|
|
||||||
bump = rate_bump,
|
|
||||||
payer = payer,
|
payer = payer,
|
||||||
)]
|
associated_token::authority = registrar,
|
||||||
exchange_rate: Account<'info, ExchangeRate>,
|
associated_token::mint = deposit_mint,
|
||||||
#[account(
|
|
||||||
init,
|
|
||||||
seeds = [b"exchange-vault", exchange_rate.key().as_ref()],
|
|
||||||
bump = vault_bump,
|
|
||||||
payer = payer,
|
|
||||||
token::authority = registrar,
|
|
||||||
token::mint = deposit_mint,
|
|
||||||
)]
|
)]
|
||||||
exchange_vault: Account<'info, TokenAccount>,
|
exchange_vault: Account<'info, TokenAccount>,
|
||||||
deposit_mint: Account<'info, Mint>,
|
deposit_mint: Account<'info, Mint>,
|
||||||
#[account(has_one = authority)]
|
#[account(has_one = authority)]
|
||||||
registrar: Account<'info, Registrar>,
|
registrar: AccountLoader<'info, Registrar>,
|
||||||
authority: Signer<'info>,
|
authority: Signer<'info>,
|
||||||
payer: Signer<'info>,
|
payer: Signer<'info>,
|
||||||
rent: Sysvar<'info, Rent>,
|
rent: Sysvar<'info, Rent>,
|
||||||
token_program: Program<'info, Token>,
|
token_program: Program<'info, Token>,
|
||||||
|
associated_token_program: Program<'info, AssociatedToken>,
|
||||||
system_program: Program<'info, System>,
|
system_program: Program<'info, System>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct MintVotingRights<'info> {
|
pub struct Deposit<'info> {
|
||||||
|
#[account(has_one = authority)]
|
||||||
|
voter: AccountLoader<'info, Voter>,
|
||||||
#[account(
|
#[account(
|
||||||
seeds = [b"exchange-rate", registrar.key().as_ref(), deposit_mint.key().as_ref()],
|
associated_token::authority = registrar,
|
||||||
bump = exchange_rate.rate_bump,
|
associated_token::mint = deposit_mint,
|
||||||
)]
|
|
||||||
exchange_rate: Account<'info, ExchangeRate>,
|
|
||||||
#[account(
|
|
||||||
seeds = [b"exchange-vault", exchange_rate.key().as_ref()],
|
|
||||||
bump = exchange_rate.vault_bump,
|
|
||||||
)]
|
)]
|
||||||
exchange_vault: Account<'info, TokenAccount>,
|
exchange_vault: Account<'info, TokenAccount>,
|
||||||
#[account(
|
#[account(
|
||||||
|
@ -168,71 +201,83 @@ pub struct MintVotingRights<'info> {
|
||||||
)]
|
)]
|
||||||
deposit_token: Account<'info, TokenAccount>,
|
deposit_token: Account<'info, TokenAccount>,
|
||||||
#[account(
|
#[account(
|
||||||
constraint = registrar.voting_mint == voting_token.mint,
|
constraint = registrar.load()?.voting_mint == voting_token.mint,
|
||||||
)]
|
)]
|
||||||
voting_token: Account<'info, TokenAccount>,
|
voting_token: Account<'info, TokenAccount>,
|
||||||
authority: Signer<'info>,
|
authority: Signer<'info>,
|
||||||
registrar: Account<'info, Registrar>,
|
registrar: AccountLoader<'info, Registrar>,
|
||||||
deposit_mint: Account<'info, Mint>,
|
deposit_mint: Account<'info, Mint>,
|
||||||
voting_mint: Account<'info, Mint>,
|
voting_mint: Account<'info, Mint>,
|
||||||
token_program: Program<'info, Token>,
|
token_program: Program<'info, Token>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct DecayVotingRights<'info> {
|
pub struct Withdraw {
|
||||||
voter: Account<'info, Voter>,
|
// todo
|
||||||
deposit: Account<'info, VoterDeposit>,
|
}
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct UpdateSchedule {
|
||||||
|
// todo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accounts.
|
// Accounts.
|
||||||
|
|
||||||
/// Instance of a voting rights distributor.
|
/// Instance of a voting rights distributor.
|
||||||
#[account]
|
#[account(zero_copy)]
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Registrar {
|
pub struct Registrar {
|
||||||
pub authority: Pubkey,
|
pub authority: Pubkey,
|
||||||
pub realm: Pubkey,
|
pub realm: Pubkey,
|
||||||
pub voting_mint: Pubkey,
|
pub voting_mint: Pubkey,
|
||||||
pub voting_mint_bump: u8,
|
pub voting_mint_bump: u8,
|
||||||
pub registrar_bump: u8,
|
pub registrar_bump: u8,
|
||||||
|
pub rates: [ExchangeRateEntry; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// User account for minting voting rights.
|
/// User account for minting voting rights.
|
||||||
#[account]
|
#[account(zero_copy)]
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Voter {
|
pub struct Voter {
|
||||||
pub authority: Pubkey,
|
pub authority: Pubkey,
|
||||||
pub registrar: Pubkey,
|
pub registrar: Pubkey,
|
||||||
pub rights_outstanding: u64,
|
|
||||||
pub voter_bump: u8,
|
pub voter_bump: u8,
|
||||||
}
|
pub deposits: [DepositEntry; 32],
|
||||||
|
|
||||||
pub struct Deposit {
|
|
||||||
pub mint_idx: u8,
|
|
||||||
pub lockup_years: u8,
|
|
||||||
pub amount: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sub account for a
|
|
||||||
#[account]
|
|
||||||
pub struct VoterDeposit {
|
|
||||||
amount: u64,
|
|
||||||
exchange_rate: Pubkey,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exchange rate for an asset that can be used to mint voting rights.
|
/// Exchange rate for an asset that can be used to mint voting rights.
|
||||||
#[account]
|
#[zero_copy]
|
||||||
#[derive(Default)]
|
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||||
pub struct ExchangeRate {
|
pub struct ExchangeRateEntry {
|
||||||
deposit_mint: Pubkey,
|
// True if the exchange rate entry is being used.
|
||||||
rate: u64,
|
pub is_used: bool,
|
||||||
rate_bump: u8,
|
|
||||||
vault_bump: u8,
|
pub mint: Pubkey,
|
||||||
|
pub rate: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Zeroable for ExchangeRateEntry {}
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
|
||||||
// Locked state.
|
// Locked state.
|
||||||
period_count: u64,
|
pub period_count: u64,
|
||||||
start_ts: i64,
|
pub start_ts: i64,
|
||||||
end_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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error.
|
// Error.
|
||||||
|
@ -241,14 +286,19 @@ pub struct ExchangeRate {
|
||||||
pub enum ErrorCode {
|
pub enum ErrorCode {
|
||||||
#[msg("Exchange rate must be greater than zero")]
|
#[msg("Exchange rate must be greater than zero")]
|
||||||
InvalidRate,
|
InvalidRate,
|
||||||
|
#[msg("")]
|
||||||
|
RatesFull,
|
||||||
|
#[msg("")]
|
||||||
|
ExchangeRateEntryNotFound,
|
||||||
|
#[msg("")]
|
||||||
|
DepositEntryNotFound,
|
||||||
|
DepositEntryFull,
|
||||||
}
|
}
|
||||||
|
|
||||||
// CpiContext.
|
// CpiContext.
|
||||||
|
|
||||||
impl<'info> From<&MintVotingRights<'info>>
|
impl<'info> From<&Deposit<'info>> for CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
|
||||||
for CpiContext<'_, '_, '_, 'info, token::Transfer<'info>>
|
fn from(accs: &Deposit<'info>) -> Self {
|
||||||
{
|
|
||||||
fn from(accs: &MintVotingRights<'info>) -> Self {
|
|
||||||
let program = accs.token_program.to_account_info();
|
let program = accs.token_program.to_account_info();
|
||||||
let accounts = token::Transfer {
|
let accounts = token::Transfer {
|
||||||
from: accs.deposit_token.to_account_info(),
|
from: accs.deposit_token.to_account_info(),
|
||||||
|
@ -259,8 +309,8 @@ impl<'info> From<&MintVotingRights<'info>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'info> From<&MintVotingRights<'info>> for CpiContext<'_, '_, '_, 'info, token::MintTo<'info>> {
|
impl<'info> From<&Deposit<'info>> for CpiContext<'_, '_, '_, 'info, token::MintTo<'info>> {
|
||||||
fn from(accs: &MintVotingRights<'info>) -> Self {
|
fn from(accs: &Deposit<'info>) -> Self {
|
||||||
let program = accs.token_program.to_account_info();
|
let program = accs.token_program.to_account_info();
|
||||||
let accounts = token::MintTo {
|
let accounts = token::MintTo {
|
||||||
mint: accs.voting_mint.to_account_info(),
|
mint: accs.voting_mint.to_account_info(),
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import * as anchor from '@project-serum/anchor';
|
import * as anchor from '@project-serum/anchor';
|
||||||
|
import { Program } from '@project-serum/anchor';
|
||||||
import { PublicKey, Keypair, SystemProgram, SYSVAR_RENT_PUBKEY } from '@solana/web3.js';
|
import { PublicKey, Keypair, SystemProgram, SYSVAR_RENT_PUBKEY } from '@solana/web3.js';
|
||||||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
||||||
|
import { GovernanceRegistry } from '../target/types/governance_registry';
|
||||||
|
|
||||||
describe('voting-rights', () => {
|
describe('voting-rights', () => {
|
||||||
|
|
||||||
// Configure the client to use the local cluster.
|
|
||||||
anchor.setProvider(anchor.Provider.env());
|
anchor.setProvider(anchor.Provider.env());
|
||||||
|
|
||||||
const program = anchor.workspace.GovernanceRegistry;
|
const program = anchor.workspace.GovernanceRegistry as Program<GovernanceRegistry>;
|
||||||
const realm = Keypair.generate().publicKey;
|
|
||||||
|
|
||||||
|
// Initialized variables shared across tests.
|
||||||
|
const realm = Keypair.generate().publicKey;
|
||||||
const votingMintDecimals = 6;
|
const votingMintDecimals = 6;
|
||||||
|
|
||||||
|
// Uninitialized variables shared across tests.
|
||||||
let registrar: PublicKey, votingMint: PublicKey, voter: PublicKey;
|
let registrar: PublicKey, votingMint: PublicKey, voter: PublicKey;
|
||||||
let registrarBump: number, votingMintBump: number, voterBump: number;
|
let registrarBump: number, votingMintBump: number, voterBump: number;
|
||||||
|
|
||||||
|
@ -39,7 +41,7 @@ describe('voting-rights', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Initializes a registrar', async () => {
|
it('Initializes a registrar', async () => {
|
||||||
await program.rpc.initRegistrar(votingMintDecimals, registrarBump, votingMintBump, {
|
await program.rpc.initRegistrar(registrarBump, votingMintBump, votingMintDecimals, {
|
||||||
accounts: {
|
accounts: {
|
||||||
registrar,
|
registrar,
|
||||||
votingMint,
|
votingMint,
|
||||||
|
|
51
yarn.lock
51
yarn.lock
|
@ -2,17 +2,17 @@
|
||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5":
|
"@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5":
|
||||||
version "7.15.4"
|
version "7.15.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
|
||||||
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
|
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@project-serum/anchor@^0.16.1":
|
"@project-serum/anchor@^0.17.1-beta.1":
|
||||||
version "0.16.1"
|
version "0.17.1-beta.1"
|
||||||
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.16.1.tgz#e044280e4534a966161a101619f5259252eda8ff"
|
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.17.1-beta.1.tgz#0532b4534c6a86e67e8530040c717979240f1dbe"
|
||||||
integrity sha512-nbpxo5xgENkdDqfRBMMhCqicj9Mc3QcGFOy1cl5j7uYqvFjdOfkOMcP19sf6/m4wmgaclfiYfWeHALy7XDOubQ==
|
integrity sha512-W23rI48nNm6sM+3L6jUh4kSWh5cLJWwJHiohB2AnnG+9jYs/NSgCcope8dP0g2DeHITCo5E/VpZpZc4Fulqqpg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@project-serum/borsh" "^0.2.2"
|
"@project-serum/borsh" "^0.2.2"
|
||||||
"@solana/web3.js" "^1.17.0"
|
"@solana/web3.js" "^1.17.0"
|
||||||
|
@ -44,6 +44,18 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
buffer "~6.0.3"
|
buffer "~6.0.3"
|
||||||
|
|
||||||
|
"@solana/spl-token@^0.1.8":
|
||||||
|
version "0.1.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6"
|
||||||
|
integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.10.5"
|
||||||
|
"@solana/web3.js" "^1.21.0"
|
||||||
|
bn.js "^5.1.0"
|
||||||
|
buffer "6.0.3"
|
||||||
|
buffer-layout "^1.2.0"
|
||||||
|
dotenv "10.0.0"
|
||||||
|
|
||||||
"@solana/web3.js@^1.17.0":
|
"@solana/web3.js@^1.17.0":
|
||||||
version "1.29.0"
|
version "1.29.0"
|
||||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.29.0.tgz#61cb40e2190f13ae8084d20012cb81f8fade78fa"
|
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.29.0.tgz#61cb40e2190f13ae8084d20012cb81f8fade78fa"
|
||||||
|
@ -64,6 +76,26 @@
|
||||||
superstruct "^0.14.2"
|
superstruct "^0.14.2"
|
||||||
tweetnacl "^1.0.0"
|
tweetnacl "^1.0.0"
|
||||||
|
|
||||||
|
"@solana/web3.js@^1.21.0":
|
||||||
|
version "1.29.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.29.2.tgz#05c162f477c226ee3211f8ee8c1c6d4203e08f54"
|
||||||
|
integrity sha512-gtoHzimv7upsKF2DIO4/vNfIMKN+cxSImBHvsdiMyp9IPqb8sctsHVU/+80xXl0JKXVKeairDv5RvVnesJYrtw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
"@solana/buffer-layout" "^3.0.0"
|
||||||
|
bn.js "^5.0.0"
|
||||||
|
borsh "^0.4.0"
|
||||||
|
bs58 "^4.0.1"
|
||||||
|
buffer "6.0.1"
|
||||||
|
cross-fetch "^3.1.4"
|
||||||
|
crypto-hash "^1.2.2"
|
||||||
|
jayson "^3.4.4"
|
||||||
|
js-sha3 "^0.8.0"
|
||||||
|
rpc-websockets "^7.4.2"
|
||||||
|
secp256k1 "^4.0.2"
|
||||||
|
superstruct "^0.14.2"
|
||||||
|
tweetnacl "^1.0.0"
|
||||||
|
|
||||||
"@types/bn.js@^4.11.5":
|
"@types/bn.js@^4.11.5":
|
||||||
version "4.11.6"
|
version "4.11.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c"
|
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c"
|
||||||
|
@ -214,7 +246,7 @@ bn.js@^4.11.9:
|
||||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
|
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
|
||||||
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
|
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
|
||||||
|
|
||||||
bn.js@^5.0.0, bn.js@^5.1.2:
|
bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
|
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
|
||||||
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
|
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
|
||||||
|
@ -279,7 +311,7 @@ buffer@6.0.1:
|
||||||
base64-js "^1.3.1"
|
base64-js "^1.3.1"
|
||||||
ieee754 "^1.2.1"
|
ieee754 "^1.2.1"
|
||||||
|
|
||||||
buffer@~6.0.3:
|
buffer@6.0.3, buffer@~6.0.3:
|
||||||
version "6.0.3"
|
version "6.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
||||||
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||||
|
@ -434,6 +466,11 @@ dot-case@^3.0.4:
|
||||||
no-case "^3.0.4"
|
no-case "^3.0.4"
|
||||||
tslib "^2.0.3"
|
tslib "^2.0.3"
|
||||||
|
|
||||||
|
dotenv@10.0.0:
|
||||||
|
version "10.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
|
||||||
|
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
|
||||||
|
|
||||||
elliptic@^6.5.2:
|
elliptic@^6.5.2:
|
||||||
version "6.5.4"
|
version "6.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
|
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
|
||||||
|
|
Loading…
Reference in New Issue