maw: asymmetric curve engine

proof of deployment

every step that produced the deployed program. build, source, deployment, and finalization. nothing has been omitted.

1. build transcript

$ anchor build
   Compiling proc-macro2 v1.0.86
   Compiling unicode-ident v1.0.12
   Compiling syn v2.0.72
   Compiling serde v1.0.204
   Compiling serde_derive v1.0.204
   Compiling thiserror v1.0.63
   Compiling bytemuck v1.16.3
   Compiling num-traits v0.2.19
   Compiling num-derive v0.4.2
   Compiling borsh v1.5.1
   Compiling borsh-derive v1.5.1
   Compiling solana-program v1.18.22
   Compiling anchor-lang v0.30.1
   Compiling anchor-lang-idl v0.1.1
   Compiling anchor-attribute-program v0.30.1
   Compiling anchor-attribute-account v0.30.1
   Compiling anchor-spl v0.30.1
   Compiling spl-token v6.0.0
   Compiling spl-token-2022 v4.0.0
   Compiling spl-transfer-hook-interface v0.6.3
   Compiling spl-tlv-account-resolution v0.6.3
   Compiling spl-pod v0.3.0
   Compiling spl-type-length-value v0.5.0
   Compiling moss-hook v1.0.0 (/build/programs/moss_hook)
    Finished release [optimized] target(s) in 3m 41s
     Running `anchor idl build --out target/idl/moss_hook.json`
       Wrote target/idl/moss_hook.json (14.2 KiB)
       Wrote target/types/moss_hook.ts (18.7 KiB)
     Checking bpf target compat ... ok
     bpf-size: text=196.4KiB rodata=12.8KiB data=0B bss=0B
     verifiable build hash: 8b41ec6f3d2a5c19d4e0b7a986f1c3aa2e5d9b0c74f8123ae6d70f9b45c8e112

2. the program

programs/moss_hook/src/lib.rs

use anchor_lang::prelude::*;
use anchor_spl::token_2022::spl_token_2022::{
    extension::{transfer_hook::TransferHookAccount, StateWithExtensions},
    state::Account as Token22Account,
};
use spl_transfer_hook_interface::instruction::{ExecuteInstruction, TransferHookInstruction};
use spl_tlv_account_resolution::state::ExtraAccountMetaList;

mod decay;
mod split;
mod state;
mod errors;

use crate::decay::{decay_balance, epochs_since};
use crate::split::{apply_split, SplitOutcome};
use crate::state::{Colony, ShelterVault, Reserve, RESERVE_SEED, SHELTER_SEED};
use crate::errors::MossError;

declare_id!("mossv1qQfW7gK3rDt8yHnXcVbNmP4sLjE2wAuT9iR6oZe7gRw");

/// demurrage constant. 0.15% per saeculum, expressed in parts-per-million.
pub const LAMBDA_PPM: u64 = 1_500;

/// rot split, in basis points. must sum to 10_000.
pub const SPLIT_SHELTER_BPS: u16 = 7_000;
pub const SPLIT_RESERVE_BPS: u16 = 2_500;
pub const SPLIT_TENDER_BPS: u16 = 500;

/// an account must be stale at least this many saecula before tend() succeeds.
pub const MIN_STALE_EPOCHS: u64 = 1;

/// fixed supply. 2^24. minted once at genesis, never again.
pub const TOTAL_SUPPLY: u64 = 16_777_216;

/// token-2022 transfer-fee extension, expressed in basis points.
pub const TRANSFER_FEE_BPS: u16 = 25;

// static assertion: split constants must sum to full basis points.
const _: () = assert!(
    (SPLIT_SHELTER_BPS as u32) + (SPLIT_RESERVE_BPS as u32) + (SPLIT_TENDER_BPS as u32) == 10_000,
    "split invariant broken at compile time"
);

#[program]
pub mod moss_hook {
    use super::*;

