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.
ContractsSylan Staking

Sylan Staking

Single‑pool staking of SYL → SYL with per‑second emissions and standard MasterChef‑style accounting. Rewards can be minted by this contract (if it holds MINTER_ROLE on SylanToken) or pulled from an external rewardSource address (pre‑funded/approved). The contract is UUPS‑upgradeable and inherits pause/ownable guards from the shared base.

Time units use seconds (block.timestamp). Amounts use 18‑decimals like SYL.


What it does

  • Stake SYL and earn SYL at a configurable rewardRatePerSecond.
  • Standard accounting with accRewardPerShare and user rewardDebt.
  • User actions: deposit(amount), withdraw(amount), harvest() (claim without changing stake).
  • Admin actions: setRewardRatePerSecond(newRate), setRewardSource(src).

State (selected)

  • syl → IERC20 — staking + reward token (SYL)
  • rewardSource → address — if 0x0, the contract mints rewards; otherwise it pulls from rewardSource (requires allowance)
  • rewardRatePerSecond → uint256 — global emission rate
  • lastUpdate → uint256 — last time accRewardPerShare was updated
  • accRewardPerShare → uint256 — cumulative rewards per staked token (fixed‑point)
  • totalStaked → uint256
  • User { amount, rewardDebt } — per user position

Accounting model (how rewards accrue)

Let Δ = now - lastUpdate. When anyone interacts:

  1. If totalStaked > 0, accrue:
    accRewardPerShare += Δ * rewardRatePerSecond * PRECISION / totalStaked
  2. Set lastUpdate = now.
  3. A user’s pending = user.amount * accRewardPerShare / PRECISION - user.rewardDebt.

On deposit(amount)

  • Accrue, compute pending, pay it to the user.
  • Pull amount SYL from user, increase totalStaked and user.amount.
  • Set user.rewardDebt = user.amount * accRewardPerShare / PRECISION.
  • Emits Deposit(user, amount) and Harvest(user, pending) if any.

On withdraw(amount)

  • Accrue, compute pending, pay it.
  • Decrease user.amount and totalStaked, transfer amount SYL back to user.
  • Update rewardDebt as above.
  • Emits Withdraw(user, amount) and Harvest(user, pending) if any.

On harvest()

  • Accrue, compute pending, pay it, update rewardDebt to current position.
  • Emits Harvest(user, pending).

PRECISION is an internal fixed‑point scale factor (implementation detail). You don’t need it for integrations; rely on the view methods.


Reward funding

  • If rewardSource == address(0) → this contract mints rewards to recipients using SylanToken.mint(to, amount) (requires it to hold MINTER_ROLE).
  • Else → it transfers rewards using safeTransferFrom(rewardSource, to, amount); the owner must keep rewardSource sufficiently funded & approved.
    When pulling, the contract emits FundPulled(amount).

External interface

Views

  • syl() → address
  • rewardSource() → address
  • rewardRatePerSecond() → uint256
  • lastUpdate() → uint256
  • accRewardPerShare() → uint256
  • totalStaked() → uint256

Writes (users)

  • deposit(uint256 amount)
  • withdraw(uint256 amount)
  • harvest()

Admin

  • setRewardRatePerSecond(uint256 newRate)
  • setRewardSource(address src)

The contract inherits pause/unpause and UUPS upgrade controls from the base (owner‑gated).


Events

  • Deposit(address indexed user, uint256 amount)
  • Withdraw(address indexed user, uint256 amount)
  • Harvest(address indexed user, uint256 amount)
  • RewardRateSet(uint256 oldRate, uint256 newRate)
  • RewardSourceSet(address indexed source)
  • FundPulled(uint256 amount) (emitted when rewards are pulled from rewardSource)
  • Upgraded(address implementation) / ContractUpgraded(address newImplementation, address caller, uint256 timestamp) (inherited)

Minimal ABI (client)

For typical staking UIs:

[ {"type":"function","stateMutability":"view","name":"syl","inputs":[],"outputs":[{"type":"address"}]}, {"type":"function","stateMutability":"view","name":"rewardRatePerSecond","inputs":[],"outputs":[{"type":"uint256"}]}, {"type":"function","stateMutability":"view","name":"totalStaked","inputs":[],"outputs":[{"type":"uint256"}]}, {"type":"function","stateMutability":"nonpayable","name":"deposit","inputs":[{"name":"amount","type":"uint256"}],"outputs":[]}, {"type":"function","stateMutability":"nonpayable","name":"withdraw","inputs":[{"name":"amount","type":"uint256"}],"outputs":[]}, {"type":"function","stateMutability":"nonpayable","name":"harvest","inputs":[],"outputs":[]} ]

Integration tips

  • Approve first: users must approve(staking, amount) on SYL before deposit.
  • Pending UI: compute pending by reading accRewardPerShare, totalStaked, and the user’s amount/rewardDebt (or expose a helper in your SDK).
  • Claim‑only: harvest() claims without changing stake; wire a “Claim” button.
  • APY display: convert rewardRatePerSecond * 86400 * 365 to yearly emissions and divide by totalStaked for a naive APR (document assumptions).

Admin & safety

  • Keep rewardRatePerSecond within treasury limits; raising it without funding will cause pull transfers to revert.
  • If using minting, grant MINTER_ROLE to the staking contract only (not EOAs). Rotate via a multisig.
  • Pausing via the base contract will block writes; surface paused state in the UI.

Verification checklist

  • Happy‑path tests: deposit → harvest → withdraw matches expected rewards.
  • Edge tests: zero stake during accrual, multiple harvests, partial withdraws.
  • Admin tests: changing rewardRatePerSecond mid‑epoch, switching rewardSource.
  • Security: reentrancy guards around transfers; SafeERC20 in use.

Out of scope for this page

  • Node consensus rewards & slashing (see NodeRegistry)
  • Token details (see Sylan Token)
  • Addresses/ABIs (see Architecture → Addresses & ABIs)
Last updated on