LpManager

Verified · Ion Swap Dark Contract · DSOL · Phase B
Contract ID
dc1_40e0d688c1e3bc67ba2ab1b6f43ed78e9753afe7d6c2bbd53a3197c76031
Code hash
774404996c06e314633d6aa460ae12bba9e3310451338d09bc6d9a00e3555dc8
Deployer
Deploy tx
ff4bccef0db2997293ee9b9bf06fa571…
Created at block
Source
View .dsol source →
Wallet: detecting…

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

burn

lock

Public state

loading…

Contract source Verified

Compiler
build-1261/dark-contracts/compiler v1
Source SHA-256
a81b389f134bdd0bf9da374aad6bf3ac0995895fa8be71fce0f39c6b6d90199b
Bytecode hash
774404996c06e314633d6aa460ae12bba9e3310451338d09bc6d9a00e3555dc8
Verified by
auto (canonical mirror)
Verified at
Thu, 30 Apr 2026 03:20:40 GMT

The on-chain bytecode hash matches the SHA-256 of LpManager's compiled output. This means the source below was the exact input the deployer used. Anyone can re-derive the same bytecode by compiling the source through build-1261/dark-contracts/compiler.

// 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