    /// invoked once at genesis to register the ExtraAccountMetaList used by
    /// the transfer-hook interface. once written, the account is closed to
    /// updates by handing its authority to the program-derived signer only.
    pub fn initialize_extra_metas(ctx: Context<InitExtraMetas>) -> Result<()> {
        let metas = crate::state::extra_metas_for_execute();
        let mut data = ctx.accounts.extra_metas.try_borrow_mut_data()?;
        ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas)?;
        Ok(())
    }

    /// invoked by the token-2022 program on EVERY transfer of moss, via CPI.
    /// there is no transfer path that does not pass through here.
    pub fn transfer_hook_execute(ctx: Context<TransferHookExecute>, amount: u64) -> Result<()> {
        let clock = Clock::get()?;

        realize_rot(&mut ctx.accounts.source_colony, &clock)?;
        realize_rot(&mut ctx.accounts.destination_colony, &clock)?;

        require!(
            ctx.accounts.source_colony.shadow_balance >= amount,
            MossError::RotExceedsTransfer
        );

        // token-2022 charged its 25 bps fee before entering the hook; the
        // withheld amount lives on the mint until harvest_epoch sweeps it.

        emit!(RotRealized {
            epoch: clock.epoch,
            slot: clock.slot,
            amount,
        });

        Ok(())
    }

    /// permissionless: any wallet may tend a colony stale for >= 1 saeculum.
    /// computes the rot owed, distributes 70/25/5, and updates state.
    pub fn tend(ctx: Context<Tend>) -> Result<()> {
        let clock = Clock::get()?;
        let colony = &mut ctx.accounts.colony;

        let n = epochs_since(colony.last_touch_epoch, clock.epoch)?;
        require!(n >= MIN_STALE_EPOCHS, MossError::NotStaleEnough);

        let before = colony.shadow_balance;
        let after = decay_balance(before, n)?;
        let rot = before
            .checked_sub(after)
            .ok_or(MossError::MathOverflow)?;
        require!(rot > 0, MossError::NothingToRealize);

        let SplitOutcome { shelter, reserve, tender } = apply_split(rot)?;
        require!(
            shelter.checked_add(reserve)
                .and_then(|s| s.checked_add(tender))
                .ok_or(MossError::MathOverflow)? == rot,
            MossError::SplitInvariantBroken
        );

        // move the swept moss onto the shelter vault, reserve, and tender ATAs
        // via token-2022 CPIs. transfers here re-enter the hook, but shadow
        // balances on those accounts are marked "sink" and skip decay.
        crate::split::route_shelter(&ctx, shelter)?;
        crate::split::route_reserve(&ctx, reserve)?;
        crate::split::route_tender(&ctx, tender)?;

        colony.shadow_balance = after;
        colony.last_touch_epoch = clock.epoch;

        emit!(Tended {
            colony: colony.key(),
            epochs: n,
            rot,
            shelter,
            reserve,
            tender,
            caller: ctx.accounts.tender_signer.key(),
        });

        Ok(())
    }

    /// move moss into the shelter vault. sheltered balances freeze against
    /// rot and receive 70% of every realized sweep, pro-rata at saeculum boundary.
    pub fn shelter_deposit(ctx: Context<ShelterDeposit>, amount: u64) -> Result<()> {
        let clock = Clock::get()?;

        realize_rot(&mut ctx.accounts.source_colony, &clock)?;
        require!(
            ctx.accounts.source_colony.shadow_balance >= amount,
            MossError::InsufficientFunds
        );

        let vault = &mut ctx.accounts.shelter_vault;
        vault.total_sheltered = vault
            .total_sheltered
            .checked_add(amount)
            .ok_or(MossError::MathOverflow)?;

        let stake = &mut ctx.accounts.stake;
        stake.owner = ctx.accounts.owner.key();
        stake.amount = stake
            .amount
            .checked_add(amount)
            .ok_or(MossError::MathOverflow)?;
        stake.entered_epoch = clock.epoch;

        crate::split::pull_into_shelter(&ctx, amount)?;

        emit!(Sheltered {
            owner: ctx.accounts.owner.key(),
            amount,
            epoch: clock.epoch,
        });

        Ok(())
    }

    /// leave the shelter. there is no lock, no cooldown, no fee. the moment
    /// the stake unwinds, the caller's clock starts again.
    pub fn shelter_withdraw(ctx: Context<ShelterWithdraw>, amount: u64) -> Result<()> {
        let clock = Clock::get()?;
        let stake = &mut ctx.accounts.stake;
        require!(stake.amount >= amount, MossError::ShelterEmpty);

        let vault = &mut ctx.accounts.shelter_vault;
        vault.total_sheltered = vault
            .total_sheltered
            .checked_sub(amount)
            .ok_or(MossError::MathOverflow)?;

        stake.amount = stake
            .amount
            .checked_sub(amount)
            .ok_or(MossError::MathOverflow)?;

        crate::split::push_from_shelter(&ctx, amount)?;

        // the destination colony inherits a fresh last_touch, because the
        // token-2022 transfer will re-enter transfer_hook_execute anyway.

        emit!(Unsheltered {
            owner: ctx.accounts.owner.key(),
            amount,
            epoch: clock.epoch,
        });

        Ok(())
    }

    /// once per saeculum, anyone may call this to (1) harvest the withheld
    /// transfer-fee tokens off the mint into the reserve, and (2) recenter
    /// the reserve's concentrated-liquidity range in the moss/sol pool.
    /// gated by a pyth twap deviation check; executed only in a jito bundle.
    pub fn harvest_epoch(ctx: Context<HarvestEpoch>) -> Result<()> {
        let clock = Clock::get()?;
        let reserve = &mut ctx.accounts.reserve;
        require!(
            clock.epoch > reserve.last_harvest_epoch,
            MossError::AlreadyHarvestedThisEpoch
        );

        crate::split::harvest_withheld_to_reserve(&ctx)?;
        crate::split::recenter_reserve_liquidity(&ctx)?;

        reserve.last_harvest_epoch = clock.epoch;
        Ok(())
    }
}

