Nolus Docs

Opening a position

Send open_lease to open a Spot Margin position, see how the fixed loan rate is set, and follow the Lease as it builds its account on Solana.

Opening a position takes one transaction from the user, an open_lease message to the protocol's Leaser with the down payment attached, and everything after that runs between contracts. The Leaser creates a Lease contract for the position, and the Lease borrows from the pool at a fixed rate and opens an account for the position on Solana through Solray, the Nolus program on Solana. The Lease then redeems its receipts so that the tokens are released into that account and buys the position asset through Metis, and the position is open once that swap settles.

Send the open message

The open_lease message names the position asset and can cap the size of the loan:

{ "open_lease": { "currency": "SOL", "max_ltd": 2000 } }

The message has two fields, and the down payment travels with it as funds:

FieldMeaning
currencyTicker of the position asset. For short markets this is USDC.
max_ltdOptional loan-to-down-payment cap in permille (2000 borrows at most 2x the down payment). Omit it for the maximum leverage.
fundsThe down payment, attached as funds in its bank denom. It can be in the pool currency, in a position asset of the protocol, or in NLS.

To send the message from the command line, resolve $LEASER and $ORACLE as shown in Find the contract addresses, then look up the bank denom of the down payment currency and execute the message:

# bank denom of the down payment currency
USDC_DENOM=$(nolusd q wasm contract-state smart $ORACLE '{"currencies":{}}' -o json \
  | jq -r '.data[] | select(.ticker=="USDC") | .bank_symbol')

# open a SOL long with 100 USDC down at maximum leverage
nolusd tx wasm execute $LEASER '{"open_lease":{"currency":"SOL"}}' \
  --amount 100000000$USDC_DENOM \
  --from <key> --gas auto --gas-adjustment 1.3 --gas-prices 0.025unls

Replace <key> with the key that signs the transaction. The amount 100000000 is 100 USDC in its smallest unit, and because the message leaves out max_ltd, the position opens at maximum leverage.

The app builds the same message for the user's Solana wallet to sign, and the app's REST API returns it as an unsigned transaction. Because users hold their tokens on Solana, they lock tokens in Solray's escrow before opening a position and receive receipts for them in their Nolus account, as User flow describes.

How much is borrowed

The size of the loan follows from the down payment and the opening LTV:

default loan-to-down-payment = initial_ltv / (1 - initial_ltv)
loan = down payment x min(max_ltd, default loan-to-down-payment)

Here initial_ltv is the opening LTV from the Leaser's configuration and max_ltd is the optional cap from the message. With an opening LTV of, for example, 80%, the default ratio is 4, so a 100 USDC down payment borrows 400 USDC and opens a 5x position worth 500 USDC. With max_ltd: 2000, the same down payment borrows 200 USDC.

Before it opens the position, the Leaser also checks the down payment and the loan against the protocol's minimum sizes, as Limits checked at opening describes.

The loan rate

The pool sets the loan rate from its utilization at the moment the position opens, using the curve below, and the rate then stays fixed for the life of the loan:

rate = base + addon / optimal x min(liability / idle, optimal / (1 - optimal))

In the formula, liability / idle is the pool's outstanding loans divided by its idle balance, which measures utilization. The base term is the minimum rate, while optimal is the target utilization and addon sets the slope. Because the min(...) caps the utilization term at the optimal utilization, the rate stops rising once the pool is past it.

For example, with a base of 3%, an optimal utilization of 70% and an addon of 2%, the curve gives these rates:

Utilizationliability / idleLoan rate
50%1.03% + 2% / 0.7 x 1.0 = 5.86%
60%1.53% + 2% / 0.7 x 1.5 = 7.29%
70% or morecapped at 0.7 / 0.3 = 2.333% + 2% / 0.7 x 2.33 = 9.67%

Margin interest is added on top of the loan rate at the annual rate set in the position parameters. To check the live values, query the pool's curve and the Leaser's margin rate:

nolusd q wasm contract-state smart $LPP '{"config":[]}' -o json
# {"data":{"borrow_rate":{"base_interest_rate":30,"utilization_optimal":700,"addon_optimal_interest_rate":20},...}}

nolusd q wasm contract-state smart $LEASER '{"config":{}}' -o json \
  | jq .data.config.lease_config.interest_rate_margin

The comment under the first query shows the shape of the pool's answer, in which the curve values are in permille, so a base_interest_rate of 30 is 3%.

Where the position lives

A position's assets live on Solana, in a per-position account of the Remote Lease app inside Solray, and the Lease contract on Nolus is the only controller of that account. The Lease sends its instructions over IBC through the protocol's remote lease controller, so the account opens, swaps, returns tokens to the escrow and closes only when the Lease asks it to. Positions on Solana describes how receipts, swaps and the account work together on the Solana side.

The opening flow

The diagram and the steps below follow an open from the user's message to the opened position:

  1. The user sends open_lease with the down payment, and the Leaser checks the limits and creates a Lease contract for the position. Only the Leaser can create a Lease, and the pool lends only to Leases it recognizes.
  2. The Lease borrows the loan from the LPP at the current rate, which is now fixed for this loan.
  3. Through its protocol's remote lease controller, the Lease sends an IBC packet to open the position's account on Solana. Solray's Remote Lease app creates the account, and the acknowledgement returns it to the Lease.
  4. The Lease redeems its receipts for the down payment and the loan, and Solray releases the tokens from escrow into that account.
  5. One swap through Metis buys the position asset. The swap accepts no less than the oracle price minus the open slippage limit, and it is retried until it fills.
  6. The Lease records the position and enters opened, and from then on it tracks interest and LTV with a periodic re-check and price alarms.

While the open runs, the Lease's {"state":{}} query returns opening, and its in_progress field moves from open_lease to transfer_out and then to buy_asset. Querying a position explains how to read the state.

If the account cannot be opened

If the request to open the account on Solana fails or times out, the Lease has not yet redeemed any receipts, so it unwinds the open on Nolus. It refunds the whole down payment to the user, closes the loan with the pool and ends in the terminal state open_failed, which carries the reason. Funding the account and the swap later in the flow never fail the open, because both are retried.

Positions are fixed at opening

Each Lease stores the loan rate, the margin rate, the LTV levels, the minimums and the due period when it opens, so a later change to the Leaser's configuration applies only to positions opened after that change. The maximum slippage limits are the exception, since the Lease reads them from the Leaser each time it starts an open, repay, close or liquidation swap.

The next page, Querying a position, shows how to find a user's positions and read their state.

On this page