Collateral
Equillar supports an optional collateral path that can be used to settle positions when needed.
Adding collateral
Collateral is added through the function:
add_collateral(collateral_token_addr, collateral_token_amount, collateral_token_symbol, collateral_addr)
This collateral serves as a guarantee of payment in the event that the company fails to meet its payment obligations.
stellar contract invoke \
--id <YOUR_CONTRACT_ID> \
-s admin \
--network testnet \
-- \
add_collateral \
--collateral_token_addr "C...." \
--collateral_token_amount "4000000000" \
--collateral_token_symbol "ABC" \
--collateral_addr "G..."
Let's analyze the parameters one by one:
- collateral_token_addr: It is the contract address of the token that is going to be used as collateral.
- collateral_token_amount: It is the amount of collateral deposited in units of the
collateral_token_addr. - collateral_token_symbol: The token code.
- collateral_addr: The address that holds the collateral, from which the funds will be sent to the contract.
Adding collateral is allowed only if all of the following conditions are met:
companyrole only- contract not paused
- the source address has enough collateral token balance
- only one collateral token type is used for the instance
Calculating the collateral level
Collateral valuation depends on the price_oracle configured at deployment time (see Deploying the contract).
The contract calculates a collateral level by:
- If there are no remaining payment obligations, the collateral level is
10_000(100%) by definition — no further calculation is needed. - Fetch the collateral token's price and the contract token's price from the oracle, both expressed in the oracle's base asset.
- Compute the cross price (collateral / contract token) from those two prices, both at the oracle's decimal scale.
- Convert the collateral amount into the contract token denomination by multiplying it by the cross price.
- Divide the resulting collateral value by the remaining payment obligations, scaling by
10_000so the result is expressed in basis points (e.g.6_000= 60% coverage,10_000= 100% coverage).
If the valuation cannot be established or is insufficient, the operation fails.
For the oracle to be compatible with Equillar, it must implement the following trait:
#[contracttype]
#[derive(Clone)]
pub enum Asset {
Stellar(Address),
Other(Symbol),
}
#[contracttype]
#[derive(Clone)]
pub struct PriceData {
pub price: i128,
pub timestamp: u64,
}
#[contractclient(name = "ReflectorClient")]
pub trait ReflectorOracle {
fn base(env: &Env) -> Asset;
fn decimals(env: &Env) -> u32;
fn lastprice(env: &Env, asset: Asset) -> Option<PriceData>;
}
Reflector is a compatible oracle and has been used as the reference implementation during development. However, any oracle contract that implements this trait can be used by the integrator.
Worked example
Assumptions:
- Contract token: USDC (7 decimals). Remaining payment obligations:
10000_0000000(10,000 USDC). - Collateral token: backed 1:1 by BTC (7 decimals). Held amount:
1_500_000token units (0.15 BTC). - Oracle prices (in the oracle's base asset, USD): BTC = 60,000, USDC = 1.
Step 1 — Prices from the oracle
- Collateral token price: 60,000 USD
- Contract token price: 1 USD
Step 2 — Cross price
cross_price = collateral_price / contract_token_price = 60,000 / 1 = 60,000
→ 1 unit of collateral token is worth 60,000 units of the contract token.
Step 3 — Collateral value in contract token terms
collateral_value = collateral_amount * cross_price = 0.15 * 60,000 = 9,000 USDC
Step 4 — Collateral level
collateral_level = (collateral_value / payment_obligations) * 10_000 = (9,000 / 10,000) * 10_000 = 9,000 (basis points)
This corresponds to a 90% coverage level — the collateral almost, but not fully, covers the outstanding payment obligations.
Payments with collateral
Liquidating positions using the collateral can be achieved by the function pay_with_collateral(token_id).
stellar contract invoke \
--id <YOUR_CONTRACT_ID> \
-s admin \
--network testnet \
-- \
pay_with_collateral \
--token_id 1234 \
This function transfers the amount of collateral corresponding to the position's address (operator role) based on the position's outstanding obligations relative to the total outstanding obligations. Let's analyze a working example in the next section.
Liquidating positions with collateral is allowed only if all of the following conditions are met:
adminrole only- Position is not completed
- There is collateral in the contract
- Contract is not paused
Worked example. Distributing collateral
We'll start with the data from the previous section:
- Contract token: USDC (7 decimals). Remaining payment obligations:
10000_0000000(10,000 USDC). - Collateral token: backed 1:1 by BTC (7 decimals). Held amount:
1_500_000token units (0.15 BTC). - Oracle prices (in the oracle's base asset, USD): BTC = 60,000, USDC = 1.
We also assume the following additional data:
- The
10,000USDC in total obligations is spread across three positions. Only the first two are liquidated in this walkthrough:- Position 1: 3,000 USDC in outstanding obligations
- Position 2: 4,000 USDC in outstanding obligations
- Position 3: 3,000 USDC in outstanding obligations
Checking whether the collateral needs to be capped
Before distributing, the contract checks whether the collateral it holds is worth more than what's currently owed:
collateral_value = collateral_held * cross_price = 0.15 * 60,000 = 9,000 USDC
remaining_obligations = 10,000 USDC
Since 9,000 < 10,000, the collateral is not capped — there isn't more collateral than debt, so the full 0.15 BTC held remains available for proportional distribution.
Liquidating position 1
- Position 1's share of the total obligations:
3,000 / 10,000 = 30% - Collateral transferred:
0.15 * 0.30 = 0.045BTC - We transfer
0.045collateral tokens to position 1's address
Liquidating position 2
- Remaining collateral after position 1:
0.15 - 0.045 = 0.105BTC - Remaining obligations after position 1:
10,000 - 3,000 = 7,000USDC - The contract re-checks the cap:
collateral_value = 0.105 * 60,000 = 6,300, still less than7,000— no cap applied - Position 2's share of the remaining obligations:
4,000 / 7,000 ≈ 57.14% - Collateral transferred:
0.105 * 0.5714 = 0.06BTC - We transfer
0.06collateral tokens to position 2's address
After these two payments, 0.15 - 0.045 - 0.06 = 0.045 BTC remain in the contract, and 3,000 USDC in obligations are still outstanding (position 3), which would be settled the same way in a subsequent call.
Worked example. Collateral in excess of obligations
Now let's look at the opposite scenario: the collateral held is worth more than what's owed, so the cap described above becomes relevant.
Assumptions:
- Collateral held: 1 BTC (7 decimals).
- Oracle price: BTC = 60,000 USDC.
- Three positions to settle, in this order:
- Position A: 5,000 USDC in outstanding obligations
- Position B: 6,000 USDC in outstanding obligations
- Position C: 7,000 USDC in outstanding obligations
- Total obligations:
5,000 + 6,000 + 7,000 = 18,000USDC
Liquidating position A
- Collateral check:
collateral_value = 1 * 60,000 = 60,000, which is greater than the18,000owed — the cap applies. - Capped collateral amount:
required_collateral = remaining_obligations / cross_price = 18,000 / 60,000 = 0.3BTC - Position A's share of the total obligations:
5,000 / 18,000 ≈ 27.78% - Collateral transferred:
0.3 * 0.2778 ≈ 0.0833BTC — worth exactly5,000USDC, matching A's debt precisely.
Liquidating position B
- Remaining collateral:
1 - 0.0833 = 0.9167BTC. Remaining obligations:18,000 - 5,000 = 13,000USDC. - Collateral check:
collateral_value = 0.9167 * 60,000 ≈ 55,000, still greater than13,000— the cap applies again. - Capped collateral amount:
required_collateral = 13,000 / 60,000 ≈ 0.2167BTC - Position B's share of the remaining obligations:
6,000 / 13,000 ≈ 46.15% - Collateral transferred:
0.2167 * 0.4615 ≈ 0.1BTC — worth exactly6,000USDC, matching B's debt precisely.
Liquidating position C
- Remaining collateral:
0.9167 - 0.1 = 0.8167BTC. Remaining obligations:13,000 - 6,000 = 7,000USDC. - Collateral check:
collateral_value = 0.8167 * 60,000 ≈ 49,000, still greater than7,000— the cap applies once more. - Capped collateral amount:
required_collateral = 7,000 / 60,000 ≈ 0.1167BTC - Position C's share of the remaining obligations:
7,000 / 7,000 = 100% - Collateral transferred:
0.1167 * 1 = 0.1167BTC — worth exactly7,000USDC, matching C's debt precisely.
Outcome
Every position receives collateral worth exactly what it was owed — no more, no less — even though the contract held far more collateral than necessary. In total, 0.0833 + 0.1 + 0.1167 = 0.3 BTC were liquidated (worth 18,000 USDC, matching total obligations exactly), leaving 1 - 0.3 = 0.7 BTC of excess collateral still held by the contract. That excess can later be recovered by the company through return_collateral_to_company.
Returning collateral to the company
If the company added collateral to the contract but it was never needed, and all payment obligations have been successfully settled, the full collateral amount can be returned to the company address by calling the return_collateral_to_company function.
stellar contract invoke \
--id <YOUR_CONTRACT_ID> \
-s admin \
--network testnet \
-- \
return_collateral_to_company
Calling this function transfers the entire collateral balance back to the company.
Events and balance after a collateral deposit
When collateral is added to the contract through add_collateral, the balance is updated via recalculate_from_collateral_received:
collateral_receivedis increased by the deposited amount
Note: the balance update and event emission only happen if the collateral level can be successfully calculated — for example, if the oracle call fails, neither the balance nor the event are updated/emitted.
This update is published through the standard ContractBalanceUpdated event (see Fundraising and positions section).
In addition, the contract emits a dedicated event with the deposit details:
CollateralDeposited:
CollateralDeposited {
addr, // the address of the contract
current_collateral_amount, // the total collateral held by the contract, including this deposit
total_deposited, // the amount transferred in this specific call
token_address, // the address of the collateral token
token_symbol, // the symbol of the collateral token
}
Reading this event with stellar-cli
stellar events \
--id <YOUR_CONTRACT_ID> \
--network testnet \
--start-ledger <LEDGER_NUMBER> \
--type contract \
--topic "CollateralDeposited,*"
Events and balance after collateral liquidation
When collateral is liquidated to cover payment obligations through pay_with_collateral, the balance is updated via recalculate_from_collateral_liquidated:
collateral_liquidatedis increased by the liquidated amountpayment_obligationsis reduced by the remaining obligations covered by this liquidation
This update is published through the standard ContractBalanceUpdated event (see Fundraising and positions section).
In addition, the contract emits a dedicated event with the payment details:
CollateralSent:
CollateralSent {
addr, // the address of the contract
to, // the address receiving the liquidated collateral
total_sent, // the total amount sent
}
Reading this event with stellar-cli
stellar events \
--id <YOUR_CONTRACT_ID> \
--network testnet \
--start-ledger <LEDGER_NUMBER> \
--type contract \
--topic "CollateralSent,*"
Events and balance after returning collateral to the company
When the full collateral amount is returned to the company through return_collateral_to_company, the balance is updated via recalculate_from_collateral_returned:
collateral_returnedis increased by the returned amount
This update is published through the standard ContractBalanceUpdated event (see Fundraising and positions section above).
In addition, the contract emits a dedicated event with the return details:
CollateralReturned:
CollateralReturned {
addr, // the address of the contract
to, // the company address receiving the collateral
total_returned, // the total amount returned
}
Reading this event with stellar-cli
stellar events \
--id <YOUR_CONTRACT_ID> \
--network testnet \
--start-ledger <LEDGER_NUMBER> \
--type contract \
--topic "CollateralReturned,*"