descro resolvers extension
This commit is contained in:
13
programs/descro_ext_resolvers/src/error.rs
Normal file
13
programs/descro_ext_resolvers/src/error.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
#[error_code]
|
||||
pub enum RegistryError {
|
||||
#[msg("Signer is not the registered authority")]
|
||||
Unauthorized,
|
||||
#[msg("Caller is not the authorized escrow program")]
|
||||
UnauthorizedCaller,
|
||||
#[msg("Fee basis points must be <= 10000")]
|
||||
InvalidFeeBps,
|
||||
#[msg("Name must not be empty")]
|
||||
EmptyName,
|
||||
}
|
||||
7
programs/descro_ext_resolvers/src/instructions.rs
Normal file
7
programs/descro_ext_resolvers/src/instructions.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub mod register;
|
||||
pub mod update;
|
||||
pub mod update_stats;
|
||||
|
||||
pub use register::*;
|
||||
pub use update::*;
|
||||
pub use update_stats::*;
|
||||
47
programs/descro_ext_resolvers/src/instructions/register.rs
Normal file
47
programs/descro_ext_resolvers/src/instructions/register.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use anchor_lang::prelude::*;
|
||||
use crate::state::{ResolverEntry, ResolverType};
|
||||
use crate::error::RegistryError;
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct RegisterResolver<'info> {
|
||||
#[account(mut)]
|
||||
pub authority: Signer<'info>,
|
||||
|
||||
#[account(
|
||||
init,
|
||||
payer = authority,
|
||||
space = 8 + ResolverEntry::INIT_SPACE,
|
||||
seeds = [b"resolver", authority.key().as_ref()],
|
||||
bump
|
||||
)]
|
||||
pub resolver_entry: Account<'info, ResolverEntry>,
|
||||
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
||||
|
||||
pub fn handler(
|
||||
ctx: Context<RegisterResolver>,
|
||||
resolver_type: ResolverType,
|
||||
name: String,
|
||||
description: String,
|
||||
fee_bps: u16,
|
||||
fee_recipient: Pubkey,
|
||||
metadata_uri: String,
|
||||
) -> Result<()> {
|
||||
require!(!name.is_empty(), RegistryError::EmptyName);
|
||||
require!(fee_bps <= 10_000, RegistryError::InvalidFeeBps);
|
||||
|
||||
let entry = &mut ctx.accounts.resolver_entry;
|
||||
entry.authority = ctx.accounts.authority.key();
|
||||
entry.resolver_type = resolver_type;
|
||||
entry.name = name;
|
||||
entry.description = description;
|
||||
entry.fee_bps = fee_bps;
|
||||
entry.fee_recipient = fee_recipient;
|
||||
entry.metadata_uri = metadata_uri;
|
||||
entry.total_resolved = 0;
|
||||
entry.ruled_for_buyer = 0;
|
||||
entry.ruled_for_seller = 0;
|
||||
entry.registered_at = Clock::get()?.unix_timestamp;
|
||||
Ok(())
|
||||
}
|
||||
34
programs/descro_ext_resolvers/src/instructions/update.rs
Normal file
34
programs/descro_ext_resolvers/src/instructions/update.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use anchor_lang::prelude::*;
|
||||
use crate::state::ResolverEntry;
|
||||
use crate::error::RegistryError;
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct UpdateResolver<'info> {
|
||||
pub authority: Signer<'info>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
seeds = [b"resolver", authority.key().as_ref()],
|
||||
bump,
|
||||
constraint = resolver_entry.authority == authority.key() @ RegistryError::Unauthorized,
|
||||
)]
|
||||
pub resolver_entry: Account<'info, ResolverEntry>,
|
||||
}
|
||||
|
||||
pub fn handler(
|
||||
ctx: Context<UpdateResolver>,
|
||||
name: String,
|
||||
description: String,
|
||||
fee_bps: u16,
|
||||
metadata_uri: String,
|
||||
) -> Result<()> {
|
||||
require!(!name.is_empty(), RegistryError::EmptyName);
|
||||
require!(fee_bps <= 10_000, RegistryError::InvalidFeeBps);
|
||||
|
||||
let entry = &mut ctx.accounts.resolver_entry;
|
||||
entry.name = name;
|
||||
entry.description = description;
|
||||
entry.fee_bps = fee_bps;
|
||||
entry.metadata_uri = metadata_uri;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use anchor_lang::prelude::*;
|
||||
use crate::state::{ResolverEntry, Ruling};
|
||||
use crate::error::RegistryError;
|
||||
use crate::ESCROW_PROGRAM_ID;
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct UpdateStats<'info> {
|
||||
/// Must be the Escrow Program's authority PDA — only it can sign this.
|
||||
#[account(
|
||||
seeds = [b"escrow_authority"],
|
||||
bump,
|
||||
seeds::program = ESCROW_PROGRAM_ID,
|
||||
)]
|
||||
pub escrow_authority: Signer<'info>,
|
||||
|
||||
#[account(mut)]
|
||||
pub resolver_entry: Account<'info, ResolverEntry>,
|
||||
}
|
||||
|
||||
pub fn handler(ctx: Context<UpdateStats>, ruling: Ruling) -> Result<()> {
|
||||
// Verify the signer is truly derived from the escrow program (constraint handles this,
|
||||
// but log an explicit error if somehow reached with a wrong caller)
|
||||
let escrow_pda = Pubkey::find_program_address(&[b"escrow_authority"], &ESCROW_PROGRAM_ID).0;
|
||||
require!(
|
||||
ctx.accounts.escrow_authority.key() == escrow_pda,
|
||||
RegistryError::UnauthorizedCaller
|
||||
);
|
||||
|
||||
let entry = &mut ctx.accounts.resolver_entry;
|
||||
entry.total_resolved = entry.total_resolved.saturating_add(1);
|
||||
match ruling {
|
||||
Ruling::Buyer => entry.ruled_for_buyer = entry.ruled_for_buyer.saturating_add(1),
|
||||
Ruling::Seller => entry.ruled_for_seller = entry.ruled_for_seller.saturating_add(1),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
47
programs/descro_ext_resolvers/src/lib.rs
Normal file
47
programs/descro_ext_resolvers/src/lib.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
pub mod error;
|
||||
pub mod instructions;
|
||||
pub mod state;
|
||||
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
pub use error::*;
|
||||
pub use instructions::*;
|
||||
pub use state::*;
|
||||
|
||||
// Replace with actual program ID after first deployment
|
||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||
|
||||
/// Escrow Program ID — only its PDA may call update_stats.
|
||||
pub const ESCROW_PROGRAM_ID: Pubkey =
|
||||
pubkey!("DjVR4EuYV6USMJFfsGZwhZ3y8rtWsmG8EvDY96GTqqi3");
|
||||
|
||||
#[program]
|
||||
pub mod descro_ext_resolvers {
|
||||
use super::*;
|
||||
|
||||
pub fn register_resolver(
|
||||
ctx: Context<RegisterResolver>,
|
||||
resolver_type: ResolverType,
|
||||
name: String,
|
||||
description: String,
|
||||
fee_bps: u16,
|
||||
fee_recipient: Pubkey,
|
||||
metadata_uri: String,
|
||||
) -> Result<()> {
|
||||
register::handler(ctx, resolver_type, name, description, fee_bps, fee_recipient, metadata_uri)
|
||||
}
|
||||
|
||||
pub fn update_resolver(
|
||||
ctx: Context<UpdateResolver>,
|
||||
name: String,
|
||||
description: String,
|
||||
fee_bps: u16,
|
||||
metadata_uri: String,
|
||||
) -> Result<()> {
|
||||
update::handler(ctx, name, description, fee_bps, metadata_uri)
|
||||
}
|
||||
|
||||
pub fn update_stats(ctx: Context<UpdateStats>, ruling: Ruling) -> Result<()> {
|
||||
update_stats::handler(ctx, ruling)
|
||||
}
|
||||
}
|
||||
36
programs/descro_ext_resolvers/src/state.rs
Normal file
36
programs/descro_ext_resolvers/src/state.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
#[account]
|
||||
#[derive(InitSpace)]
|
||||
pub struct ResolverEntry {
|
||||
pub authority: Pubkey,
|
||||
pub resolver_type: ResolverType,
|
||||
#[max_len(64)]
|
||||
pub name: String,
|
||||
#[max_len(256)]
|
||||
pub description: String,
|
||||
pub fee_bps: u16,
|
||||
pub fee_recipient: Pubkey,
|
||||
#[max_len(256)]
|
||||
pub metadata_uri: String,
|
||||
pub total_resolved: u64,
|
||||
pub ruled_for_buyer: u64,
|
||||
pub ruled_for_seller: u64,
|
||||
pub registered_at: i64,
|
||||
}
|
||||
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq, InitSpace, Debug)]
|
||||
pub enum ResolverType {
|
||||
CentralAuthority,
|
||||
JuryDAO,
|
||||
MAD,
|
||||
Algorithmic,
|
||||
Multisig,
|
||||
}
|
||||
|
||||
/// Passed to update_stats; mirrors the Escrow program's Winner enum.
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
|
||||
pub enum Ruling {
|
||||
Buyer,
|
||||
Seller,
|
||||
}
|
||||
Reference in New Issue
Block a user