Skip to content

Commit f0aabc7

Browse files
pileksmetapileks
andauthored
Gated mint program (#456)
* wip * wip * implementation of initiation of new optimistic governance proposal * add finalize optimistic proposal ix * launch_proposal should only be able to challenge an optimistic proposal if one exists * minor cleanups * tests for optimistic vault transaction proposal initiation * optimistic proposal finalization tests * prevent initialization of futarchy proposal when optimistic governance proposal has already passed but not been finalized yet * add missing check * team can't sponsor a challenge to an optimistic governance proposal * work-in-progress PR comments * address review comments * reintroduce resize_dao crankable instruction with scripts * reintroduce resize_dao crankable instruction with scripts (#400) * remove duplicate optimistic proposal check * initiate vault spend optimistic proposal won't fail due to reentrancy from squads * increase payer usdc balance to prevent tests from depleting entire balance * prevent udapte_dao during active optimistic proposal * prevent admin execution of multisig proposals when there is an active optimistic governance proposal * prevent deadlock from optimistic proposal staleness * ensure squads vault transaction is created by permissionless account * additional checks for proper transaction in vault spend optimistic proposals * finalize resize dao logic, add scripts for dumping and resizing daos * initial layout for v2 SDK * add more programs to new SDK * new sdk - performance package v2 * new sdk - launchpad v6 program * proper exports for all sdk v2 types * proper re-exports * add pricemath * move tests to v2 sdk * fix minor ts issues * add relevant 0.5 clients to sdk v2 * add 0.4 clients to sdk v2 * update all new functionality to sdk v2 * init launchpad v8 * launchpad v8 scaffolding * sdk scaffolding * tests scaffolding * initialize launch ix * init launch sdk * init launch tests * start launch ix * fund ix * close launch ix * update sdk ixs * start launch test * fund tests * close launch tests * set funding record approval ix * set funding record approval sdk * set funding record tests * settle launch ix * settle launch sdk * settle launch ix tests + finalization * return of the set funding record approval test * claim ix * refund ix * claim additional tokens ix * claim refund and claim addtional ixs * claim tests * refund tests * claim additional tokens tests * finalize launch ix * finalize launch ix * finalize launch tests * extend launch ix * exten launch sdk * extend launch tests * launchpad v8 integration test * kill vibes dir * kill vibes dir * apply latest program changes to new SDK * remove unnecessary logic from initialize_launch * remove unnecessary event field, formatting * proper naming, more checks * add immediate ability to mint to dao's squads multisig vault * add sdk2 to CI * enable optimistic governance by default for DAOs initialized with a team address * remove unused account * reintroduce disabling of optimistic governance on dao initialization and migration * final cleanup pass * update sdk * update sdkv2 with changes from old sdk * rename autocrat to futarchy in v0.6 clients * rename autocratClient to futarchyClient * sdk2 is now main sdk * fix up typescript errors in tests * add v0.3 clients, fix autocomplete behavior for imports * update scripts * restore legacy v0.6.0 IDLs and v0.5 simulateSwap helper * replace old SDK dir with new one * update workflow to not include sdk2 anymore * redeploy SDK under new name * remove unused dependencies * update sdk package version * remove unused files * adds full lifecycle launchpad_v8 -> performance_package_v2 + mint_governor test * prepare specs and ralph loop * init gated_token * gated_token scaffold * init gated mint * add whitelisted user * disable gating * permissionless account thaw * gated invoke * add launchpad_v8 support, clean up tests * rename to gated_mint * add gh workflows * add whitelist_admin * add remove_whitelisted_user ix * update ackee audit report * exact versions in gated_mint Cargo.toml * add gated_mint to README --------- Co-authored-by: Pileks <jure@metadao.fi>
1 parent 70f6b75 commit f0aabc7

43 files changed

Lines changed: 5063 additions & 4 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Anchor.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ skip-lint = false
99
bid_wall = "WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx"
1010
conditional_vault = "VLTX1ishMBbcX3rdBWGssxawAo1Q2X2qxYFYqiGodVg"
1111
futarchy = "FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq"
12+
gated_mint = "GaTEjZy6eMdHg2BcL8dk3iE78jkJ9sPtyw1q2tMNi8PA"
1213
launchpad = "MooNyh4CBUYEKyXVnjGYQ8mEiJDpGvJMdvrZx1iGeHV"
1314
launchpad_v7 = "moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM"
1415
launchpad_v8 = "moonDJUoHteKkGATejA5bdJVwJ6V6Dg74gyqyJTx73n"

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Programs for unruggable capital formation and market-driven governance.
88

99
| program | tag | program ID |
1010
| ----------------- | ---- | -------------------------------------------- |
11+
| gated_mint | v0.1.0 | GaTEjZy6eMdHg2BcL8dk3iE78jkJ9sPtyw1q2tMNi8PA |
1112
| launchpad | v0.8.0 | moonDJUoHteKkGATejA5bdJVwJ6V6Dg74gyqyJTx73n |
1213
| launchpad | v0.7.0 | moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM |
1314
| bid_wall | v0.7.0 | WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx |

audits/2026-ackee-blockchain-metadao-the-fundraising-and-report-v2-1.pdf renamed to audits/2026-ackee-blockchain-metadao-the-fundraising-and-report-v4-1.pdf

1.78 MB
Binary file not shown.

programs/gated_mint/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "gated_mint"
3+
version = "0.1.0"
4+
description = "Created with Anchor"
5+
edition = "2021"
6+
7+
[lib]
8+
crate-type = ["cdylib", "lib"]
9+
name = "gated_mint"
10+
11+
[features]
12+
no-entrypoint = []
13+
no-idl = []
14+
no-log-ix-name = []
15+
cpi = ["no-entrypoint"]
16+
default = []
17+
production = []
18+
19+
[dependencies]
20+
anchor-lang = { version = "=0.29.0", features = ["init-if-needed", "event-cpi"] }
21+
anchor-spl = "=0.29.0"
22+
solana-program = "=1.17.14"
23+
spl-token = "=4.0.0"
24+
solana-security-txt = "=1.1.1"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use anchor_lang::prelude::Pubkey;
2+
use anchor_lang::solana_program::pubkey;
3+
4+
pub const WHITELISTED_PROGRAMS: &[Pubkey] = &[
5+
pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
6+
pubkey!("FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq"),
7+
pubkey!("moonDJUoHteKkGATejA5bdJVwJ6V6Dg74gyqyJTx73n"),
8+
pubkey!("VLTX1ishMBbcX3rdBWGssxawAo1Q2X2qxYFYqiGodVg"),
9+
pubkey!("WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx"),
10+
pubkey!("gvnr27cVeyW3AVf3acL7VCJ5WjGAphytnsgcK1feHyH"),
11+
pubkey!("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"),
12+
];
13+
14+
pub const TOKEN_ACCOUNT_LEN: usize = 165;
15+
pub const TOKEN_ACCOUNT_MINT_OFFSET: usize = 0;
16+
pub const TOKEN_ACCOUNT_STATE_OFFSET: usize = 108;
17+
18+
pub const TOKEN_STATE_UNINITIALIZED: u8 = 0;
19+
pub const TOKEN_STATE_INITIALIZED: u8 = 1;
20+
pub const TOKEN_STATE_FROZEN: u8 = 2;

programs/gated_mint/src/error.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use anchor_lang::prelude::*;
2+
3+
#[error_code]
4+
pub enum GatedMintError {
5+
#[msg("Unauthorized: signer is not the gated mint admin")]
6+
UnauthorizedAdmin,
7+
#[msg("Unauthorized: signer is not the current freeze authority of the mint")]
8+
UnauthorizedFreezeAuthority,
9+
#[msg("Mint mismatch: account does not match the expected gated mint")]
10+
MintMismatch,
11+
#[msg("Gating is already disabled for this mint")]
12+
GatingDisabled,
13+
#[msg("Gating must be disabled to call this instruction")]
14+
GatingNotDisabled,
15+
#[msg("Target program is not on the gated_mint whitelist")]
16+
TargetProgramNotWhitelisted,
17+
#[msg("Target program may not be the gated_mint program itself")]
18+
SelfInvocation,
19+
#[msg("Invalid token account: account is not a valid SPL Token account of the gated mint")]
20+
InvalidTokenAccount,
21+
#[msg("Unauthorized: signer is neither admin nor whitelist admin")]
22+
UnauthorizedWhitelistAuthority,
23+
#[msg("Whitelist admin may not equal admin")]
24+
InvalidWhitelistAdmin,
25+
}

programs/gated_mint/src/events.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use anchor_lang::prelude::*;
2+
3+
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
4+
pub struct CommonFields {
5+
pub slot: u64,
6+
pub unix_timestamp: i64,
7+
pub gated_mint_config_seq_num: u64,
8+
}
9+
10+
impl CommonFields {
11+
pub fn new(clock: &Clock, seq_num: u64) -> Self {
12+
Self {
13+
slot: clock.slot,
14+
unix_timestamp: clock.unix_timestamp,
15+
gated_mint_config_seq_num: seq_num,
16+
}
17+
}
18+
}
19+
20+
#[event]
21+
pub struct GatedMintInitializedEvent {
22+
pub common: CommonFields,
23+
pub gated_mint_config: Pubkey,
24+
pub mint: Pubkey,
25+
pub admin: Pubkey,
26+
pub whitelist_admin: Option<Pubkey>,
27+
pub previous_freeze_authority: Pubkey,
28+
pub pda_bump: u8,
29+
}
30+
31+
#[event]
32+
pub struct WhitelistedUserAddedEvent {
33+
pub common: CommonFields,
34+
pub gated_mint_config: Pubkey,
35+
pub whitelisted_user: Pubkey,
36+
pub mint: Pubkey,
37+
pub user: Pubkey,
38+
pub authority: Pubkey,
39+
}
40+
41+
#[event]
42+
pub struct WhitelistedUserRemovedEvent {
43+
pub common: CommonFields,
44+
pub gated_mint_config: Pubkey,
45+
pub whitelisted_user: Pubkey,
46+
pub mint: Pubkey,
47+
pub user: Pubkey,
48+
pub authority: Pubkey,
49+
}
50+
51+
#[event]
52+
pub struct WhitelistAdminSetEvent {
53+
pub common: CommonFields,
54+
pub gated_mint_config: Pubkey,
55+
pub mint: Pubkey,
56+
pub previous_whitelist_admin: Option<Pubkey>,
57+
pub new_whitelist_admin: Option<Pubkey>,
58+
}
59+
60+
#[event]
61+
pub struct GatedInvokeEvent {
62+
pub common: CommonFields,
63+
pub gated_mint_config: Pubkey,
64+
pub mint: Pubkey,
65+
pub caller: Pubkey,
66+
pub target_program: Pubkey,
67+
pub thawed_count: u32,
68+
pub frozen_count: u32,
69+
}
70+
71+
#[event]
72+
pub struct GatingDisabledEvent {
73+
pub common: CommonFields,
74+
pub gated_mint_config: Pubkey,
75+
pub mint: Pubkey,
76+
}
77+
78+
#[event]
79+
pub struct AccountThawedEvent {
80+
pub common: CommonFields,
81+
pub gated_mint_config: Pubkey,
82+
pub mint: Pubkey,
83+
pub token_account: Pubkey,
84+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use anchor_lang::prelude::*;
2+
use anchor_spl::token::Mint;
3+
4+
use crate::{
5+
CommonFields, GatedMintConfig, GatedMintError, WhitelistedUser, WhitelistedUserAddedEvent,
6+
WHITELISTED_USER_SEED,
7+
};
8+
9+
#[event_cpi]
10+
#[derive(Accounts)]
11+
pub struct AddWhitelistedUser<'info> {
12+
#[account(
13+
mut,
14+
has_one = mint @ GatedMintError::MintMismatch,
15+
constraint = !gated_mint_config.gating_disabled @ GatedMintError::GatingDisabled,
16+
)]
17+
pub gated_mint_config: Account<'info, GatedMintConfig>,
18+
19+
pub authority: Signer<'info>,
20+
21+
pub mint: Account<'info, Mint>,
22+
23+
/// CHECK: any pubkey may be whitelisted; not signed.
24+
pub user: UncheckedAccount<'info>,
25+
26+
#[account(
27+
init,
28+
payer = payer,
29+
space = 8 + WhitelistedUser::INIT_SPACE,
30+
seeds = [WHITELISTED_USER_SEED, mint.key().as_ref(), user.key().as_ref()],
31+
bump,
32+
)]
33+
pub whitelisted_user: Account<'info, WhitelistedUser>,
34+
35+
#[account(mut)]
36+
pub payer: Signer<'info>,
37+
38+
pub system_program: Program<'info, System>,
39+
}
40+
41+
impl AddWhitelistedUser<'_> {
42+
pub fn validate(&self) -> Result<()> {
43+
let signer = self.authority.key();
44+
let is_admin = signer.eq(&self.gated_mint_config.admin);
45+
let is_whitelist_admin = self
46+
.gated_mint_config
47+
.whitelist_admin
48+
.map(|wa| wa.eq(&signer))
49+
.unwrap_or(false);
50+
require!(
51+
is_admin || is_whitelist_admin,
52+
GatedMintError::UnauthorizedWhitelistAuthority
53+
);
54+
Ok(())
55+
}
56+
57+
pub fn handle(ctx: Context<Self>) -> Result<()> {
58+
let cfg = &mut ctx.accounts.gated_mint_config;
59+
cfg.seq_num += 1;
60+
61+
ctx.accounts.whitelisted_user.set_inner(WhitelistedUser {
62+
mint: ctx.accounts.mint.key(),
63+
user: ctx.accounts.user.key(),
64+
bump: ctx.bumps.whitelisted_user,
65+
});
66+
67+
let clock = Clock::get()?;
68+
emit_cpi!(WhitelistedUserAddedEvent {
69+
common: CommonFields::new(&clock, cfg.seq_num),
70+
gated_mint_config: cfg.key(),
71+
whitelisted_user: ctx.accounts.whitelisted_user.key(),
72+
mint: ctx.accounts.mint.key(),
73+
user: ctx.accounts.user.key(),
74+
authority: ctx.accounts.authority.key(),
75+
});
76+
77+
Ok(())
78+
}
79+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use anchor_lang::prelude::*;
2+
3+
use crate::{CommonFields, GatedMintConfig, GatedMintError, GatingDisabledEvent};
4+
5+
#[event_cpi]
6+
#[derive(Accounts)]
7+
pub struct DisableGating<'info> {
8+
#[account(
9+
mut,
10+
constraint = !gated_mint_config.gating_disabled @ GatedMintError::GatingDisabled,
11+
)]
12+
pub gated_mint_config: Account<'info, GatedMintConfig>,
13+
14+
#[account(address = gated_mint_config.admin @ GatedMintError::UnauthorizedAdmin)]
15+
pub admin: Signer<'info>,
16+
}
17+
18+
impl DisableGating<'_> {
19+
pub fn validate(&self) -> Result<()> {
20+
Ok(())
21+
}
22+
23+
pub fn handle(ctx: Context<Self>) -> Result<()> {
24+
let cfg = &mut ctx.accounts.gated_mint_config;
25+
cfg.gating_disabled = true;
26+
cfg.seq_num += 1;
27+
28+
let clock = Clock::get()?;
29+
emit_cpi!(GatingDisabledEvent {
30+
common: CommonFields::new(&clock, cfg.seq_num),
31+
gated_mint_config: cfg.key(),
32+
mint: cfg.mint,
33+
});
34+
35+
Ok(())
36+
}
37+
}

0 commit comments

Comments
 (0)