sTokens

Introduction

Each asset supported by the Source One Market is integrated through a sToken contract, which is an EIP-20 compliant representation of balances supplied to the protocol. By minting sTokens, users (1) earn interest through the sToken's exchange rate, which increases in value relative to the underlying asset, and (2) gain the ability to use sTokens as collateral.

sTokens are the primary means of interacting with the Source One Market; when a user mints, redeems, borrows, repays a borrow, liquidates a borrow, or transfers sTokens, she will do so using the sToken contract.

There are currently two types of sTokens: SBep20 and SBnb. Though both types expose the EIP-20 interface, SBep20 wraps an underlying BEP-20 asset, while SBnb simply wraps BNB itself. As such, the core functions which involve transferring an asset into the protocol have slightly different interfaces depending on the type, each of which is shown below.

Mint

The mint function transfers an asset into the protocol, which begins accumulating interest based on the current Supply Rate for the user receives a quantity of sTokens equal to the underlying tokens tokens supplied, divided by the current Exchange Rate.

SBep20

function mint(uint mintAmount) returns (uint)
  • msg.sender: The account which shall supply the asset, and own the minted sTokens.

  • mintAmount: The amount of the asset to be supplied, in units of the underlying asset.

  • RETURN: 0 on success, otherwise an Error code

Before supplying an asset, users must first approve the sToken to access their token balance.

SBnb

function mint() payable
  • msg.value: payable The amount of ether to be supplied, in wei.

  • msg.sender: The account which shall supply the bnb, and own the minted sTokens.

  • RETURN: No return, reverts on error.

Solidity

Bep20 underlying = Bep20(0xToken...);     // get a handle for the underlying asset
SBep20 sToken = SBep20(0x3FDA...);        // get a handle for the corresponding vToken Contract
underlying.approve(address(sToken), 100); // approve the transfer
assert(sToken.mint(100) == 0);            // mint the sTokens and assert there is no error

Web3 1.0

const sToken = SBep20.at(0x3FDB...);
await sToken.methods.mint().send({from: myAccount, value: 50});

Redeem

The redeem function converts a specified quantity of sTokens into the underlying asset, and returns them to the user. The amount of underlying tokens received is equal to the quantity of sTokens redeemed, multiplied by the current Exchange Rate. The amount redeemed must be less than the user's Account Liquidity and the market's available liquidity.

SBep20 / SBnb

function redeem(uint redeemTokens) returns (uint)
  • msg.value: The account to which redeemed funds shall be transferred.

  • redeemTokens: The number of sTokens to be redeemed.

  • RETURN: 0 on success, otherwise an Error code

Solidity

SBnb sToken = SBnb(0x3FDB...);
require(sToken.redeem(7) == 0, "something went wrong");

Web3 1.0

const sToken = SBep20.at(0x3FDA...);
sToken.methods.redeem(1).send({from: ...});

Redeem Underlying

The redeem underlying function converts sTokens into a specified quantity of the underlying asset, and returns them to the user. The amount of sTokens redeemed is equal to the quantity of underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less than the user's Account Liquidity and the market's available liquidity.

SBep20 / SBnb

function redeemUnderlying(uint redeemAmount) returns (uint)
  • msg.sender: The account to which redeemed funds shall be transferred.

  • redeemAmount: The amount of underlying to be redeemed.

  • RETURN: 0 on success, otherwise an Error code

Solidity

SBnb sToken = SBnb(0x3FDB...);
require(sToken.redeemUnderlying(50) == 0, "something went wrong");

Web3 1.0

const sToken = SBep20.at(0x3FDA...);
sToken.methods.redeemUnderlying(10).send({from: ...});

Borrow

The borrow function transfers an asset from the protocol to the user, and creates a borrow balance which begins accumulating interest based on the Borrow Rate for the asset. The amount borrowed must be less than the user's Account Liquidity and the market's available liquidity.To borrow Ether, the borrower must be 'payable' (solidity).

SBep20 / SBnb

function borrow(uint borrowAmount) returns (uint)
  • msg.sender: The account to which redeemed funds shall be transferred.

  • redeemAmount: The amount of underlying to be redeemed.

  • RETURN: 0 on success, otherwise an Error code

Solidity

SBnb sToken = SBnb(0x3FDB...);
require(sToken.borrow(100) == 0, "got collateral?");

Web3 1.0

const sToken = SBep20.at(0x3FDA...);
await sToken.methods.borrow(50).send({from: 0xMyAccount});

Repay Borrow

The repay function transfers an asset into the protocol, reducing the user's borrow balance.

SBep20

function repayBorrow(uint repayAmount) returns (uint)
  • msg.sender: The account which borrowed the asset, and shall repay the borrow.

  • borrowAmount: The amount of the underlying borrowed asset to be repaid. A value of -1 (i.e. 2256 - 1) can be used to repay the full amount.

  • RETURN: 0 on success, otherwise an Error code

