Skip to Content
⚠️ Alert: Sylan is under active development—only trust contract/wallet addresses announced on our official channels; we will never DM you, ask for funds, or run surprise airdrops/presales.
ContractsPresale

PresaleContract

Token sale contract for distributing SYL in a capped presale with optional whitelist, soft/hard caps, per‑wallet limits, and a post‑sale claim/refund window. Payments are made in a configured ERC‑20 (e.g., USDC/USDT/DAI). The sale can operate in mint‑on‑claim mode or using pre‑funded SYL.

Units: sale times use seconds (block.timestamp). Payment amounts use the payment token’s decimals. SYL uses 18 decimals.


Responsibilities

  • Accept payments during the sale window, enforcing hard cap and per‑wallet min/max.
  • Optionally enforce a whitelist.
  • After the sale, finalize → set result (success = totalRaised ≥ softCap) and set a claim deadline.
  • If success: forward raised funds to fundsWallet, and (if pre‑funded) burn immediate unsold SYL.
  • If failed: enable refunds.
  • Let buyers claim SYL until claimDeadline; after that, allow the owner (or anyone) to burn remaining unclaimed tokens.

Key parameters (initialize)

initialize( address paymentToken, // e.g. USDC/USDT/DAI (ERC20) address saleToken_, // SYL proxy address fundsWallet_, // where raised funds are sent on success address initialOwner, // proxy owner uint64 startTime_, uint64 endTime_, uint256 softCap_, // in payment token units uint256 hardCap_, // in payment token units uint256 minBuy_, // per-wallet min (0 to disable) uint256 maxBuy_, // per-wallet max (0 to disable) uint256 rate_, // **tokens per 1 payment token**, scaled 1e18 bool mintOnClaim_ // if true, contract must hold MINTER_ROLE on SYL )

Rate scaling
tokens = paymentAmount * rate / 1e18
To get X SYL per 1.0 payment token, set:
rate = X * 1e18 / 10**payment.decimals()
Example: USDC (6 decimals), 100 SYL / 1 USDCrate = 100 * 1e18 / 1e6.


State (selected)

  • payment (ERC20Upgradeable) — the payment token
  • saleToken (SylanToken) — SYL token contract
  • fundsWallet (address)
  • startTime, endTime (uint64 s)
  • softCap, hardCap (uint256 in payment units)
  • minBuy, maxBuy (uint256 per‑wallet limits, in payment units)
  • rate (uint256, scaled as above)
  • totalRaised (uint256)
  • finalized, success (bool)
  • claimDeadline (uint64 s)
  • whitelistEnabled (bool), isWhitelisted(address) → bool
  • mintOnClaim (bool)
  • contributed(address) → uint256 — amount a buyer paid
  • claimedOrRefunded(address) → bool

Lifecycle

1) Buy

buy(uint256 paymentAmount)whenNotPaused
Requirements:

  • startTime ≤ now ≤ endTime
  • If whitelistEnabled, isWhitelisted[msg.sender] == true
  • paymentAmount > 0
  • totalRaised + paymentAmount ≤ hardCap
  • Per‑wallet: minBuy and maxBuy enforced on the new cumulative total

Effects:

  • Records contributed[msg.sender] += paymentAmount, totalRaised += paymentAmount
  • Pulls funds: payment.safeTransferFrom(msg.sender, address(this), paymentAmount)
  • Emits Contributed(buyer, paymentAmount, newTotal)

Buyers must approve(presale, paymentAmount) on the payment token before calling buy.

2) Finalize

finalize(uint64 claimDeadline_)onlyOwner
Requirements: sale ended (now > endTime or hard cap reached), not already finalized, and claimDeadline_ > now.

Effects:

  • finalized = true; success = (totalRaised ≥ softCap); claimDeadline = claimDeadline_
  • If success:
    • Forwards payment: payment.safeTransfer(fundsWallet, totalRaised)
    • If pre‑funded (mintOnClaim == false): computes sold = totalRaised * rate / 1e18; burns any immediate unsold from the contract balance and emits UnsoldBurned(unsold)
  • Emits Finalized(success, totalRaised)

3a) Claim (success)

claim()whenNotPaused
Requirements: finalized && success, now ≤ claimDeadline, and not already claimed.

Effects:

  • tokens = contributed[msg.sender] * rate / 1e18
  • If mintOnClaimsaleToken.mint(msg.sender, tokens); else → saleToken.transfer(msg.sender, tokens)
  • Marks claimedOrRefunded[msg.sender] = true
  • Emits Claimed(buyer, tokens)

3b) Refund (failure)

refund()whenNotPaused
Requirements: finalized && !success and not already refunded.

Effects:

  • Transfers back the contributed amount: payment.safeTransfer(msg.sender, contributed[msg.sender])
  • Marks claimedOrRefunded[msg.sender] = true
  • Emits Refunded(buyer, paymentAmount)

4) Burn leftovers

burnUnsold()whenNotPaused
Requirements: finalized and now > claimDeadline.

Effects:

  • Burns any remaining SYL held by the contract (unclaimed)
  • Emits UnsoldBurned(amount)

