Borrowing Function

Obligation


Before doing any borrowing related action, you need to have Obligation object. Obligation is an object to record your collateral and debt. So you need to create at least one to do any related borrowing action.

It’s possible to have multiple obligation.

Each obligation isn’t related to another, so depositing collateral in one obligation doesn’t meant you can borrow an asset using another obligation - even you are the owner of both obligations.

Understanding Obligation

Obligation is a shared object, instead of owning Obligation object in your wallet - you will hold an ObligationKey in your wallet. This ObligationKey is a proof you own a particular Obligation that connected to this ObligationKey. So what you will see in your wallet is an NFT of ObligationKey.

Let’s learn how to create Obligation object:

Open Obligation

public fun open_obligation(
  version: &Version,
  ctx: &mut TxContext,
): (Obligation, ObligationKey, ObligationHotPotato) { }

Parameters:

Name

Type

Description

version

Version

The version control object, contract version must match with this

Return Values

Name
Type
Description

Obligation

Obligation

The obligation object which will be shared

Obligation Key

ObligationKey

This is an owned object that serves as proof of ownership of the obligation object.

ObligationHotPotato

ObligationHotPotato

Because open_obligation function used HotPotato pattern which this pattern is make it composable.

Return Obligation

public fun return_obligation(
	version: &Version,
	obligation: Obligation,
	obligation_hot_potato: ObligationHotPotato,
	ctx: &mut TxContext,
) { }

Parameters:

Name

Type

Description

version

Version

The version control object, contract version must match with this

obligation

Obligation

The obligation object created by open_obligation.

obligation_hot_potato

ObligationHotPotato

HotPotato object generated by open_obligation .

You might be wondering why we implement the HotPotato pattern in the open_obligation function. The reason is that the Obligation is a shared object. If we directly return the object, it will be shared, and we won’t be able to combine this transaction with another function for borrowing in a single PTB (Programmable Transaction Block).

By using this methodology, we can combine the open obligation transaction with a deposit collateral transaction in one PTB. Also to make the Obligation shared, we also need to include the return_obligation function to transaction to make the Obligation object become shared.

Example:

module protocol::foo {

  use sui::coin::{Self, Coin};
  use sui::clock::{Self, Clock};
  use sui::tx_context::{Self ,TxContext};
  use sui::transfer;

  use scallop_protocol::obligation::open_obligation;
  use scallop_protocol::obligation::return_obligation;
  use scallop_protocol::Market as ScallopMarket;
  use scallop_protocol::Version as ScallopVersion;

  ...

  public fun doFoo(
    scallop_version: &ScallopVersion,
    clock: &Clock,
    ctx: &mut TxContext
  ) {
    let (obligation_id, obligation_key, obligation_hot_potato) = scallop_protocol::obligation::open_obligation(
	scallop_version,
	scallop_market,
	coin,
	clock,
	ctx,
    );
    // ----------------
    // Do other tx here

    // ----------------
    scallop_protocol::obligation::return_obligation(
      scallop_version,
      obligation_id,
      obligation_hot_potato
     );
    transfer::transfer(obligation_key, ctx.sender());
  }
}

Now, you are ready to do any borrowing related actions with your Obligation.

To be able to borrow, you need to have collateral. Let’s learn how to deposit collateral.

Collateral


Deposit Collateral

Allows deposit collateral to your obligation account. This function allow anyone to deposit collateral into other user obligation account. Why everyone can do it? Because this is fine if someone doing it for you. You will not loss anything if someone does it for you. So we open it for everyone.

public fun deposit_collateral<T>(
  version: &Version,
  obligation: &mut Obligation,
  coin: Coin<T>,
  ctx: &mut TxContext,
) { }

Parameters:

Name

Type

Description

version

Version

The version control object, contract version must match with this

obligation

Obligation

The id of obligation, it should be shared object.

coin

Coin

The base asset to be deposit to the collateral pool

Events:

  • CollateralDepositEvent is emmited when the collateral deposit tx success

Errors:

  • 770: Means your current obligation is locked and need to unstake first from borrow incentive

  • 73730: Means current collateral is not active yet.

  • 1794: Invalid collateral type.

  • 1796: Unable to deposited borrowed coin because the depositor try to deposit same asset as depositor borrowed.

Example:

module protocol::foo {

  use sui::coin::{Self, Coin};
  use sui::clock::{Self, Clock};
  use sui::tx_context::{Self ,TxContext};
  use sui::transfer;

  use scallop_protocol::obligation::open_obligation;
  use scallop_protocol::obligation::return_obligation;
  use scallop_protocol::deposit_collateral::deposit_collateral;
  use scallop_protocol::Market as ScallopMarket;
  use scallop_protocol::Version as ScallopVersion;
  use scallop_protocol::obligation::{Self, Obligation};