Before repaying an asset, users must first approve the sToken to access their token balance.

SBnb

function repayBorrow() payable
  • msg.value: payable The amount of ether to be repaid, in wei.

  • msg.sender: The amount which borrowed the asset, and shall repay the borrow

  • RETURN: No return, reverts on error

Solidity

SBnb sToken = SBnb(0x3FDB...);
require(sToken.repayBorrow.value(100)() == 0, "transfer approved?");

Web3 1.0

const sToken = SBep20.at(0x3FDA...);
sToken.methods.repayBorrow(10000).send({from: ...});

Repay Borrow Behalf

The repay function transfers an asset into the protocol, reducing the target user's borrow balance.

SBep20

1function repayBorrowBehalf(address borrower, uint repayAmount) returns (uint)
  • msg.sender: The account which shall repay the borrow.

  • borrower: The account which borrowed the asset to be repaid.

  • repayAmount: The amount of the underlying borrowed asset to be repaid. A value of -1 (i.e. 2256 - 1) can be used to repay the full amount.

  • RETURN: 0 on success, otherwise an Error code

Before repaying an asset, users must first approve the sToken to access their token balance.

SBnb

1function repayBorrowBehalf(address borrower) payable
  • msg.value: payable The amount of ether to be repaid, in wei.

  • msg.sender: The account which shall repay the borrow

  • borrower: The account which borrowed the asset to be repaid

  • RETURN: No return, reverts on error

Solidity

SBnb sToken = SBnb(0x3FDB...);
require(sToken.repayBorrowBehalf.value(100)(0xBorrower) == 0, "transfer approved?");

Web3 1.0

const sToken = SBep20.at(0x3FDA...);
await sToken.methods.repayBorrowBehalf(0xBorrower, 10000).send({from: 0xPayer});

Liquidate Borrow

A user who has negative account liquidity is subject to liquidation by other users of the protocol to return his/her account liquidity back to positive (i.e. above the collateral requirement). When a liquidation occurs, a liquidator may repay some or all of an outstanding borrow on behalf of a borrower and in return receive a discounted amount of collateral held by the borrower; this discount is defined as the liquidation incentive.

A liquidator may close up to a certain fixed percentage (i.e. close factor) of any individual outstanding borrow of the underwater account. Unlike in v1, liquidators must interact with each sToken contract in which they wish to repay a borrow and seize another asset as collateral. When collateral is seized, the liquidator is transferred sTokens, which they may redeem the same as if they had supplied the asset themselves. Users must approve each sToken contract before calling liquidate (i.e. on the borrowed asset which they are repaying), as they are transferring funds into the contract.

SBep20

function liquidateBorrow(address borrower, uint amount, address collateral) returns (uint)
  • msg.sender: The account which shall liquidate the borrower by repaying their debt and seizing their collateral.

  • borrower: The account with negative account liquidity that shall be liquidated.

  • repayAmount: The amount of the borrowed asset to be repaid and converted into collateral, specified in units of the underlying borrowed asset.

  • sTokenCollateral: The address of the sToken currently held as collateral by a borrower, that the liquidator shall seize.

  • RETURN: 0 on success, otherwise an Error code

Before repaying an asset, users must first approve the sToken to access their token balance.

SBnb

function liquidateBorrow(address borrower, address sTokenCollateral) payable
  • msg.value: payable The amount of ether to be repaid and converted into collateral, in wei.

  • msg.sender: The account which shall liquidate the borrower by repaying their debt and seizing their collateral.

  • borrower: The account with negative account liquidity that shall be liquidated.

  • sTokenCollateral: The address of the sToken currently held as collateral by a borrower, that the liquidator shall seize.

  • RETURN: No return, reverts on error

Solidity

SBnb sToken = SBnb(0x3FDB...);
SBep20 sTokenCollateral = SBep20(0x3FDA...);
require(sToken.liquidateBorrow.value(100)(0xBorrower, sTokenCollateral) == 0, "borrower underwater??");

Web3 1.0

const sToken = SBep20.at(0x3FDA...);
const sTokenCollateral = SBnb.at(0x3FDB...);
await sToken.methods.liquidateBorrow(0xBorrower, 33, sTokenCollateral).send({from: 0xLiquidator});

Key Events

Error Codes

Failure Info

Exchange Rate

Each sToken is convertible into an ever increasing quantity of the underlying asset, as interest accrues in the market. The exchange rate between a sToken and the underlying asset is equal to:

exchangeRate = (getCash() + totalBorrows() - totalReserves()) / totalSupply()

SBep20 / SBnb

function exchangeRateCurrent() returns (uint)

RETURN: The current exchange rate as an unsigned integer, scaled by 1e18.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint exchangeRateMantissa = sToken.exchangeRateCurrent();

Web3 1.0

