Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
420 changes: 179 additions & 241 deletions packages/svm/idls/controller.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/svm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"litesvm": "=0.1.0"
},
"devDependencies": {
"@mimicprotocol/sdk": "0.0.1-rc.20",
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.20",
"@types/mocha": "^10.0.10",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use anchor_lang::prelude::*;

use crate::{
errors::ControllerError,
state::{EntityRegistry, GlobalSettings},
state::{ControllerSettings, EntityRegistry},
types::EntityType,
};

Expand All @@ -21,11 +21,11 @@ pub struct CloseEntityRegistry<'info> {
pub entity_registry: Box<Account<'info, EntityRegistry>>,

#[account(
seeds = [b"global-settings"],
bump = global_settings.bump,
seeds = [b"controller-settings"],
bump = controller_settings.bump,
has_one = admin @ ControllerError::OnlyAdmin
)]
pub global_settings: Box<Account<'info, GlobalSettings>>,
pub controller_settings: Box<Account<'info, ControllerSettings>>,

pub system_program: Program<'info, System>,
}
Expand Down
14 changes: 7 additions & 7 deletions packages/svm/programs/controller/src/instructions/initialize.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anchor_lang::prelude::*;
use std::str::FromStr;

use crate::{constants::DEPLOYER_KEY, errors::ControllerError, state::GlobalSettings};
use crate::{constants::DEPLOYER_KEY, errors::ControllerError, state::ControllerSettings};

#[derive(Accounts)]
pub struct Initialize<'info> {
Expand All @@ -10,12 +10,12 @@ pub struct Initialize<'info> {

#[account(
init,
seeds = [b"global-settings"],
seeds = [b"controller-settings"],
bump,
payer = deployer,
space = 8 + GlobalSettings::INIT_SPACE
space = 8 + ControllerSettings::INIT_SPACE
)]
pub global_settings: Box<Account<'info, GlobalSettings>>,
pub controller_settings: Box<Account<'info, ControllerSettings>>,

pub system_program: Program<'info, System>,
}
Expand All @@ -27,10 +27,10 @@ pub fn initialize(ctx: Context<Initialize>, admin: Pubkey) -> Result<()> {
ControllerError::OnlyDeployer,
);

let global_settings = &mut ctx.accounts.global_settings;
let controller_settings = &mut ctx.accounts.controller_settings;

global_settings.admin = admin;
global_settings.bump = ctx.bumps.global_settings;
controller_settings.admin = admin;
controller_settings.bump = ctx.bumps.controller_settings;

Ok(())
}
12 changes: 6 additions & 6 deletions packages/svm/programs/controller/src/instructions/set_admin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anchor_lang::prelude::*;

use crate::{errors::ControllerError, state::GlobalSettings};
use crate::{errors::ControllerError, state::ControllerSettings};

#[derive(Accounts)]
pub struct SetAdmin<'info> {
Expand All @@ -9,17 +9,17 @@ pub struct SetAdmin<'info> {

#[account(
mut,
seeds = [b"global-settings"],
bump = global_settings.bump,
seeds = [b"controller-settings"],
bump = controller_settings.bump,
has_one = admin @ ControllerError::OnlyAdmin
)]
pub global_settings: Box<Account<'info, GlobalSettings>>,
pub controller_settings: Box<Account<'info, ControllerSettings>>,
}

pub fn set_admin(ctx: Context<SetAdmin>, new_admin: Pubkey) -> Result<()> {
let global_settings = &mut ctx.accounts.global_settings;
let controller_settings = &mut ctx.accounts.controller_settings;

global_settings.admin = new_admin;
controller_settings.admin = new_admin;

emit!(SetAdminEvent {
new_admin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use anchor_lang::prelude::*;

use crate::{
errors::ControllerError,
state::{EntityRegistry, GlobalSettings},
state::{ControllerSettings, EntityRegistry},
types::EntityType,
};

Expand All @@ -22,11 +22,11 @@ pub struct SetAllowedEntity<'info> {
pub entity_registry: Box<Account<'info, EntityRegistry>>,

#[account(
seeds = [b"global-settings"],
bump = global_settings.bump,
seeds = [b"controller-settings"],
bump = controller_settings.bump,
has_one = admin @ ControllerError::OnlyAdmin
)]
pub global_settings: Box<Account<'info, GlobalSettings>>,
pub controller_settings: Box<Account<'info, ControllerSettings>>,

pub system_program: Program<'info, System>,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use anchor_lang::prelude::*;

#[account]
#[derive(InitSpace)]
pub struct GlobalSettings {
pub struct ControllerSettings {
pub admin: Pubkey,
pub bump: u8,
}
4 changes: 2 additions & 2 deletions packages/svm/programs/controller/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod controller_settings;
pub mod entity_registry;
pub mod global_settings;

pub use controller_settings::*;
pub use entity_registry::*;
pub use global_settings::*;
9 changes: 8 additions & 1 deletion packages/svm/programs/settler/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[package]
name = "settler"
version = "0.1.0"
description = "Created with Anchor"
description = "Manages on-chain intent settlement for Mimic protocol"
edition = "2021"
license = "GPL-3.0"

[lib]
crate-type = ["cdylib", "lib"]
Expand All @@ -15,6 +16,12 @@ no-entrypoint = []
no-idl = []
no-log-ix-name = []
idl-build = ["anchor-lang/idl-build"]
anchor-debug = []
custom-heap = []
custom-panic = []

[dependencies]
anchor-lang = { version = "0.32.1", features = [ "init-if-needed" ] }

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] }
4 changes: 4 additions & 0 deletions packages/svm/programs/settler/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub const DEPLOYER_KEY: &str = env!(
"DEPLOYER_KEY",
"Please set the DEPLOYER_KEY env variable before compiling."
);
79 changes: 79 additions & 0 deletions packages/svm/programs/settler/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use anchor_lang::prelude::*;

