Entrypoints
Each form below maps directly to a contract entrypoint. Filling it in and clicking Call constructs a typed-argv DSOL call. Your wallet shows an approval modal before broadcasting; nothing fires without your explicit click.
mint @direct
burn @direct
lock @direct
Public state
loading…
Contract source Verified
// LpManager — Phase B of silent-iron-keystone v4.
//
// PURPOSE
// ─────────
// Wraps LP_MINT / LP_BURN / LP_LOCK in a typed-argv DSOL contract.
// Same bug-class-fix property as IonSwapDeposit: every parameter
// (positionId, pool_id, amount, blinding) is positional in the ABI
// and CANNOT be silently dropped.
//
// PRIVACY MODEL
// ─────────────
// LP positions are commitment-based today (`pos-<r12>` → Pedersen
// commit + blinding held client-side in localStorage). This contract
// preserves that pattern: the OPENING (amount, blinding) stays in
// the user's wallet; the contract holds only the commitment hash.
//
// BUG-CLASS FIX
// ─────────────
// The v1.2.144 stuck-LP regression came from `provider.lpMint({
// commitment: null, positionId: null })`. With typed argv, calls
// missing `commitment` or `positionId` are rejected at the ABI
// decoder before any state mutation. Result: structurally
// impossible to mint a stuck LP position.
//
// EMITTED EVENTS
// ──────────────
// mint() → LP_MINT_V1 syscall, byte-equivalent to legacy attestation
// burn() → LP_BURN_V1 syscall, byte-equivalent to legacy attestation
// lock() → LP_LOCK event (no syscall — pure on-contract state)
dark contract LpManager {
// commitment_hex hex-encoded as a sentinel — non-empty means present.
public mapping(bytes => bytes) positionPool; // positionId → pool_id
public mapping(bytes => bytes) positionCommit; // positionId → Pedersen commit
public mapping(bytes => uint64) positionMintBlock; // positionId → block minted
public mapping(bytes => uint64) positionLockUntil; // positionId → lock-until block (0 = unlocked)
public mapping(bytes => bool) positionBurned; // positionId → burned flag
public mapping(bytes => bool) spentNullifiers;
@direct
entry mint(bytes positionId, bytes pool_id, bytes commitment) {
require(positionMintBlock[positionId] == 0, "LP_DUPLICATE_POSITION");
positionPool[positionId] = pool_id;
positionCommit[positionId] = commitment;
positionMintBlock[positionId] = block.number;
positionLockUntil[positionId] = 0;
positionBurned[positionId] = false;
syscall(LP_MINT_V1, ctx.txHash);
}
@direct
entry burn(bytes positionId, uint64 amount, bytes nullifier) {
require(positionMintBlock[positionId] != 0, "LP_UNKNOWN_POSITION");
require(!positionBurned[positionId], "LP_ALREADY_BURNED");
require(block.number >= positionLockUntil[positionId], "LP_STILL_LOCKED");
require(!spentNullifiers[nullifier], "LP_REPLAY");
require(amount > 0, "LP_ZERO_AMOUNT");
positionBurned[positionId] = true;
spentNullifiers[nullifier] = true;
syscall(LP_BURN_V1, ctx.txHash);
}
@direct
entry lock(bytes positionId, uint64 unlock_block) {
require(positionMintBlock[positionId] != 0, "LP_UNKNOWN_POSITION");
require(!positionBurned[positionId], "LP_ALREADY_BURNED");
require(unlock_block > block.number + 6, "LP_UNLOCK_TOO_SOON");
require(unlock_block > positionLockUntil[positionId], "LP_LOCK_NOT_EXTENSION");
positionLockUntil[positionId] = unlock_block;
}
}
Download .dsol