/// realize any rot owed since last touch and update the colony's shadow
/// balance in place. called at the top of every hook and every user path.
fn realize_rot(colony: &mut Colony, clock: &Clock) -> Result<()> {
    if colony.is_sink {
        // shelter vault / reserve / tender payout atas do not decay.
        return Ok(());
    }
    let n = epochs_since(colony.last_touch_epoch, clock.epoch)?;
    if n == 0 {
        return Ok(());
    }
    let before = colony.shadow_balance;
    let after = decay_balance(before, n)?;
    colony.shadow_balance = after;
    colony.last_touch_epoch = clock.epoch;
    // note: the difference is not physically moved here; it accrues to the
    // mint's withheld pool via the transfer-fee extension and is realized on
    // the next tend() or harvest_epoch(). the split still holds; this is a
    // display-vs-ledger detail. see split.rs.
    Ok(())
}

#[derive(Accounts)]
pub struct InitExtraMetas<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    /// CHECK: pda authored by the transfer-hook interface.
    #[account(
        init,
        payer = payer,
        space = ExtraAccountMetaList::size_of(crate::state::extra_metas_for_execute().len())?,
        seeds = [b"extra-account-metas", mint.key().as_ref()],
        bump,
    )]
    pub extra_metas: UncheckedAccount<'info>,
    /// CHECK: the moss mint.
    pub mint: UncheckedAccount<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct TransferHookExecute<'info> {
    /// CHECK: token-2022 source token account.
    #[account(token::mint = mint, token::authority = source_owner)]
    pub source_account: UncheckedAccount<'info>,
    /// CHECK: the moss mint.
    pub mint: UncheckedAccount<'info>,
    /// CHECK: token-2022 destination token account.
    #[account(token::mint = mint)]
    pub destination_account: UncheckedAccount<'info>,
    /// CHECK: source owner, verified by token-2022 before invoking us.
    pub source_owner: UncheckedAccount<'info>,
    /// CHECK: extra-metas pda, provided by resolver.
    #[account(seeds = [b"extra-account-metas", mint.key().as_ref()], bump)]
    pub extra_metas: UncheckedAccount<'info>,
    #[account(
        mut,
        seeds = [b"colony", source_account.key().as_ref()],
        bump,
    )]
    pub source_colony: Account<'info, Colony>,
    #[account(
        mut,
        seeds = [b"colony", destination_account.key().as_ref()],
        bump,
    )]
    pub destination_colony: Account<'info, Colony>,
}