#[error_code]
pub enum SettlerError {
#[msg("Only Deployer can call this instruction")]
OnlyDeployer,

#[msg("Only an allowlisted solver can call this instruction")]
OnlySolver,

#[msg("Provided Axia address is not allowlisted")]
AxiaNotAllowlisted,

#[msg("Only a allowlisted validator can call this instruction")]
OnlyValidator,

#[msg("No max fees provided")]
NoMaxFees,

#[msg("Validator is not allowlisted")]
ValidatorNotAllowlisted,

#[msg("Signer must be intent creator")]
IncorrectIntentCreator,

#[msg("Signer must be proposal creator")]
IncorrectProposalCreator,

#[msg("Intent is already final")]
IntentIsFinal,

#[msg("Intent is not final")]
IntentIsNotFinal,

#[msg("Proposal is already final")]
ProposalIsFinal,

#[msg("Proposal is not final")]
ProposalIsNotFinal,

#[msg("Intent not yet expired. Please wait for the deadline to pass")]
IntentNotYetExpired,

#[msg("Intent has already expired")]
IntentIsExpired,

#[msg("Proposal not yet expired. Please wait for the deadline to pass")]
ProposalNotYetExpired,

#[msg("Proposal has already expired")]
ProposalIsExpired,

#[msg("Deadline must be in the future")]
DeadlineIsInThePast,

#[msg("Proposal deadline can't be after the Intent's deadline")]
ProposalDeadlineExceedsIntentDeadline,

#[msg("Intent has insufficient validations")]
InsufficientIntentValidations,

#[msg("Signature verification failed")]
SigVerificationFailed,

#[msg("Incorrect intent for proposal")]
IncorrectIntentForProposal,

#[msg("Proposal is not signed by Axia")]
ProposalIsNotSigned,

#[msg("Invalid fee mint")]
InvalidFeeMint,

#[msg("Fee amount exceeds max fee")]
FeeAmountExceedsMaxFee,

#[msg("Math Error")]
MathError,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use anchor_lang::prelude::*;

use crate::{errors::SettlerError, state::Intent};

#[derive(Accounts)]
pub struct ClaimStaleIntent<'info> {
#[account(mut)]
pub creator: Signer<'info>,

#[account(
mut,
close = creator,
has_one = creator @ SettlerError::IncorrectIntentCreator,
)]
pub intent: Box<Account<'info, Intent>>,
}

pub fn claim_stale_intent(ctx: Context<ClaimStaleIntent>) -> Result<()> {
let now = Clock::get()?.unix_timestamp as u64;

require!(
ctx.accounts.intent.deadline < now,
SettlerError::IntentNotYetExpired
);

Ok(())
}
81 changes: 81 additions & 0 deletions packages/svm/programs/settler/src/instructions/create_intent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use anchor_lang::prelude::*;

use crate::{
controller::{self, accounts::EntityRegistry, types::EntityType},
errors::SettlerError,
state::Intent,
types::{IntentEvent, OpType, TokenFee},
};

#[derive(Accounts)]
// TODO: can we optimize this deser? we just need the three Vec<T> for their length
#[instruction(intent_hash: [u8; 32], data: Vec<u8>, max_fees: Vec<TokenFee>, events: Vec<IntentEvent>, min_validations: u16)]
pub struct CreateIntent<'info> {
#[account(mut)]
pub solver: Signer<'info>,

#[account(
seeds = [b"entity-registry", &[EntityType::Solver as u8 + 1], solver.key().as_ref()],
bump = solver_registry.bump,
seeds::program = controller::ID
)]
pub solver_registry: Box<Account<'info, EntityRegistry>>,

#[account(
init,
seeds = [b"intent", intent_hash.as_ref()],
bump,
payer = solver,
space = Intent::total_size(data.len(), max_fees.len(), &events, min_validations)?
)]
// TODO: change to AccountLoader?
// TODO: init within the handler body to save compute?
pub intent: Box<Account<'info, Intent>>,

#[account(
seeds = [b"fulfilled-intent", intent_hash.as_ref()],
bump
)]
/// This PDA must be uninitialized
pub fulfilled_intent: SystemAccount<'info>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we doing something with this account?

Copy link
Member Author

@GuidoDipietro GuidoDipietro Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We check this in a future instruction to validate that a given intent has not already been executed on-chain.
I.e., this is our on-chain proof that a given intent was fulfilled.


pub system_program: Program<'info, System>,
}

pub fn create_intent(
ctx: Context<CreateIntent>,
intent_hash: [u8; 32],
data: Vec<u8>,
max_fees: Vec<TokenFee>,
events: Vec<IntentEvent>,
min_validations: u16,
op: OpType,
user: Pubkey,
nonce: [u8; 32],
deadline: u64,
is_final: bool,
) -> Result<()> {
let now = Clock::get()?.unix_timestamp as u64;
require!(deadline > now, SettlerError::DeadlineIsInThePast);
require!(max_fees.len() > 0, SettlerError::NoMaxFees);

// TODO: check hash

let intent = &mut ctx.accounts.intent;

intent.op = op;
intent.user = user;
intent.creator = ctx.accounts.solver.key();
intent.hash = intent_hash;
intent.nonce = nonce;
intent.deadline = deadline;
intent.min_validations = min_validations;
intent.is_final = is_final;
intent.data = data;
intent.max_fees = max_fees;
intent.events = events;
intent.validators = vec![];
intent.bump = ctx.bumps.intent;

Ok(())
}
Loading