# Smart Contracts

diagram

The Divider is the accounting engine of the Sense Protocol. It allows users to "divide" Target assets into ERC20 Principal Tokens (PTs) (opens new window) & Yield Tokens (YTs) (opens new window) with the help of numerous auxiliary contracts including Adapters, Adapter Factories, and the Periphery contract. Each Target can have an arbitrary number of active instances or series of PTs and YTs, and each series is uniquely identified by their maturity. The Divider reads scale values (opens new window) from Adapters to determine how much Target to distribute to PT & YT holders at or before maturity. Constituting the "core" of Sense, these contracts fully implement the Sense Lifecycle (opens new window) as well as permissionless series management & onboarding of arbitrary Adapters.

The core is accompanied by the following modules:

All user journeys & entry points of the Sense Protocol are outlined here (opens new window), and the following sections dive into each contract.

# Core

# Divider

Divider source (opens new window)

The Divider contains the logic to issue() ERC20 PTs and YTs, re-combine() those assets into Target before their maturity, collect() Target with YTs, and redeem() at or after maturity. Beyond series management, such as initialization & settlement, the Divider purposefully pushes application logic to the Adapters. Divider math derivations (opens new window) (thank you ABDK!).

# Adapter

BaseAdapter source (opens new window)

CropAdapter source (opens new window)

Following a hub and spoke model, Adapters surround the Divider and hold logic related to their particular application (opens new window), such as stripping yield from yield-bearing assets. Once an Adapter is onboarded, users can initialize/settle series, issue PTs/YTs, and collect/redeem their Target via the Divider.

The Adapter holds the Target before a series' maturity and contains logic to handle arbitrary airdrops from native or 3rd party liquidity mining programs. Typically denominated in another asset, airdropped tokens are distributed to YT holders in addition to the yield accrued from the Target. Moreover, Adapters store parameters related to their individual applications, which is digested by the Divider when performing the above-mentioned operations. The parameters include:

  1. target - address to the Target
  2. underlying - address to the Target’s underlying
  3. oracle - address to the Oracle of the Target's Underlying
  4. stake - token to stake at issuance
  5. stakeSize - amount to stake at issuance
  6. minm - min maturity (seconds after block.timestamp)
  7. maxm - max maturity (seconds after block.timestamp)
  8. mode - maturity date type (0 for monthly, 1 for weekly)
  9. ifee - series issuance fee (opens new window)
  10. tilt - any principal set aside for YTs at maturity (relevant for non-stripping applications)
  11. level - feature access codes

Non-airdrop Adapter implementations must inherit BaseAdapter and override the following functions:

  1. scale()
  2. getUnderlyingPrice()
  3. wrapUnderlying()
  4. unwrapTarget()

The other overridable functions are mostly available for non-stripping adapters that require additional flexibility, such as BaseAdapter.onRedeem(), a hook called on PT redemptions.

Airdrop adapter implementations must inherit CropAdapter and override all required functions mentioned above as well as _claimReward(), which allows the adapter to harvest airdrops from the Target and distribute them to YT holders.

On collects and redemptions, the Divider queries the adapter for scales, an abstract metric used to define how the Target is divided between PT and YT holders.

How the scale is determined, or even what the scale represents, is dependent on the adapter’s application. For example, in the yield stripping application, scale represents the exchange rate between the Target & Underlying, and the scale’s growth is the yield earned on the Target.

However, because the scale calculation is abstracted away from the Divider, the adapter has full discretion in how and when it determines the scale value.

There are two types of Adapters:

  1. Verified Adapters - verified by the Sense team and can be permissionlessly deployed by Adapter Factories
  2. Unverified Adapters - unverified or deemed unsafe by the Sense team and could be controlled by malicious actors

During Sense’s guarded launch (opens new window), the Divider interfaces only with Verified Adapters. However, once the permissionless flag is enabled, users can permissionessly onboard Adapters via Divider.addAdapter() and leverage Sense's infrastructure to build new fixed-income products, structured products, and yield primitives never before seen in DeFi.