  public fun doFoo<T>(
    scallop_version: &ScallopVersion,
    obligation: &mut Obligation,
    coin: Coin<T>,
    ctx: &mut TxContext
   ) {
     let deposit_amount = coin::value(&coin);
	
     let (obligation_id, obligation_key, obligation_hot_potato) = scallop_protocol::obligation::open_obligation(
	scallop_version,
	scallop_market,
	coin,
	clock,
	ctx,
     );
    // ----------------
    // Do other tx here
	
    scallop_protocol::deposit_collateral::deposit_collateral(
      scallop_version,
      obligation_id,
      deposit_amount,
      ctx,
    );
   // ----------------
	
    scallop_protocol::obligation::return_obligation(
      scallop_version,
      obligation_id,
      obligation_hot_potato
    );
    transfer::transfer(obligation_key, ctx.sender());
  }
}

Withdraw Collateral

Unlike depositing collateral, withdrawing collateral requires the ObligationKey object, which is an owned object. This means that only the owner of the obligation account can withdraw collateral; another address cannot withdraw collateral from an obligation account it does not own.

public fun withdraw_collateral<T>(
    version: &Version,
    obligation: &mut Obligation,
    obligation_key: &ObligationKey,
    market: &mut Market,
    coin_decimals_registry: &CoinDecimalsRegistry,
    withdraw_amount: u64,
    x_oracle: &XOracle,
    clock: &Clock,
    ctx: &mut TxContext,
  ): Coin<T> { }

Parameters:

Name

Type

Description

version

Version

The version control object, contract version must match with this

obligation

Obligation

The id of obligation, it should be shared object.

obligation_key

ObligationKey

The key to prove the ownership of the obligation

market

Market

The Scallop market object, it contains base assets, and related protocol configs

coin_decimal_registry

CoinDecimalsRegistry

The package that has decimal coin of base assets.

withdraw_amount

u64

The amount assets will withdraw from pool

x_orcale

XOracle

The x-oracle object which provides the price of assets

clock

Clock

The SUI system clock object

Return Value:

Name
Type
Description

Coin

Coin

The withdrawed underlying asset

Events

  • CollateralWithdrawEvent is emmited when transaction success

Errors

  • 770: Means your current obligation is locked and need to unstake first from borrow incentive

  • 0: Not owner of obligation

  • 1795: Withdraw collateral to much

Example

module protocol::foo {

  use sui::coin::{Self, Coin};
  use sui::clock::{Self, Clock};
  use sui::tx_context::{Self ,TxContext};
  
  use sui::transfer;

  use scallop_protocol::withdraw_collateral::withdraw_collateral;
  use scallop_protocol::Market as ScallopMarket;
  use scallop_protocol::Version as ScallopVersion;
  use scallop_protocol::obligation::{Self, Obligation, ObligationKey};
  use scallop_protocol::coin_decimals_registry::CoinDecimalsRegistry;
  use scallop_protocol::x_oracle::XOracle;
  
  ...
	
	
  public fun doFoo<T>(
    version: &ScallopVersion,
    obligation: &mut Obligation,
    obligation_key: &ObligationKey,
    market: &mut ScallopMarket,
    coin_decimals_registry: &CoinDecimalsRegistry,
    withdraw_amount: u64,
    x_oracle: &XOracle,
    clock: &Clock,
    ctx: &mut TxContext
  ) {
    let withdrawed_coin = scallop_protocol::withdraw_collateral::withdraw_collateral(
      scallop_version,
      obligation_id,
      obligation_key,
      market,
      coin_decimals_registry,
      withdraw_amount,
      x_oracle,
      clock,
      ctx,
    );
    transfer::transfer(withdrawed_coin, ctx.sender());
  }
}

Check here the example how to do update price x_oracle :

https://github.com/scallop-io/sui-scallop-sdk/blob/main/src/builders/oracle.ts

We recommend you to call this function using our Scallop SDK, so before calling any function related to withdraw Collateral, you need to call update price in the beginning of the PTB (Programmable Transaction Block).

Here what your PTB should looks like:

PTB for calling function that have withdraw collateral:
- update price (you get this from SDK)
- your function that call withdraw_collateral

Borrowing


Borrow

Allow user to borrow assets from borrowing pools againts collateral deposited by user.

 public fun borrow<T>(
  version: &Version,
  obligation: &mut Obligation,
  obligation_key: &ObligationKey,
  market: &mut Market,
  coin_decimals_registry: &CoinDecimalsRegistry,
  borrow_amount: u64,
  x_oracle: &XOracle,
  clock: &Clock,
  ctx: &mut TxContext,
): Coin<T> { }