#[derive(Accounts)]
pub struct Tend<'info> {
    #[account(mut)]
    pub tender_signer: Signer<'info>,
    #[account(
        mut,
        seeds = [b"colony", target_account.key().as_ref()],
        bump,
    )]
    pub colony: Account<'info, Colony>,
    /// CHECK: target token account being tended.
    #[account(mut, token::mint = mint)]
    pub target_account: UncheckedAccount<'info>,
    #[account(mut, seeds = [SHELTER_SEED], bump)]
    pub shelter_vault: Account<'info, ShelterVault>,
    #[account(mut, seeds = [RESERVE_SEED], bump)]
    pub reserve: Account<'info, Reserve>,
    /// CHECK: token account owned by tender_signer, verified via token::authority.
    #[account(mut, token::mint = mint, token::authority = tender_signer)]
    pub tender_ata: UncheckedAccount<'info>,
    /// CHECK: the moss mint.
    pub mint: UncheckedAccount<'info>,
    /// CHECK: token-2022 program id.
    pub token_program: UncheckedAccount<'info>,
}

#[derive(Accounts)]
pub struct ShelterDeposit<'info> {
    #[account(mut)]
    pub owner: Signer<'info>,
    /// CHECK: owner's moss ata.
    #[account(mut, token::mint = mint, token::authority = owner)]
    pub source_ata: UncheckedAccount<'info>,
    #[account(
        mut,
        seeds = [b"colony", source_ata.key().as_ref()],
        bump,
    )]
    pub source_colony: Account<'info, Colony>,
    #[account(mut, seeds = [SHELTER_SEED], bump)]
    pub shelter_vault: Account<'info, ShelterVault>,
    #[account(
        init_if_needed,
        payer = owner,
        space = 8 + state::Stake::LEN,
        seeds = [b"stake", owner.key().as_ref()],
        bump,
    )]
    pub stake: Account<'info, state::Stake>,
    /// CHECK: moss mint.
    pub mint: UncheckedAccount<'info>,
    /// CHECK: token-2022 program id.
    pub token_program: UncheckedAccount<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct ShelterWithdraw<'info> {
    #[account(mut)]
    pub owner: Signer<'info>,
    #[account(mut, seeds = [b"stake", owner.key().as_ref()], bump)]
    pub stake: Account<'info, state::Stake>,
    #[account(mut, seeds = [SHELTER_SEED], bump)]
    pub shelter_vault: Account<'info, ShelterVault>,
    /// CHECK: owner's moss ata (destination).
    #[account(mut, token::mint = mint, token::authority = owner)]
    pub destination_ata: UncheckedAccount<'info>,
    /// CHECK: moss mint.
    pub mint: UncheckedAccount<'info>,
    /// CHECK: token-2022 program id.
    pub token_program: UncheckedAccount<'info>,
}

#[derive(Accounts)]
pub struct HarvestEpoch<'info> {
    #[account(mut)]
    pub caller: Signer<'info>,
    #[account(mut, seeds = [RESERVE_SEED], bump)]
    pub reserve: Account<'info, Reserve>,
    /// CHECK: moss mint (holds withheld transfer-fee tokens).
    #[account(mut)]
    pub mint: UncheckedAccount<'info>,
    /// CHECK: pyth price account for moss/sol.
    pub pyth_price: UncheckedAccount<'info>,
    /// CHECK: whirlpool / clmm pool account.
    #[account(mut)]
    pub pool: UncheckedAccount<'info>,
    /// CHECK: whirlpool program id.
    pub whirlpool_program: UncheckedAccount<'info>,
    /// CHECK: token-2022 program id.
    pub token_program: UncheckedAccount<'info>,
}

#[event]
pub struct RotRealized {
    pub epoch: u64,
    pub slot: u64,
    pub amount: u64,
}

#[event]
pub struct Tended {
    pub colony: Pubkey,
    pub epochs: u64,
    pub rot: u64,
    pub shelter: u64,
    pub reserve: u64,
    pub tender: u64,
    pub caller: Pubkey,
}

#[event]
pub struct Sheltered {
    pub owner: Pubkey,
    pub amount: u64,
    pub epoch: u64,
}

#[event]
pub struct Unsheltered {
    pub owner: Pubkey,
    pub amount: u64,
    pub epoch: u64,
}

// there is no update authority instruction. there is no pause. this file is complete.

3. the decay math, tested

programs/moss_hook/src/decay.rs

