Skip to content

Latest commit

 

History

History
242 lines (177 loc) · 28.4 KB

File metadata and controls

242 lines (177 loc) · 28.4 KB
SIP 0094
Title Perimeter Fee Activation and Adoption Fund Transfer
Author Edan Yago (@YagoBit)
Status Approved
Track Contract
Created 2026-08-12

SIP-0094 (Parts 1–3): Perimeter Fee Activation and Adoption Fund Transfer

Summary

This post introduces SIP-0094, voted as three technical proposals: the governor caps any single proposal at ten actions (proposalMaxOperations), a split Bitocracy has used before (SIP-0046, Parts 1–4; SIP-0084, Parts 1–2). Parts 1 and 2 run on the owner governor; Part 3 runs on the admin governor, which owns the contract it touches. Each part is its own ballot, and this document covers all three – the single reference for the programme, narrative and technical alike. All three are created in the same cycle and voted in the same window; they form one programme.

Part 1 installs the Perimeter Fee – the fee component of the Sovryn security perimeter – across the lending protocol and Zero: ten actions, all of them code installation.
Part 2 carries three actions: the final wiring action and, alongside it in two transfer legs, the Adoption Fund's fully-vested balance of 5,021,309.20 SOV moving to the Exchequer, continuing the arrangement Bitocracy created in SIP-0015 and has exercised since in SIP-0065 (2023), SIP-0076 (2024), and SIP-0079 (2024).
Part 3, a one-action proposal on the admin governor, retires the SOV emission subsidy to the Zero Stability Pool.

Nothing charges when these proposals execute. The system ships installed and switched off, and stays off until a separate enable step after post-deployment verification. The launch configuration, stated here so the vote is cast with full knowledge: a uniform 0.10% (10 basis points) – $1 per $1,000 – on user-initiated withdrawal transactions, shown in the interface before the user confirms.

Automated protocol flows – redemptions, liquidations, Stability Pool operations, rollovers – never pay the fee. They are not fee surfaces in the deployed code.

Background

Sovryn holds user funds across lending pools, loan collateral, and Zero troves. In today's DeFi threat environment, value is lost at exits: an exploit converts a bug into a withdrawal in minutes. The security perimeter answers this in phases, and phase 1 is the fee rail – the piece that must exist first, launched at a deliberately minimal 0.1% rate. On the treasury side, this is housekeeping. SIP-0015 (2021) created the Exchequer Committee and entrusted it with the SOV held in the Adoption and Development Funds as that SOV vests. Bitocracy has exercised that arrangement three times since – SIP-0065 (3,000,000 SOV, executed June 2023, tx 0xb1f0ccf6…77dfff32), SIP-0076 (February 2024, both funds), and SIP-0079 (2024, Adoption Fund, toward the BOB campaign) – and the funds' vesting schedules have since run to completion. What remains today is the final vested residue: 5,021,309.20 SOV in the Adoption Fund and 2,486,471.60 SOV in the Development Fund (balances verified on-chain, 5 August 2026, RSK block ~9,124,829). After these transfers the funds are empty and the arrangement begun with SIP-0015 in 2021 is complete.

How the security perimeter works

The perimeter is a trust boundary drawn around the protocol’s holdings. Inside it, nothing changes: positions, operations, and protocol flows compose exactly as they do today, with no new friction. The boundary acts only at the point where value leaves the protocol, because exits are where DeFi losses actually happen – an exploit only profits in the minutes it takes to turn a bug into a withdrawal.

The perimeter ships in phases, in deliberate order. Phase 1 – this programme – installs the funding rail: fee machinery on every user-initiated exit surface, deployed switched off and enabled at a minimal rate only after post-deployment verification. Phase 2 – targeted for the weeks after execution – ships the protection itself: continuous monitoring of every exit, and the ability to intervene in the window between a suspicious withdrawal beginning and value leaving the perimeter. The rate schedule publishes together with phase 2, so rates move only as protection ships and the fee tracks the protection actually delivered.

Rail-first is the safe order. The rail is the piece every later phase depends on, and it is the piece that changes user experience least – a visible $1-per-$1,000 fee, not a new constraint on movement. Until the perimeter is complete, every exit remains exactly as exposed as it is today; that exposure, not the fee, is what this programme exists to end.

Motivation

Most money markets charge nothing on withdrawal. Those same markets can also do nothing for a user in the minutes after an exploit begins. The Perimeter Fee is the funding rail for continuous monitoring of every exit – a real, ongoing service, charged where it is delivered, at a launch rate sized to be barely perceptible: $1 per $1,000, on user-initiated withdrawal transactions, visible in the interface before every confirmation. The goal is a protocol that pays for its own defence out of its own usage rather than out of treasury drawdown.