Admin surface (onlyOwner)

  • setWhitelistEnabled(bool)
  • setWhitelisted(address user, bool allowed)
  • setTimes(uint64 start, uint64 end) — only before sale starts
  • setCaps(uint256 soft, uint256 hard) — only before sale starts
  • setLimits(uint256 min, uint256 max) — only before sale starts
  • setRate(uint256 rate) — only before sale starts; rate > 0
  • setFundsWallet(address) — non‑zero address
  • setMintOnClaim(bool) — toggle minting vs pre‑funded mode
  • pause() / unpause() and UUPS upgradeTo/upgradeToAndCall(...)

Events

  • Contributed(address indexed buyer, uint256 paymentAmount, uint256 newTotal)
  • Finalized(bool success, uint256 totalRaised)
  • Claimed(address indexed buyer, uint256 tokensAmount)
  • Refunded(address indexed buyer, uint256 paymentAmount)
  • WhitelistSet(address indexed user, bool allowed)
  • ParamsUpdated()
  • UnsoldBurned(uint256 amount)

Minimal ABI (buyer‑facing)

[ {"type":"function","stateMutability":"view","name":"payment","inputs":[],"outputs":[{"type":"address"}]}, {"type":"function","stateMutability":"view","name":"saleToken","inputs":[],"outputs":[{"type":"address"}]}, {"type":"function","stateMutability":"view","name":"fundsWallet","inputs":[],"outputs":[{"type":"address"}]}, {"type":"function","stateMutability":"view","name":"startTime","inputs":[],"outputs":[{"type":"uint64"}]}, {"type":"function","stateMutability":"view","name":"endTime","inputs":[],"outputs":[{"type":"uint64"}]}, {"type":"function","stateMutability":"view","name":"softCap","inputs":[],"outputs":[{"type":"uint256"}]}, {"type":"function","stateMutability":"view","name":"hardCap","inputs":[],"outputs":[{"type":"uint256"}]}, {"type":"function","stateMutability":"view","name":"minBuy","inputs":[],"outputs"][{"type":"uint256"}]}, {"type":"function","stateMutability":"view","name":"maxBuy","inputs":[],"outputs"][{"type":"uint256"}]}, {"type":"function","stateMutability":"view","name":"rate","inputs":[],"outputs":[{"type":"uint256"}]}, {"type":"function","stateMutability":"view","name":"whitelistEnabled","inputs":[],"outputs":[{"type":"bool"}]}, {"type":"function","stateMutability":"view","name":"isWhitelisted","inputs":[{"name":"user","type":"address"}],"outputs":[{"type":"bool"}]}, {"type":"function","stateMutability":"view","name":"finalized","inputs":[],"outputs":[{"type":"bool"}]}, {"type":"function","stateMutability":"view","name":"success","inputs":[],"outputs":[{"type":"bool"}]}, {"type":"function","stateMutability":"view","name":"claimDeadline","inputs":[],"outputs":[{"type":"uint64"}]}, {"type":"function","stateMutability":"view","name":"totalRaised","inputs":[],"outputs":[{"type":"uint256"}]}, {"type":"function","stateMutability":"view","name":"contributed","inputs":[{"name":"buyer","type":"address"}],"outputs":[{"type":"uint256"}]}, {"type":"function","stateMutability":"view","name":"claimedOrRefunded","inputs":[{"name":"buyer","type":"address"}],"outputs":[{"type":"bool"}]}, {"type":"function","stateMutability":"nonpayable","name":"buy","inputs":[{"name":"paymentAmount","type":"uint256"}],"outputs":[]}, {"type":"function","stateMutability":"nonpayable","name":"claim","inputs":[],"outputs":[]}, {"type":"function","stateMutability":"nonpayable","name":"refund","inputs":[],"outputs":[]} ]

Integration tips

  • Approvals: buyers must approve the payment token for each purchase.
  • Decimals: display amounts in payment token units; compute SYL using the rate formula above.
  • Whitelist UX: if enabled, surface isWhitelisted(user) and disable the buy button accordingly.
  • Claim timer: show a countdown to claimDeadline; after expiry, claim() reverts and burnUnsold() can be called.
  • Mint vs pre‑funded:
    • mintOnClaim = true → presale contract must hold MINTER_ROLE on SYL; no prefunding needed.
    • mintOnClaim = false → deposit enough SYL before finalization; finalize will burn immediate unsold; burnUnsold() later removes unclaimed leftovers.

Admin & security notes

  • setTimes, setCaps, setLimits, and setRate are locked once the sale starts.
  • fundsWallet must be a non‑zero address (ideally a multisig).
  • Contract is pausable; UIs should reflect paused state.
  • Uses ReentrancyGuard and SafeERC20 for transfers; claims/refunds are pull‑style from escrowed funds.

Out of scope for this page

  • Addresses & ABIs → Architecture → Addresses & ABIs
  • Token/vesting allocations → Sylan Token / Sylan Vesting
  • Marketplace protocol (escrow/consensus) — unrelated to presale
Last updated on