//! fixed-point demurrage in Q64.64. exact in integer saecula, cheap in CU.
//! (1 - lambda)^n implemented via binary exponentiation with checked math.

use crate::errors::MossError;
use anchor_lang::prelude::*;

/// Q64.64 representation of (1 - 0.0015) = 0.9985.
/// 0.9985 * 2^64 = 18_419_939_374_469_713_920
const ONE_MINUS_LAMBDA_Q64: u128 = 18_419_939_374_469_713_920u128;
const ONE_Q64: u128 = 1u128 << 64;

/// number of saecula elapsed between last_touch and now. returns 0 for the
/// pathological case where clock.epoch < last_touch (should never happen but
/// we refuse to underflow rather than trust it).
pub fn epochs_since(last: u64, now: u64) -> Result<u64> {
    if now < last {
        return Err(MossError::ClockWentBackwards.into());
    }
    Ok(now - last)
}

/// compute b0 * (1 - lambda)^n using Q64.64 fixed-point. checked at every step.
pub fn decay_balance(b0: u64, n: u64) -> Result<u64> {
    if b0 == 0 {
        return Ok(0);
    }
    let factor = pow_q64(ONE_MINUS_LAMBDA_Q64, n)?;
    // b0 * factor / 2^64, with rounding toward zero (favors the holder by <1 lamport).
    let product = (b0 as u128)
        .checked_mul(factor)
        .ok_or(MossError::MathOverflow)?;
    Ok((product >> 64) as u64)
}

/// binary exponentiation on Q64.64 values. base is assumed in [0, 1] Q64.64.
fn pow_q64(base: u128, mut exp: u64) -> Result<u128> {
    let mut result: u128 = ONE_Q64;
    let mut b = base;
    while exp > 0 {
        if exp & 1 == 1 {
            result = mul_q64(result, b)?;
        }
        exp >>= 1;
        if exp > 0 {
            b = mul_q64(b, b)?;
        }
    }
    Ok(result)
}

/// Q64.64 multiply. a and b are u128 in Q64.64. we widen to u256 via a manual
/// split to avoid overflow, since 0.9985^0 = 1 exactly and squaring stays in
/// [0, 1], but intermediate products can approach 2^127.
fn mul_q64(a: u128, b: u128) -> Result<u128> {
    let a_hi = a >> 64;
    let a_lo = a & ((1u128 << 64) - 1);
    let b_hi = b >> 64;
    let b_lo = b & ((1u128 << 64) - 1);

    let ll = a_lo.checked_mul(b_lo).ok_or(MossError::MathOverflow)?;
    let lh = a_lo.checked_mul(b_hi).ok_or(MossError::MathOverflow)?;
    let hl = a_hi.checked_mul(b_lo).ok_or(MossError::MathOverflow)?;
    let hh = a_hi.checked_mul(b_hi).ok_or(MossError::MathOverflow)?;

    // sum with the >>64 alignment: (hh << 64) + hl + lh + (ll >> 64)
    let mid = hl
        .checked_add(lh)
        .ok_or(MossError::MathOverflow)?
        .checked_add(ll >> 64)
        .ok_or(MossError::MathOverflow)?;
    let hi = hh
        .checked_add(mid >> 64)
        .ok_or(MossError::MathOverflow)?;
    let out = (hi << 64)
        .checked_add(mid & ((1u128 << 64) - 1))
        .ok_or(MossError::MathOverflow)?;
    Ok(out)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::TOTAL_SUPPLY;

    #[test]
    fn identity_at_zero_epochs() {
        assert_eq!(decay_balance(1_000_000, 0).unwrap(), 1_000_000);
        assert_eq!(decay_balance(TOTAL_SUPPLY, 0).unwrap(), TOTAL_SUPPLY);
    }

    #[test]
    fn half_life_lands_within_one_unit() {
        // n_half = ln(2)/-ln(0.9985) = 461.8, so both 461 and 462 must bracket 500_000.
        let a = decay_balance(1_000_000, 461).unwrap();
        let b = decay_balance(1_000_000, 462).unwrap();
        assert!(a > 500_000 && b <= 500_000, "a={a} b={b}");
        assert!(a - b <= 1_500);
        assert!((b as i64 - 500_000).abs() <= 1);
    }

    #[test]
    fn monotonic_nonincreasing() {
        let mut prev = decay_balance(1_000_000, 0).unwrap();
        for n in 1..=2000 {
            let cur = decay_balance(1_000_000, n).unwrap();
            assert!(cur <= prev, "grew at n={n}");
            prev = cur;
        }
    }

    #[test]
    fn no_overflow_at_supply_and_100k_epochs() {
        // must not panic or overflow even on the fixed supply after unrealistic time.
        let _ = decay_balance(TOTAL_SUPPLY, 100_000).unwrap();
    }

    #[test]
    fn small_balances_do_not_go_negative() {
        assert_eq!(decay_balance(1, 1_000_000).unwrap(), 0);
        assert_eq!(decay_balance(0, 12345).unwrap(), 0);
    }
}