Proposed Changes

SIP-0094 Part 1 – Perimeter Fee installation (ten actions)

Ten governance actions that install the Perimeter Fee code: new lending-pool logic modules, three refreshed protocol modules, the fee module that owns the governance setters, the borrower-exit hook, and upgrades to two Zero contracts with their controller wiring. All ten execute as one atomic transaction – there is no partial-installation state. The action-by-action breakdown, with exact call data, target-by-target ownership verification, and rollback anchors for every replaced address, is in Deployed contracts below.

SIP-0094 Part 2 – final wiring and Adoption Fund transfer (three actions)

  1. The final Perimeter Fee wiring action (the deployment comprises eleven actions; the governor's ten-action cap places this one here — in the executable ordering it is the proposal's last action, asserted at creation). Executing Part 2 before Part 1 simply reverts and waits – an ordering mistake cannot create a broken state, which the fork rehearsal proves by executing the parts out of order and asserting the revert.
  2. Withdrawal of the Adoption Fund's fully-vested 5,021,309.20 SOV (0x0f31cfd6aAb4d378668Ad74DeFa89d3f4DB26633) to the Exchequer Multisig (0x924f5ad34698Fd20c90Fe5D5A8A0abd3b42dc711), under the SIP-0015 arrangement, in the shape SIP-0065 and SIP-0076 used. The withdrawal's leg-by-leg decomposition is frozen into the proposal at creation, together with the balance re-read from chain.

SIP-0094 Part 3 – Stability Pool subsidy retirement

A one-action proposal on the admin governor – the governor that owns the relevant contract – sets the SOV emission rate for Zero Stability Pool deposits to zero (CommunityIssuance.setAPR(0)), conserving the SOV currently spent to subsidise those deposits. Nothing is removed and no contract changes: it is a parameter set to zero, and a future governance vote could set it back.

Companion actions, disclosed for completeness

  • Development Fund residue. The Development Fund's 2,486,471.60 vested SOV transfers to the Exchequer by a direct multisig transaction in the same window, under the locked-token-owner authority the Exchequer holds on that contract, through the fund's deployed, verified sweep function – which empties the fund and retires it. No governance action is required for it; it is listed here so this post describes the whole programme in one place.
  • The enable step. After Parts 1 and 2 execute, charging begins only when post-deployment verification is green, monitoring is live, and the pause switch has been rehearsed in a live drill – at which point the Exchequer sends the enable transaction.

Technical Details

Surface Fee at launch
Lending-pool withdrawal (iToken redemption) 0.10%
Borrower collateral withdrawal / loan close 0.10%
Zero trove collateral withdrawal / closure 0.10%
Zero collateral-surplus claim 0.10%

One rate everywhere: no exemptions and no per-pool differences. Every user faces the same 10 bps on the same surface. That includes the collateral-surplus claim – the outlet for exits whose moment the user did not choose, such as a full redemption against a trove, which is kept as its own surface precisely so governance can price it separately, including at zero, at any time by configuration. Automated protocol flows : redemptions, liquidations, Stability Pool operations, or rollovers, are not fee surfaces in the deployed code; for liquidations, the separation is structural, since the liquidation module lies outside the charging code entirely and is not even redeployed by this release. A fifth surface, AMM liquidity-pool withdrawal, is defined in the deployed code but ships shut, with nothing consuming it in this release; a shut gate short-circuits everything beneath it, and activating it later would be an owner configuration call, visible on-chain the moment it is made.

  • Built to fail open. On any fault in the fee machinery: controller unreachable, invalid quote, vault transfer failure – the product forgoes the fee and pays the user the full amount, and the user's own payout leg keeps the protocol's existing behaviour, so Perimeter Fee introduces no new way for a withdrawal to fail. The failure direction of every Perimeter Fee fault is "fee forgone," never "withdrawal reverted." This is asserted across a full failure matrix in the test suites and reconfirmed under each lens of the multi-lens internal review.
  • Everything is visible. Every affected withdrawal shows the fee amount and an explanation before the user confirms. There are no silent deductions, and every configuration change emits a public on-chain event the moment it is made.
  • Rates are configuration, applied only forward. Rates live on the deployed controller as basis-points settings – changeable only by the contract owner, never retroactively: a fee is resolved at the moment a transaction executes, so no change can reach a withdrawal that has already happened. The launch setting is 10 bps everywhere; the stated intent – an intent, not a code constraint – is that rates move only with the rate schedule to be published with phase 2; and the instrument for any incident is the pause switch, which halts all charging in minutes and is rehearsed as a live mainnet drill before launch.
  • Exchequer Ownership The Exchequer Multisig – the operational committee Bitocracy created in SIP-0015 – holds both roles on the fee contracts at launch. The roles are deliberately split so that the fast operational keys are narrow: the admin role reaches only the pause switch, the fee destination, and fees already collected; it cannot touch user principal. Rates, exemptions, upgrades, and role assignment sit behind the owner role. User deposits sit in the product pools, which neither role can transfer; the one lever that can touch a future withdrawal is the rate setting itself – owner-only, forward-only, and public the instant it changes.
  • Proceeds. Collected fees accrue in the fee vault under Exchequer control; the transferred SOV joins the treasury the Exchequer already stewards under SIP-0015.
  • Code and review. Roughly 3,000 lines of production code carrying roughly 13,000 lines of tests, by the engineering team's count; a multi-lens internal security review (AI-assisted and manual, including a cross-model pass) with no surviving critical or important findings; five fund-safety invariants reconfirmed under each of the review lenses; and a full fork rehearsal through real governance: create, vote, queue, and execute, with charged and uncharged paths asserted on every surface.