# Adapter factory

BaseFactory source (opens new window)

The Adapter factory allows any person to deploy a Verified Adapter for a given Target in a permissionless manner.

Following a gradual expansion, Sense Finance will deploy one Adapter Factory for each protocol (e.g cTokens Adapter Factory, Curve LP Share Adapter Factory, etc).

Most factories are be similar except for how they implement _exists(), a method that checks whether the Target address supplied is a supported asset of that protocol.

To create an Adapter Factory, the contract needs to inherit from BaseFactory and override exists() and deployAdapter().

Once the Adapter Factory is built and verified in the Periphery, anyone can deploy a Verified Adapter via Periphery.deployAdapter().

# Periphery

Periphery source (opens new window)

The Periphery contains bundled actions for Series Actors (opens new window) and general users (opens new window).

For Series Actors, the Periphery exposes the public entry points to deploy Verified Adapters and initialize series. The Target Sponsor calls deployAdapter which deploys an Adapter via an Adapter Factory and onboards the Target to the Sense Fuse Pool. The Series Sponsor calls sponsorSeries to initialize a series in the Divider, deploy a Space pool, and onboard the series’ PTs and Space LP shares into the Sense Fuse Pool.

Series sponsorship of all Adapters is initiated through the Periphery. Because their implementation is unknown or unsafe, assets from unverified adapters are excluded from the Sense Fuse Pool, and they have the option to skip Space Pool deployment.

Space only holds PTs & Targets, so users need to execute additional steps to issue() and combine() in order to enter/exit into/from a YT position (opens new window). The Periphery allows users to bundle the necessary calls behind a single function interface and perform the following operations atomically, flash loaning Target from an Adapter when need be:

  • swap[Target|Underlying]ForPTs
  • swap[Target|Underlying]ForYTs
  • swapPTsFor[Target|Underlying]
  • swapYTsFor[Target|Underlying]

Similarly, the Periphery exposes several atomic transactions for LP management through Space.

  • addLiquidityFrom[Target|Underlying]
  • removeLiquidityTo[Target|Underlying]
  • migrateLiquidity

# Tokens

Token source (opens new window)

YT source (opens new window)

Sense Protocol uses Rari's ERC20 implementation (opens new window) and defines:

  • Token as a minimalist ERC20 implementation with auth'd burn() and mint(). Used for PTs.
  • YT as a minimalist yield token implementation that:
    1. inherits from Token
    2. adds maturity, divider and adapter address variables
    3. defines collect() (which calls Divider.collect()) and overrides transfer() and transferFrom() to also call collect()

# Modules

# Space

Space source (opens new window)

SpaceFactory source (opens new window)

Space is a PT/Target AMM Pool built on Balancer V2. It implements the Yieldspace (opens new window) invariant but introduces a meaningful improvement by allowing LPs to deposit a yield-generating quote asset, i.e. the Target, instead of the PT’s Underlying, as was originally conceived.

Building on the shoulders of giants, Space exposes a manipulation-resistant LP share price and a TWAR (time-weighted average rate), both of which are digested by the Sense Fuse Pool. Each Series has a unique Space for PT/Target trading, which is deployed through the SpaceFactory.

# Pool Manager

Pool Manager source (opens new window)

PoolManager manages the Sense Fuse Pool, a collection of borrowing/lending markets serving all PTs, the Space LP Shares, and their respective Targets. It allows users to permissionlessly onboard new Targets (addTarget()), PTs and their Space LP shares (queueSeries() & addSeries()). Once new assets are onboarded, the Sense Fuse Pool queries price data from the Master Oracle which maps token addresses to oracle addresses.

# Access

Trust source (opens new window)

We use Trust to provide access control via requiresTrust to contracts inheriting from it. In some contracts, we introduce per-function access control for greater granularity, such as peripherOnly.

# Admin

Admin docs are located in the security page. (opens new window)

Last Updated: 10/11/2022, 7:38:38 PM