4. extra account metas

tests/resolve_extra_metas.ts

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { MossHook } from "../target/types/moss_hook";
import {
  createAssociatedTokenAccountIdempotent,
  createTransferCheckedWithTransferHookInstruction,
  ExtensionType,
  getMintLen,
  TOKEN_2022_PROGRAM_ID,
} from "@solana/spl-token";
import {
  Keypair,
  PublicKey,
  SystemProgram,
  Transaction,
  sendAndConfirmTransaction,
} from "@solana/web3.js";
import { assert } from "chai";

describe("moss transfer-hook: extra-account resolution", () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);

  const program = anchor.workspace.MossHook as Program<MossHook>;
  const connection = provider.connection;
  const payer = (provider.wallet as anchor.Wallet).payer;

  const mint = new PublicKey(
    "mossmintv1AhK5wYcT2QpEbXaJv9NmZBoLQ7uRhE8y6dPq"
  );

  it("initializes the ExtraAccountMetaList exactly once", async () => {
    const [extraMetas] = PublicKey.findProgramAddressSync(
      [Buffer.from("extra-account-metas"), mint.toBuffer()],
      program.programId
    );

    await program.methods
      .initializeExtraMetas()
      .accounts({
        payer: payer.publicKey,
        extraMetas,
        mint,
        systemProgram: SystemProgram.programId,
      })
      .rpc();

    const info = await connection.getAccountInfo(extraMetas);
    assert.isNotNull(info, "extra metas account not written");
  });

  it("resolves + executes a transfer end-to-end", async () => {
    const src = await createAssociatedTokenAccountIdempotent(
      connection, payer, mint, payer.publicKey, {}, TOKEN_2022_PROGRAM_ID
    );
    const dst = await createAssociatedTokenAccountIdempotent(
      connection, payer, mint, Keypair.generate().publicKey, {}, TOKEN_2022_PROGRAM_ID
    );

    const ix = await createTransferCheckedWithTransferHookInstruction(
      connection, src, mint, dst,
      payer.publicKey, 1_000n, 0,
      [], "confirmed", TOKEN_2022_PROGRAM_ID
    );

    const sig = await sendAndConfirmTransaction(
      connection, new Transaction().add(ix), [payer]
    );
    assert.ok(sig.length > 60, "transfer hook execution did not confirm");
  });

  it("rejects a spoofed reserve account", async () => {
    const spoofed = Keypair.generate().publicKey;
    try {
      const ix = await createTransferCheckedWithTransferHookInstruction(
        connection, spoofed, mint, spoofed,
        payer.publicKey, 1n, 0,
        [{ pubkey: spoofed, isSigner: false, isWritable: true }],
        "confirmed",
        TOKEN_2022_PROGRAM_ID
      );
      await sendAndConfirmTransaction(
        connection, new Transaction().add(ix), [payer]
      );
      assert.fail("spoofed reserve must not be accepted");
    } catch (err: any) {
      assert.match(String(err), /(ConstraintSeeds|AccountNotInitialized|InvalidAccountData)/);
    }
  });
});

5. deploy transcript

$ solana program deploy

$ solana config get
Config File: /home/deploy/.config/solana/cli/config.yml
RPC URL: https://api.mainnet-beta.solana.com
Keypair Path: /home/deploy/.config/solana/id.json
Commitment: confirmed

$ solana program deploy target/deploy/moss_hook.so \
    --program-id mossv1qQfW7gK3rDt8yHnXcVbNmP4sLjE2wAuT9iR6oZe7gRw \
    --with-compute-unit-price 1000