Reviewing the code

All source is public and open for review now, while deployment is being prepared. Two of the three code bases are proposed as pull requests that stay open until this programme is approved and executed — mainline follows the chain rather than leading it — so the pull request is where the proposed change can be read as a diff against what is live today:

What Where to review
Perimeter Fee core — the shared exit-fee controller and vault Sovryn-perimeter (standalone repository; nothing to merge)
Lending and borrowing hooks, plus the proposal tooling for all three parts PR Sovryn-smart-contracts#580 (branch sovryn-perimeter-feedevelopment)
Zero trove-exit hooks and the collateral-surplus claim PR zero-contracts#10 (branch sovryn-perimeter-feedevelopment)

The implementations this programme installs are deployed from exactly those branch commits, and each is source-verified on the Rootstock Blockscout explorer — so the code read in the pull request, the bytecode on chain, and the addresses in the proposals below are the same artefact, checkable independently by anyone.

Deployed contracts

The Perimeter Fee contracts deploy — switched off, under Exchequer control — before this post publishes, so the table below carries final addresses at publication. Every address is source-verified on the Rootstock Blockscout explorer before publication, and each proposal's on-chain actions can be checked against this table action by action.

Part 1 — the ten actions, in execution order. Each is a single call with one address

argument; the ABI-encoded call data is that argument, and it is frozen into the proposal at creation, where it can be read in the Bitocracy interface and rebuilt from the proposal builders in the lending repository.

# Target Signature Argument
1 LoanTokenLogicBeaconLM 0x5b155ECcC1dC31Ea59F2c12d2F168C956Ac0FFAa registerLoanTokenModule(address) 0x593DB96E61F59F1278068742486Fc85CDBe9D872
2 LoanTokenLogicBeaconWrbtc 0x845eF7Be59664899398282Ef42239634aBDd752C registerLoanTokenModule(address) 0x6c8f59D321560f4bEF84c99e83dd7FF31122e14f
3 sovrynProtocol 0x5A0D867e0D70Fcc6Ade25C3F1B89d618b5B4Eaa7 replaceContract(address) 0xdB4fF0a861bF0c714B31E678c919c2B7EfD05329
4 sovrynProtocol replaceContract(address) 0xa3FCC9F88De9A7f0258eda6cD8d6F7D39ef6fb8d
5 sovrynProtocol replaceContract(address) 0xa87Bd1eF82FA2BE049473c73eAaa97d6AB9C4399
6 sovrynProtocol replaceContract(address) 0x0562e396f5fF7e819c8e9CcF25DCb68e16A2672F
7 sovrynProtocol setBorrowerExitPerimeterOps(address) 0xe533dc6D03Ae97977Da3357A98AA94b769738AB5
8 CollSurplusPool proxy 0x310ec7Fe6e4943DA773De8948255E37CC45e34bb setImplementation(address) 0xdd518C2aae5A83812883D4e350b88d3566792bE4
9 BorrowerOperations proxy 0x5B9dB4B8bdeF3e57323187a9AC2639C5DEe5FD39 setImplementation(address) 0x020aAb1d84E38b619A4C49f48d472c3b9187ce93
10 BorrowerOperations proxy setExitFeeController(address) 0x99994b4522483DE17F31a5bC010c5901AdD3440E

Actions 9 and 10 are ordered so the implementation swap precedes the controller wiring inside the same atomic transaction. No action carries RBTC value.