const sToken = SBnb.at(0x3FDB...);
const exchangeRate = (await sToken.methods.exchangeRateCurrent().call()) / 1e18;

Tip: note the use of call vs. send to invoke the function from off-chain without incurring gas costs.

Get Cash

Cash is the amount of underlying balance owned by this sToken contract. One may query the total amount of cash currently available to this market.

SBep20 / SBnb

function getCash() returns (uint)

RETURN: The quantity of underlying asset owned by the contract.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint cash = sToken.getCash();

Web3 1.0

const sToken = sBnb.at(0x3FDB...);
const cash = (await sToken.methods.getCash().call());

Total Borrow

A user who borrows assets from the protocol is subject to accumulated interest based on the current borrow rate. Interest is accumulated every block and integrations may use this function to obtain the current value of a user's borrow balance with interest.

SBep20 / SBnb

function totalBorrowsCurrent() returns (uint)

RETURN: The quantity of underlying asset owned by the contract.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint borrows = sToken.totalBorrowsCurrent();

Web3 1.0

const sToken = SBnb.at(0x3FDB...);
const borrows = (await sToken.methods.totalBorrowsCurrent().call());

Borrow Balance

A user who borrows assets from the protocol is subject to accumulated interest based on the current borrow rate. Interest is accumulated every block and integrations may use this function to obtain the current value of a user's borrow balance with interest.

SBep20 / SBnb

function borrowBalanceCurrent(address account) returns (uint)
  • account: The account which borrowed the assets.

  • RETURN: The user's current borrow balance (with interest) in units of the underlying asset.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint borrows = sToken.borrowBalanceCurrent(msg.caller);

Web3 1.0

const sToken = SBnb.at(0x3FDB...);
const borrows = await sToken.methods.borrowBalanceCurrent(account).call();

Borrow Rate

At any point in time one may query the contract to get the current borrow rate per block.

SBep20 / SBnb

function borrowRatePerBlock() returns (uint)

RETURN: The current borrow rate as an unsigned integer, scaled by 1e18.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint borrowRateMantissa = sToken.borrowRatePerBlock();

Web3 1.0

const sToken = SBnb.at(0x3FDB...);
const borrowRate = (await sToken.methods.borrowRatePerBlock().call()) / 1e18;

Total Supply

Total Supply is the number of tokens currently in circulation in this sToken market. It is part of the EIP-20 interface of the sToken contract.

SBep20 / SBnb

function totalSupply() returns (uint)

RETURN: The total number of tokens in circulation for the market.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint tokens = sToken.totalSupply();

Web3 1.0

const sToken = SBnb.at(0x3FDB...);
const tokens = (await sToken.methods.totalSupply().call());

Underlying Balance

The user's underlying balance, representing their assets in the protocol, is equal to the user's sToken balance multiplied by the Exchange Rate.

SBep20 / SBnb

function balanceOfUnderlying(address account) returns (uint)
  • account: The account to get the underlying balance of.

  • RETURN: The amount of underlying currently owned by the account.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint tokens = sToken.balanceOfUnderlying(msg.caller);

Web3 1.0

const sToken = SBnb.at(0x3FDB...);
const tokens = await sToken.methods.balanceOfUnderlying(account).call();

Supply Rate

At any point in time one may query the contract to get the current supply rate per block. The supply rate is derived from the borrow rate, reserve factor and the amount of total borrows.

SBep20 / SBnb

function supplyRatePerBlock() returns (uint)

RETURN: The current supply rate as an unsigned integer, scaled by 1e18.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint supplyRateMantissa = sToken.supplyRatePerBlock();

Web3 1.0

const sToken = SBnb.at(0x3FDB...);
const supplyRate = (await sToken.methods.supplyRatePerBlock().call()) / 1e18;

Total Reserves

Reserves are an accounting entry in each sToken contract that represents a portion of historical interest set aside as cash which can be withdrawn or transferred through the protocol's governance. A small portion of borrower interest accrues into the protocol, determined by the reserve factor.

SBep20 / SBnb

function totalReserves() returns (uint)

RETURN: The total amount of reserves held in the market.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint reserves = sToken.totalReserves();

Web3 1.0

const sToken = SBnb.at(0x3FDB...);
const reserves = (await sToken.methods.totalReserves().call());

Reserve Factor

The reserve factor defines the portion of borrower interest that is converted into reserves.

SBep20 / SBnb

function reserveFactorMantissa() returns (uint)

RETURN: The current reserve factor as an unsigned integer, scaled by 1e18.

Solidity

SBep20 sToken = SToken(0x3FDA...);
uint reserveFactorMantissa = sToken.reserveFactorMantissa();

Web3 1.0

const sToken = SBnb.at(0x3FDB...);
const reserveFactor = (await sToken.methods.reserveFactorMantissa().call()) / 1e18;

Last updated