Spot Margin
How Nolus Spot Margin positions work on Solana, the contracts behind them, the position parameters, and how to find every contract address on chain.
Spot Margin lets a user open a leveraged position with a down payment and a loan from a Nolus lending pool. The position holds the real asset on the spot market rather than a derivative, and the rate on its loan stays fixed for the life of the position.
Every Nolus market runs on Solana today, so a position's assets live in an account on Solana and its swaps run through Metis. The loan, the accounting and the risk checks run in the protocol contracts on Nolus, which hold receipts for the tokens on Solana. The pages in this section give the contracts, the parameters and the exact messages that a developer needs to quote, open, query, repay and close a position.
Markets and protocols
Nolus runs one protocol per market, as the
protocol overview describes, and
each protocol is named SOLANA-METIS-<LPN>, where the LPN, the pool's native
currency, is the currency that its lending pool lends.
Whether a market is long or short depends on what its pool lends. A long
market borrows USDC from the SOLANA-METIS-USDC pool and buys the position
asset, so the position gains when the asset rises against USDC. A short
market borrows the shorted token from that token's own pool, for example
SOLANA-METIS-SOL, and holds USDC, so the position gains when the shorted
token falls against USDC. The app at app.nolus.io
shows the current markets and assets.
Contracts behind a position
Each protocol has its own set of contracts, and the table below gives the role that each of them plays in a position:
| Contract | Role in a position |
|---|---|
| Leaser | Quotes and opens positions, keeps the registry of each user's positions, holds the position parameters. |
| Lease | One contract per position. A state machine that holds the loan, tracks interest and LTV, and drives the position's account on Solana. |
| LPP | The lending pool. Lends the LPN to new positions at a fixed rate and receives repayments. |
| Oracle | Prices and price alarms that trigger LTV re-checks. |
| Remote lease controller | Sends the Lease's instructions to Solana over IBC. |
| Profit | Collects margin interest. Margin interest funds NLS buybacks and the Nolus referral program. |
| Reserve | Covers shortfalls when a closed or liquidated position does not cover its debt. |
The architecture page shows how these contracts fit together with the platform contracts and the off-chain services.
Loan-to-value and the position parameters
A position's health is measured by its loan-to-value, or LTV, which is the total amount due divided by the value of the position, with both sides in the pool currency. The amount due is the principal together with any due and overdue interest.
The Leaser holds the parameters that every new position is opened with. The
table gives an example value for each parameter and the field of
lease_config that holds it:
| Parameter | For example | Field in lease_config |
|---|---|---|
| Opening LTV | 80% (a position up to 5x the down payment, so the loan is up to 4x) | position_spec.liability.initial |
| Healthy LTV, the target after a partial liquidation | 83% | position_spec.liability.healthy |
| Warning levels | 85%, 86.5% and 88% | position_spec.liability.first_liq_warn, second_liq_warn, third_liq_warn |
| Liquidation LTV | 90% | position_spec.liability.max |
| Periodic LTV re-check, plus price alarms in between | every 5 days | position_spec.liability.recalc_time |
| Minimum position size | 15 USDC | position_spec.min_asset |
| Minimum transaction size | 0.5 USDC | position_spec.min_transaction |
| Interest due period | 30 days | due_period |
| Margin interest, on top of the loan rate | 4.5% a year | interest_rate_margin |
| Maximum slippage for open, repay and close | 7% | max_slippages.open, repay, close |
| Maximum slippage for liquidation | 10% | max_slippages.liquidation |
Check the live values with the Leaser's config query, which uses the
$LEASER variable set in
Find the contract addresses:
nolusd q wasm contract-state smart $LEASER '{"config":{}}' -o json | jq .data.config.lease_configThe loan rate comes from the pool's utilization curve instead, at the moment the position opens, and then stays fixed, as The loan rate explains.
As LTV crosses each warning level, the Lease emits a
wasm-ls-liquidation-warning event. When LTV reaches the liquidation level,
the position is partially liquidated back to the healthy LTV, or fully
liquidated when a partial liquidation would leave less than the minimum
position. Liquidations and risk describes the risk framework
in full.
Worked example
A short example shows how these levels play out as the price moves. Suppose a user opens a SOL long with a 100 USDC down payment and borrows 400 USDC, so that the position holds 500 USDC worth of SOL and opens at an LTV of 400 / 500 = 80%. Ignoring interest, a 10% fall in SOL leaves the position worth 450 USDC, which puts its LTV at 400 / 450 = 88.9%, past the third warning level. If SOL falls 12% instead, the position is worth 440 USDC and its LTV reaches 400 / 440 = 90.9%, past the liquidation LTV, so the Lease sells enough SOL to bring LTV back to 83%.
Position lifecycle
Every position moves through the states below, which are the states that the
Lease reports to its {"state":{}} query:
Repaying, a partial close and a partial liquidation all leave a position in
opened. Whatever a position owes the user when it ends is paid out
automatically, so the user never has a claim step to take.
Find the contract addresses
Every contract address can be discovered from the platform admin contract.
After you configure nolusd once, two queries to the admin contract list the
protocol names and resolve the contracts of one protocol:
nolusd config set client chain-id pirin-1
nolusd config set client node https://rpc.nolus.network:443
ADMIN=nolus1gurgpv8savnfw66lckwzn4zk7fp394lpe667dhu7aw48u40lj6jsqxf8nd
# list protocol names
nolusd q wasm contract-state smart $ADMIN '{"protocols":{}}'
# resolve one protocol
nolusd q wasm contract-state smart $ADMIN '{"protocol":"SOLANA-METIS-USDC"}' -o jsonADMIN holds the address of the admin contract, and the second query takes
one of the protocol names that the first query returns. Its answer gives the
network, the dex and the addresses of the protocol's contracts:
{
"data": {
"network": "Solana",
"dex": "Metis",
"contracts": {
"leaser": "nolus1...",
"lpp": "nolus1...",
"oracle": "nolus1...",
"profit": "nolus1...",
"remote_lease": "nolus1...",
"reserve": "nolus1..."
}
}
}Each nolus1... value stands for a full contract address. The commands on
the following pages read the Leaser, pool and oracle addresses from shell
variables, which you can set from the same query:
P=$(nolusd q wasm contract-state smart $ADMIN '{"protocol":"SOLANA-METIS-USDC"}' -o json)
LEASER=$(echo "$P" | jq -r .data.contracts.leaser)
LPP=$(echo "$P" | jq -r .data.contracts.lpp)
ORACLE=$(echo "$P" | jq -r .data.contracts.oracle)The messages on those pages follow the same conventions for values on the
wire. Amounts are integers in the currency's smallest unit, rates and LTV
levels are in permille, where 800 stands for 80%, and durations are in
nanoseconds. Funds attached to a transaction use the currency's bank denom,
which the oracle's {"currencies":{}} query maps from each ticker as
bank_symbol, together with its decimal_digits. To print a template for
every query and execute message of every Nolus contract, run
nolusd contract-help.
Walk through the flow
The pages that follow take a position through its life, from the quote to the close, with the messages for each step.
Quoting a position
Preview the position size, the loan and both interest rates before opening.
Opening a position
The open_lease message, the loan rate, and the Solana side of the position.
Querying a position
Find a user's positions, read the state and every returned field.
Repaying and closing
Repay, full and partial close, take profit and stop loss, liquidation.
Solray
Solray, the Nolus program on Solana. How tokens stay on Solana while Nolus holds receipts, how positions run there, and the program ID, IDL and instructions.
Quoting a position
Preview a Spot Margin position with the Leaser quote query, including position size, loan amount, loan rate and margin rate, before opening it.