Core Perimeter Fee contracts (repo Sovryn-perimeter) — deployed and verified:

Contract Address
ExitFeeController proxy 0x99994b4522483DE17F31a5bC010c5901AdD3440E
ExitFeeController implementation 0x50EC5c1C156cfA7e3007a0b0C97298E4f58a552d
ExitFeeVault proxy 0xDDE75f75ff33Aa802f2316cCAe2bE77823fc6f9B
ExitFeeVault implementation 0x8f977F4c9dCcCE1a0306A34944C0460e4445deB4

Both implementations are source-verified on the Rootstock Blockscout explorer (solc 0.8.20, evm paris, optimizer 200 runs). The controller was deployed switched off and configured in the same session: the four charging surfaces registered active at 10 bps, the AMM surface registered shut, the fee receiver pointed at the vault, and both contracts' operational admin and ownership directed to the Exchequer Multisig — read back from chain after execution. Operational admin on both contracts is the Exchequer Multisig today.

Lending / borrowing implementations installed by Part 1 (repo Sovryn-smart-contracts, branch sovryn-perimeter-fee, review in PR #580):

Contract Address
LoanTokenLogicLM 0x593DB96E61F59F1278068742486Fc85CDBe9D872
LoanTokenLogicWrbtcLM 0x6c8f59D321560f4bEF84c99e83dd7FF31122e14f
LoanClosingsRollover 0xdB4fF0a861bF0c714B31E678c919c2B7EfD05329
LoanClosingsWith 0xa3FCC9F88De9A7f0258eda6cD8d6F7D39ef6fb8d
LoanMaintenance 0xa87Bd1eF82FA2BE049473c73eAaa97d6AB9C4399
ExitFeeModule 0x0562e396f5fF7e819c8e9CcF25DCb68e16A2672F
BorrowerExitPerimeterOps 0xe533dc6D03Ae97977Da3357A98AA94b769738AB5

Three of the modules Part 1 registers link the stateless library SwapsImplSovrynSwapLib, and all three link the same deployment of it — needed to reproduce their bytecode, and source-verified on the explorer:

Registered module Links SwapsImplSovrynSwapLib at
LoanClosingsRollover 0xFE2bb2d345452673C4E90622147c4F515F2F4CE0
LoanClosingsWith 0xFE2bb2d345452673C4E90622147c4F515F2F4CE0
LoanMaintenance 0xFE2bb2d345452673C4E90622147c4F515F2F4CE0

0xFE2bb2d… was deployed during this release. The library's own source is unchanged from the previous release, but it imports an interface this release extends, which changes the compiled artifact and therefore its address; the two are byte-identical apart from each one's embedded self-address and metadata fingerprint, which anyone can confirm on-chain. The release links this copy rather than the older one because the explorer verifies it as a full source match while the older copy matches only partially — so a voter rebuilding a module's bytecode lands on source the explorer vouches for exactly.

The library is not registered by Part 1; it is referenced only inside the linked modules' bytecode, and its address is listed here so every registered module can be independently rebuilt from source.

Zero implementations installed by Part 1 (repo zero-contracts, branch sovryn-perimeter-fee, review in PR #10):

Contract Address
BorrowerOperations implementation (fee-hooked) 0x020aAb1d84E38b619A4C49f48d472c3b9187ce93
CollSurplusPool implementation 0xdd518C2aae5A83812883D4e350b88d3566792bE4

For every module Part 1 replaces, the table below records the currently-live implementation it replaces – the rollback anchor – together with the runtime code hash of each new artifact, so a voter can bind every proposal action to both the address it installs and the address it supersedes. Anchors were read from the live protocol, beacons, and proxies on 2026-08-25; they are re-verified against the chain immediately before proposal creation, and any drift (an unrelated upgrade landing in between) voids and re-freezes this table.

Replaced module Currently live (rollback anchor) New implementation Runtime code hash
LoanClosingsRollover 0xc7AEa780c8b891e2c101a9ED2f9EaCF87FFdc359 0xdB4fF0a861bF0c714B31E678c919c2B7EfD05329 0xdb0abc5e825a401bd3239dfddc02612088ec1373ee29dd04a2bb3692e089c347
LoanClosingsWith 0x47eCc9391fd6f5761B8d24139f36Ce61fF6a9819 0xa3FCC9F88De9A7f0258eda6cD8d6F7D39ef6fb8d 0x34083ae377445509350becb30c2dd641ea531025eb5496c590d5cd91f6013b60
LoanMaintenance 0x4BC462e2c8D106511D8e7985b05A52fD8b726464 0xa87Bd1eF82FA2BE049473c73eAaa97d6AB9C4399 0xdb251795feb63cf2e1b952e0be33e20662e7c40f63a87d11e6a23935ce529587
iToken logic (LM) 0xfaffde7161C58743B86a22EF268245560aC705dD 0x593DB96E61F59F1278068742486Fc85CDBe9D872 0x6a08b3c8c9edbb040e4d33ee45cf5dc163e82b54b3dc72fced08cf0feb63c800
iToken logic (WrbtcLM) 0x24B3687966C4f05e48ED5A3B9c14F56d9ad5BF6B 0x6c8f59D321560f4bEF84c99e83dd7FF31122e14f 0xe09a3c2e8c1edcbb2960ff350ee36bbdb3c0a57210598ad271983fa98d674981
Zero BorrowerOperations 0xD603B4c5F7BF13a2AA510F2c625546dB4138D330 0x020aAb1d84E38b619A4C49f48d472c3b9187ce93 0xe978bc12cc95f546332aa28c029306ea2b45990307548c0b80bd740ae28e5cf1
Zero CollSurplusPool 0xE9005C36452576eFA74f297906e77381E6878398 (the original implementation — never upgraded before this proposal) 0xdd518C2aae5A83812883D4e350b88d3566792bE4 0xc00a07e5f8b0c1eba5b6b33e3ed38a9b15d7867e0fe6ea8611949b3bc53caa6f

ExitFeeModule and BorrowerExitPerimeterOps are new registrations rather than replacements, so they have no rollback anchor. LoanClosingsLiquidation is deliberately not redeployed: its runtime bytecode is byte-identical to the module already registered, so liquidations keep running against the same code throughout.

Risks

Risks introduced:

  • A fee where the market's default is zero. Users pay 10 basis points on withdrawals that were previously free.
  • The rail precedes the rest of the perimeter. Phase 1 charges its minimal rate before phase 2 ships.
  • New code in live paths. The fee vault and controller are new contracts; the release also replaces three live loan-closing and maintenance modules, upgrades two Zero contracts, one for the first time, and the borrower-exit hook carries the same protocol-level authority the existing core modules already hold.

Risk mitigants:

  • Minimal, visible, forward-only. $1 per $1,000, shown before every confirmation; no change ever reaches an executed transaction; every change is public instantly.
  • Fail-open everywhere. No Perimeter Fee fault path shrinks a user's withdrawal, the system gives up its fee instead, asserted across the failure matrix and reconfirmed under every lens of the internal review.
  • Off until verified. Both parts execute with charging globally disabled; the enable step waits for verification, live monitoring, and a rehearsed pause drill.
  • Rail-first sequencing. Deploying the rail at a minimal rate is what makes phase 2 deployable at all; every exit today is exactly as exposed as it was yesterday until the perimeter ships, which is the problem this programme exists to end.

Implementation

Item Value
Parts 1–3 Three proposals (Parts 1–2 on the owner governor, Part 3 on the admin governor), created in the same cycle, voted in the same window; Part 1 executes first (wrong order reverts harmlessly)
Owner and admin at launch Exchequer Multisig 0x924f5ad34698Fd20c90Fe5D5A8A0abd3b42dc711
Adoption Fund (Part 2 action) 0x0f31cfd6aAb4d378668Ad74DeFa89d3f4DB26633 – 5,021,309.20 SOV
Development Fund (companion multisig action) 0x617866cC4a089c3653ddC31a618b078291839AeB – 2,486,471.60 SOV
Stability Pool subsidy (Part 3 – one-action admin-governor proposal) CommunityIssuance.setAPR(0)
Contract addresses Deployed and explorer-verified before publication — see Deployed contracts above
Perimeter phase 2 Targeted for the weeks following execution, with a reserve window; rate schedule published then

Rationale

Three ballots: the deployment is eleven owner-governor actions against a ten-action cap, and the subsidy retirement lives on the admin governor because that governor owns the contract it touches – the same split Bitocracy used for SIP-0046 and SIP-0084. One window because the actions form one programme: the perimeter, and the treasury housekeeping that closes out the 2021 fund arrangement. Each part is its own proposal with its own tally, so holders judge each on its merits – with one stated cost: Part 2 carries the final wiring action and the Adoption Fund withdrawal on the same ballot, so a holder weighing those two purposes weighs them together. That is the price of keeping the programme to two governance cycles, stated here rather than hidden. The Development Fund residue moves by multisig because the Exchequer already holds the relevant authority on that contract; disclosing it here, rather than exercising it silently, is the point of this section.

License

Copyright and related rights waived via CC0.