sNOTE
On this page
sNOTE represents a share of a NOTE staking vault. Rewards change its NOTE-denominated exchange rate; they do not fix the USDG market value of either token.
Summary
fact
| Property | Value |
|---|---|
| Standard | ERC-4626 vault over NOTE (18 decimals); shares have 24 decimals because _decimalsOffset() = 6 (virtual shares) |
| Reward source | RevenueRouter only, via notifyReward(amount) after transferring NOTE |
| Streaming | Linear over STREAM_DURATION = 7 days, re-based on each notify |
| Withdrawal | requestWithdraw(shares) then wait cooldown (constructor argument, MAX_COOLDOWN = 30 days) then redeem/withdraw with owner == msg.sender. requests(owner) returns the total pending shares and the latest unlockAt across all entries, which is the time from which every pending request is redeemable (a cooldown reduction can make earlier entries redeemable sooner; maxRedeem reflects that) |
| Share price | Monotonically non-decreasing |
Why Streaming
If rewards were credited instantly, a large NOTE holder could deposit in the block before a buyback fill, capture the reward, and withdraw. Streaming vests each reward linearly over seven days so that a depositor earns in proportion to time staked. Streaming and the withdrawal cooldown constrain short-lived reward capture; they do not remove every trading or contract risk.
Accounting
fact Accounting Calculation
fact The cooldown is set at deployment. PROTOCOL_SPEC.md specifies a 3-day default; script/DeployToken.s.sol currently defaults SNOTE_COOLDOWN to 7 days unless overridden by environment. Governance can change it with setCooldown up to 30 days. The launch value is tbd.
On notifyReward(a) the router has already transferred a NOTE. The call reverts with RewardNotReceived if the vault balance does not cover the new stream. The vault sets streamAmount = unvestedRewards() + a, streamStart = now, streamEnd = now + 7 days. totalAssets is unchanged at the moment of the call, so the share price does not jump.
While totalSupply() == 0 a notified reward is not streamed: it is added to deferredRewards, excluded from totalAssets, and RewardDeferred(notifier, amount, deferredTotal) is emitted. The first deposit that restores supply starts a fresh 7 day stream of the deferred amount plus any unvested remainder (RewardNotified with notifier == sNOTE). When the last shares are redeemed mid-stream the unvested part is deferred the same way, so no reward vests to an empty vault.
Worked Example
illustrative Vault holds 1,000,000 NOTE against 1,000,000 shares (price 1.0). The router notifies 7,000 NOTE at t = 0.
| Time | Unvested | totalAssets | Share price |
|---|---|---|---|
| t = 0 | 7,000 | 1,000,000 | 1.000000 |
| t = 1 day | 6,000 | 1,001,000 | 1.001000 |
| t = 3.5 days | 3,500 | 1,003,500 | 1.003500 |
| t = 7 days | 0 | 1,007,000 | 1.007000 |
At t = 3.5 days the router notifies a further 3,500 NOTE. Stream re-bases: streamAmount = 3,500 (unvested) + 3,500 = 7,000, vesting over the next 7 days from t = 3.5 d. totalAssets remains 1,003,500 at that instant and reaches 1,010,500 at t = 10.5 days.
Withdrawals
requestWithdraw(shares): the shares are transferred into the vault and escrowed as a new, independentWithdrawRequest{shares, unlockAt}withunlockAt = now + cooldown(at mostMAX_OPEN_REQUESTS = 8open requests per account). A later request never delays an earlier one; redemptions consume matured requests oldest-first, andrequests(owner)reports the total pending shares with theunlockAtof the newest request.- After
unlockAt,redeem(shares, receiver, owner)orwithdraw(assets, receiver, owner)for up to the pending amount, at the share price at redemption time. Escrowed shares keep accruing. cancelWithdrawRequest()returns escrowed shares to the caller.
Standard ERC-4626 redeem/withdraw without a matured request revert (NoRequest, CooldownActive(unlockAt), ExceedsRequested); calling on behalf of another owner reverts with OwnerMustBeCaller, so ERC-4626 allowances are not honoured for exits. deposit and mint are unrestricted (when not paused). Pausing applies to entries only: requestWithdraw, cancelWithdrawRequest, withdraw and redeem are never paused, so a holder can always start the cooldown and leave once it has matured.
Inflation Attack Protection
The 10^6 virtual-share offset makes the classic first-depositor donation attack unprofitable: an attacker must donate a million times the victim’s deposit to steal one wei of value.
Governance Surface
| Function | Who | Event |
|---|---|---|
setRewardNotifier(addr) | owner | RewardNotifierUpdated |
setCooldown(seconds) (≤ 30 days) | owner | CooldownUpdated |
| pause / unpause | guardian / owner | OZ Paused / Unpaused; stops deposit and mint only, exits stay open |
What sNOTE Does Not Do
- It does not mint NOTE. All rewards are NOTE already bought on the open auction with realised fees.
- It does not rebase. Balances are constant; share price rises.
- It does not lock beyond the cooldown. There are no lock tiers or boosted multipliers.
Keep exploring, or revisit this topic from the navigation.