Sylan Token (SYL)
SYL is the ERC‑20 token used for payments (escrow), staking/rewards, and ecosystem flows. The token is upgradeable (UUPS), permit‑enabled (EIP‑2612), burnable, and pausable. Administrative privileges are managed via AccessControl roles.
Interact with the proxy address listed under Architecture → Addresses & ABIs.
Features & standards
| Capability | Standard/Module | Notes |
|---|---|---|
| Fungible token | ERC‑20 | 18 decimals. |
| Permit (gasless approve) | EIP‑2612 (permit) | Supports nonces and DOMAIN_SEPARATOR. |
| Burn | burn, burnFrom | Holders (or approved spenders) can destroy tokens. |
| Pause transfers | Pausable | Transfers disabled while paused; mint/burn also respect pause via _update guard. |
| Roles | AccessControl | DEFAULT_ADMIN_ROLE, MINTER_ROLE, PAUSER_ROLE. |
| Upgradeability | UUPS | Owner‑gated upgradeTo/upgradeToAndCall. |
Roles & administration
| Role | Powers |
|---|---|
DEFAULT_ADMIN_ROLE | Grants/revokes other roles; typically held by a multisig. |
MINTER_ROLE | Can mint(address to, uint256 amount) (if minting is enabled in the deployed policy). |
PAUSER_ROLE | Can pause() and unpause(). |
| Owner (UUPS) | Authorized to upgrade implementation; by default the owner is also the default admin. |
Governance recommendations live on Concepts → Security. In production, prefer a Gnosis Safe and a timelock for upgrades.
Read model
name() → stringsymbol() → string(expected:SYL)decimals() → uint8(18)totalSupply() → uint256balanceOf(address) → uint256allowance(owner, spender) → uint256nonces(owner) → uint256DOMAIN_SEPARATOR() → bytes32
Write surface (user‑facing)
transfer(to, amount)approve(spender, amount)transferFrom(from, to, amount)permit(owner, spender, value, deadline, v, r, s)burn(amount)/burnFrom(account, amount)
Admin/role‑gated
mint(to, amount)— onlyMINTER_ROLEpause()/unpause()— onlyPAUSER_ROLEgrantRole(bytes32 role, address account)/revokeRole(bytes32 role, address account)— adminupgradeTo(address newImpl)/upgradeToAndCall(...)— owner
Transfers and state‑changing ERC‑20 ops are wrapped by an internal
_updatethat enforceswhenNotPaused.
Events of interest
Transfer(address from, address to, uint256 value)Approval(address owner, address spender, uint256 value)Paused(address account)/Unpaused(address account)RoleGranted(bytes32 role, address account, address sender)/RoleRevoked(...)Upgraded(address implementation)(UUPS)ContractUpgraded(address newImplementation, address caller, uint256 timestamp)(emitted by the shared UUPS base)
Minimal ABI (client use)
For convenience (full ABI in the repo):
[
{"type":"function","stateMutability":"view","name":"decimals","inputs":[],"outputs":[{"type":"uint8"}]},
{"type":"function","stateMutability":"view","name":"symbol","inputs":[],"outputs":[{"type":"string"}]},
{"type":"function","stateMutability":"view","name":"balanceOf","inputs":[{"name":"account","type":"address"}],"outputs":[{"type":"uint256"}]},
{"type":"function","stateMutability":"view","name":"allowance","inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"outputs":[{"type":"uint256"}]},
{"type":"function","stateMutability":"nonpayable","name":"approve","inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"outputs":[{"type":"bool"}]},
{"type":"function","stateMutability":"nonpayable","name":"permit","inputs":[
{"name":"owner","type":"address"},
{"name":"spender","type":"address"},
{"name":"value","type":"uint256"},
{"name":"deadline","type":"uint256"},
{"name":"v","type":"uint8"},
{"name":"r","type":"bytes32"},
{"name":"s","type":"bytes32"}
],"outputs":[]}
]Integration tips
- Permit first, then act: Use
permitto set allowance in the same UX step without an extra approve tx. - Pauses: Your UI should surface paused state and disable actions if token transfers are halted.
- Meta‑env: Respect chain ID in EIP‑712 domain for
permitto avoid signature reuse across networks.
Examples
Permit + purchase (viem/Wagmi outline)
import { signTypedData, writeContract } from 'wagmi/actions'
const APIESCROW_ADDRESS = '0xfd685eFeeB6E69358Cf87c9c5Bf41E56154461B1' // Polygon Amoy
const API_ID =
'0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
// 1) Build and sign EIP-712 permit
// domain: { name: 'SYL', version: '1', chainId, verifyingContract: SYL }
// types: { Permit: [ {name:'owner',type:'address'}, ... ] }
// value: { owner, spender: APIESCROW_ADDRESS, value, nonce, deadline }
// 2) Submit permit (no tx) + call escrow using the allowance
await writeContract({
address: APIESCROW_ADDRESS,
abi: apiEscrowAbi,
functionName: 'purchaseSubscription',
args: [API_ID],
})For a full permit example, see Integrations.
Security & operations
- Keep
MINTER_ROLEminimal; if minting is only for staking rewards, limit it to the Staking contract or a dedicated distributor. - Publish role holders and proxy owner addresses under Architecture → Addresses & ABIs for transparency.
- Prefer burn for treasury hygiene (unused allocations or slashed rewards), executed from authorized accounts.
Verification checklist (before mainnet)
- Proxy owner is a multisig;
DEFAULT_ADMIN_ROLEis set appropriately. -
MINTER_ROLE/PAUSER_ROLEare assigned to the intended actors only; remove from EOAs if not needed. -
permitvectors tested on target networks; domain separators verified. - Token symbol/name/decimals confirmed on the explorer.
- Upgrade path tested (proxy ↔ new impl) with storage gap checks.
Out of scope for this page
- Distribution/vesting specifics → SylanVesting & Presale pages.
- Staking economics → SylanStaking.
- Addresses/ABIs → Architecture → Addresses & ABIs.
Last updated on