program type_custom_token

// Neptune Custom Token Type Script (TSP-1 pattern)
//
// Validates a custom coin across a transaction.
// The type script enforces:
//   1. Supply conservation: sum(inputs) == sum(outputs) (for transfers)
//   2. Mint authorization: new supply requires mint_auth proof
//   3. Token identity: all coins reference the same token_id
//
// Public input:
//   kernel MAST hash, input UTXO hash, output UTXO hash
// Secret input:
//   token_id, coin data, config commitment
use os.neptune.kernel

// Verify a mint authority secret against the config commitment.
fn verify_mint_auth(mint_auth_hash: Field) {
    let secret: Field = divine()
    let d: Digest = hash(secret, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    let (h0, _, _, _, _) = d
    assert_eq(mint_auth_hash, h0)
}

event SupplyChange {
    old_supply: Field,
    new_supply: Field,
}

fn main() {
    // Read type-script public inputs.
    let (kernel_hash, input_hash, output_hash) = kernel.read_type_script_hashes()
    // Token identity โ€” all coins in this transaction must match.
    let token_id: Digest = divine5()
    // Config: mint_auth_hash determines who can change supply.
    // 0 = minting disabled (fixed supply).
    let mint_auth_hash: Field = divine()
    // Config commitment binds token_id + mint_auth.
    let (t0, t1, t2, t3, t4) = token_id
    let config: Digest = hash(t0, t1, t2, t3, t4, mint_auth_hash, 0, 0, 0, 0)
    let expected_config: Digest = divine5()
    assert_digest(config, expected_config)
    // Sum input coin amounts for this token.
    let num_inputs: Field = divine()
    let mut input_total: Field = 0
    for i in 0..num_inputs bounded 8 {
        let coin_token: Digest = divine5()
        assert_digest(coin_token, token_id)
        let amount: Field = divine()
        let _: U32 = as_u32(amount)
        input_total = input_total + amount
    }
    // Sum output coin amounts for this token.
    let num_outputs: Field = divine()
    let mut output_total: Field = 0
    for i in 0..num_outputs bounded 8 {
        let coin_token: Digest = divine5()
        assert_digest(coin_token, token_id)
        let amount: Field = divine()
        let _: U32 = as_u32(amount)
        output_total = output_total + amount
    }
    // Check conservation or authorized mint/burn.
    if input_total == output_total {
    } else {
        // Pure transfer โ€” supply unchanged, no auth needed.
        // Supply changed โ€” mint authority required.
        // mint_auth_hash == 0 means minting disabled; verify_mint_auth
        // will fail on 0 because hash(secret)[0] can never equal 0
        // for a valid preimage (Tip5 is collision-resistant).
        verify_mint_auth(mint_auth_hash)
        reveal
        SupplyChange { old_supply: input_total, new_supply: output_total }
    }
}

Local Graph