Parameters:

Name
Type
Description

version

Version

The version control object, contract version must match with this

obligation

Obligation

The id of obligation, it should be shared object.

obligation_key

ObligationKey

The key to prove the ownership of the obligation

market

Market

The Scallop market object, it contains base assets, and related protocol configs

coin_decimals_registry

CoinDecimalsRegistry

The package that has decimal coin of base assets.

borrow_amount

u64

The amount assets will borrow from pool

x_oracle

XOracle

The x-oracle object which provides the price of assets

clock

Clock

The SUI system clock object

Returns Value

Name
Type
Description

coin

Coin

The borrowed underlying asset

Events:

  • BorrowEventV3 is emmited when transaction is success

Errors:

  • 770: Means your current obligation is locked and need to unstake first from borrow incentive

  • 0: Not owner of obligation

  • 1284: Means unable to borrow a collateral coin

  • 1282: Borrow to small

  • 1281: Borrow to much

  • 81924: Reserve not enough

Example:

module protocol::foo {

  use sui::coin::{Self, Coin};
  use sui::clock::{Self, Clock};
  use sui::tx_context::{Self ,TxContext};
  use sui::transfer;

  use scallop_protocol::borrow::borrow;
  use scallop_protocol::Market as ScallopMarket;
  use scallop_protocol::Version as ScallopVersion;
  use scallop_protocol::obligation::{Self, Obligation, ObligationKey};
  use scallop_protocol::coin_decimals_registry::CoinDecimalsRegistry;
  use scallop_protocol::x_oracle::XOracle;
	
  ...
	
	
  public fun doFoo<T>(
    version: &ScallopVersion,
    obligation: &mut Obligation,
    obligation_key: &ObligationKey,
    market: &mut ScallopMarket,
    coin_decimals_registry: &CoinDecimalsRegistry,
    borrow_amount: u64,
    x_oracle: &XOracle,
    clock: &Clock,
    ctx: &mut TxContext
  ) {
      let borrowed_coin = scallop_protocol::borrow::borrow(
	  scallop_version,
	  obligation_id,
	  obligation_key,
	  market,
	  coin_decimals_registry,
	  borrow_amount,
	  x_oracle,
	  clock,
	  ctx,
	);
		
      transfer::transfer(borrowed_coin, ctx.sender());
  }
}

Check here the example how to do update price x_oracle :

https://github.com/scallop-io/sui-scallop-sdk/blob/main/src/builders/oracle.ts

We recommend you to call this function using our Scallop SDK, so before calling any function related to borrow, you need to call update price in the beginning of the PTB (Programmable Transaction Block).

Here what your PTB should looks like:

PTB for calling function that have borrow:
- update price (you get this from SDK)
- your function that call borrow

Repay

Enabled user to repay underlying assets without requiring the ObligationKey object. Therefore, users can repay the borrowing obligation using any account, not just the obligation owner’s account.

public entry fun repay<T>(
  version: &Version,
  obligation: &mut Obligation,
  market: &mut Market,
  user_coin: Coin<T>,
  clock: &Clock,
  ctx: &mut TxContext,
) { }

Parameters:

Name
Type
Description

version

Version

The version control object, contract version must match with this

obligation

Obligation

The id of obligation, it should be shared object.

market

Market

The Scallop market object, it contains base assets, and related protocol configs

user_coin

Coin

The coin object used to repay borrowed underlying asset

clock

Clock

The SUI system clock object

Events:

  • RepayEventV3 is emmited when transaction is success

Errors:

  • 770: Means your current obligation is locked and need to unstake first from borrow incentive

Example:

module protocol::foo {

  use sui::coin::{Self, Coin};
  use sui::clock::{Self, Clock};
  use sui::tx_context::{Self ,TxContext};
  use sui::transfer;
  use scallop_protocol::repay::repay;
  use scallop_protocol::Market as ScallopMarket;
  use scallop_protocol::Version as ScallopVersion;
  use scallop_protocol::obligation::{Self, Obligation};
  use scallop_protocol::coin_decimals_registry::CoinDecimalsRegistry;

  public fun doFoo<T>(
    version: &ScallopVersion,
    obligation: &mut Obligation,
    market: &mut ScallopMarket,
    user_coin: Coin<T>,
    clock: &Clock,
    ctx: &mut TxContext
  ) {
      scallop_protocol::repay::repay(
	scallop_version,
        obligation_id,
	market,
	user_coin,
	clock,
	ctx,
      );
  }
}

Last updated