Skip to content

spToken

spToken

spTokens are tokens minted and burnt upon supply and withdraw of assets to a SparkLend market, which denote the amount of crypto assets supplied and the yield earned on those assets. The spTokens’ value is pegged to the value of the corresponding supplied asset at a 1:1 ratio and can be safely stored, transferred or traded. All yield collected by the spTokens' reserves are distributed to spToken holders directly by continuously increasing their wallet balance.

EIP20 Methods

All standard EIP20 methods are implemented for spTokens, such as balanceOf, transfer, transferFrom, approve, totalSupply etc.

💡 `balanceOf` will always return the most up to date balance of the user, which includes their principal balance + the yield generated by the principal balance.

EIP712 Methods

DOMAIN_SEPARATOR

function DOMAIN_SEPARATOR()

Get the domain separator for the token at current chain.

nonces

function nonces(address owner)

Returns the nonce value for address specified as parameter. This is the nonce used when calling permit()

const token = new Contract(spTokenAddress, spToken.abi, provider);
await token.nonces(user);

SparkLend View Methods

scaledBalanceOf

function scaledBalanceOf(address user)

Returns the scaled supply balance of user. The scaled balance is the sum of all the updated stored balance divided by the reserve's liquidity index at the moment of the update.

getScaledUserBalanceAndSupply

function getScaledUserBalanceAndSupply(address user)

Returns the scaled balance of the user and the scaled total supply.

scaledTotalSupply

function scaledTotalSupply()

Returns the scaled total supply of the spToken.

getPreviousIndex

function getPreviousIndex(address user)

Returns last index interest that was accrued to the user's balance (expressed in ray).

getIncentivesController

function getIncentivesController()

Returns the address of the Incentives Controller contract

POOL

function POOL()

Returns the address of the associated Pool for the spToken.

UNDERLYING_ASSET_ADDRESS

function UNDERLYING_ASSET_ADDRESS()

Returns address of the underlying reserve asset.

RESERVE_TREASURY_ADDRESS

function RESERVE_TREASURY_ADDRESS()

Returns address of the Maker Treasury, controlled by governance, receiving the fee on this spToken.

SparkLend Write Methods

setIncentivesController

function setIncentivesController(ISparkIncentivesController controller)

Sets a new Incentives Controller.

permit

Allows a user to permit another account (or contract) to use their funds using a signed message. This enables gas-less transactions and single approval/transfer transactions.

ParameterTypeDescription
owneraddressThe owner of the funds
spenderaddressThe spender for the funds
valueuint256The amount the spender is permitted to use
deadlineuint256The deadline timestamp that the permit is valid. Use type(uint).max for no deadline.
vuint8Signature parameter
rbytes32Signature parameter
sbytes32Signature parameter
import { signTypedData_v4 } from 'eth-sig-util'
import { fromRpcSig } from 'ethereumjs-util'
// ... other imports
import spTokenAbi from "./spTokenAbi.json"
// ... setup your web3 provider
const spTokenAddress = "SPTOKEN_ADDRESS"
const spTokenContract = new web3.eth.Contract(spTokenAbi, spTokenAddress)
const privateKey = "YOUR_PRIVATE_KEY_WITHOUT_0x"
const chainId = 1
const owner = "OWNER_ADDRESS"
const spender = "SPENDER_ADDRESS"
const value = 100 // Amount the spender is permitted
const nonce = 1 // The next valid nonce, use `_nonces()`
const deadline = 1600093162
const permitParams = {
  types: {
    EIP712Domain: [
      { name: "name", type: "string" },
      { name: "version", type: "string" },
      { name: "chainId", type: "uint256" },
      { name: "verifyingContract", type: "address" },
    ],
    Permit: [
      { name: "owner", type: "address" },
      { name: "spender", type: "address" },
      { name: "value", type: "uint256" },
      { name: "nonce", type: "uint256" },
      { name: "deadline", type: "uint256" },
    ],
  },
  primaryType: "Permit",
  domain: {
    name: "spTOKEN_NAME",
    version: "1",
    chainId: chainId,
    verifyingContract: spTokenAddress,
  },
  message: {
    owner,
    spender,
    value,
    nonce,
    deadline,
  },
}
const signature = signTypedData_v4(
  Buffer.from(privateKey, "hex"),
  { data: permitParams }
)
// The signature can now be used to execute the transaction
const { v, r, s } = fromRpcSig(signature)
await spTokenContract.methods
    .permit({
      owner,
      spender,
      value,
      deadline,
      v,
      r,
      s
    })
    .send()
    .catch((e) => {
        throw Error(`Error permitting: ${e.message}`)
    })

FAQ

  • How spToken earn interest? / How spToken balance increases?

    Pool methods (deposit, withdraw, borrow, repay, liquidationCall) updates the state and cumulated liquidity index of the reserve once every block. SpToken's balanceOf method returns the balance computed based on block.timestamp and liquidityIndex of the underlying reserve and hence, returns the most up to date balance of account, which includes principal + interest.

  • LiquidityRate vs LiquidityIndex

  • Can I transfer spTokens?

    Yes! with few caveat to keep in mind

    • By transferring spTokens, you’re transferring your balance of the underlying asset. Only the account holding the spTokens can withdraw the deposited asset.
    • SpToken transfer will fail if the resulting Health Factor of user will end up being below 1.
  • If I transfer spToken does my pending liquidity rewards get transferred?

    No, liquidity rewards earned prior to the transfer of spToken are accrued by the user/address holding the spTokens originally. Though, all future liquidity rewards will be earned by the new recipient.

  • What is the difference between ScaledBalance and Balance?

  • Example please!

    Let’s say you supply 1,000 DAI to the Spark Pool, you will receive 1,000 spDAI (at 1:1 exchange rate).

    You can see your spDAI balance increasing right away.

    Now, say a month later your spDAI balance is 1,050. You could withdraw 1,050 DAI from LendingPool by burning 1050 spDAI.