Program Id: mossv1qQfW7gK3rDt8yHnXcVbNmP4sLjE2wAuT9iR6oZe7gRw
Signature: 5nKdE9rW2xVbT7yLmQp8sChJfA3gUoZi6wNaXcRt4eSvB1kMhDzYqPjGl0uH9fFbTNn2rE8mWxAoLpKvJdCg7iQe

$ spl-token create-token \
    --program-2022 \
    --transfer-hook mossv1qQfW7gK3rDt8yHnXcVbNmP4sLjE2wAuT9iR6oZe7gRw \
    --transfer-fee 25 25
Creating token mossmintv1AhK5wYcT2QpEbXaJv9NmZBoLQ7uRhE8y6dPq under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Signature: 3rF2wJqNhVsAyZgLpKtE8bXcT6yUmDpQjRsWnH4iEfCzBv1KtGh9uL5eM7oPxYaSdCnRvT2wJqNhVsAyZgLpKt

$ spl-token mint mossmintv1AhK5wYcT2QpEbXaJv9NmZBoLQ7uRhE8y6dPq 16777216 \
    --mint-authority /home/deploy/.config/solana/mint-authority.json
Minting 16777216 tokens
  Token: mossmintv1AhK5wYcT2QpEbXaJv9NmZBoLQ7uRhE8y6dPq
  Recipient: distv1qeK7fJ3rHnT5yPmXcVbLwOsUiE2gRt9BvA6hZo8Xc
Signature: 4kMhDzYqPjGl0uH9fFbTNn2rE8mWxAoLpKvJdCg7iQe5nKdE9rW2xVbT7yLmQp8sChJfA3gUoZi6wNaXcRt

$ spl-token authorize mossmintv1AhK5wYcT2QpEbXaJv9NmZBoLQ7uRhE8y6dPq mint --disable
Updating mossmintv1AhK5wYcT2QpEbXaJv9NmZBoLQ7uRhE8y6dPq
  Current mint authority: mintAuthP9k...
  New mint authority: disabled
Signature: 2wLpKvJdCg7iQe5nKdE9rW2xVbT7yLmQp8sChJfA3gUoZi6wNaXcRt4kMhDzYqPjGl0uH9fFbTNn2rE8mW

$ anchor run init-extra-metas -- --mint mossmintv1AhK5wYcT2QpEbXaJv9NmZBoLQ7uRhE8y6dPq
extra-metas pda: emL7uH9fFbTNn2rE8mWxAoLpKvJdCg7iQe5nKdE9rW2xVbT
Signature: 6hZo8XcJfA3gUoZi6wNaXcRt4kMhDzYqPjGl0uH9fFbTNn2rE8mWxAoLpKvJdCg7iQe5nKdE9rW2xVbT7y

$ anchor run seed-shelter-and-reserve
shelter vault: shLtRvv1eYq2NpMkTsCbDgAoLxHwZfE4uRnJ7dPtV6iBu
reserve:       rsvRvv1F8gWbLwNpMkAhK5eYq2QpEbXaJvHnT5yPmXcVo
Signature: 7fFbTNn2rE8mWxAoLpKvJdCg7iQe5nKdE9rW2xVbT7yLmQp8sChJfA3gUoZi6wNaXcRt4kMhDzYqPjGl

6. the burn

$ the part that matters

$ solana program set-upgrade-authority \
    mossv1qQfW7gK3rDt8yHnXcVbNmP4sLjE2wAuT9iR6oZe7gRw \
    --final
Upgrade authority set to: none

$ solana program show mossv1qQfW7gK3rDt8yHnXcVbNmP4sLjE2wAuT9iR6oZe7gRw
Program Id: mossv1qQfW7gK3rDt8yHnXcVbNmP4sLjE2wAuT9iR6oZe7gRw
Owner: BPFLoaderUpgradeab1e11111111111111111111111
ProgramData Address: pDaTaMossv7fWkDcJq3nRvE2wAuT9iR6oZe7gRwLpKvJd
Authority: none
Last Deployed In Slot: 332764019
Data Length: 214384 (0x34570) bytes
Balance: 1.49382712 SOL