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.
Solray is the Nolus program on Solana and part of the Nolus protocol. Using IBC channels and architecture, it activates the Nolus money market on Solana: users' tokens stay on Solana while Nolus works with receipts for them, and the assets of every position are held and swapped on Solana too.
Wallets, explorers, indexers and other integrations that work with Nolus balances and Solana tokens will find the program ID, the IDL and the instruction groups in the reference at the end of this page.
IBC in brief
Solray builds on IBC, so a short look at how IBC moves packets makes the rest of this page easier to follow. Inter-Blockchain Communication (IBC) is the standard protocol that Cosmos chains use to exchange packets, which relayers carry from one chain to the other. Relayers between Nolus and Solana are operated by the Nolus team.
Nolus uses IBC channels and architecture for one purpose, which is to run its money market on Solana through Solray. Solray therefore connects the Nolus chain and Solana only, and it is not a bridge between networks.
Each packet carries a token transfer or a position instruction and goes through three steps:
- The sending chain emits the packet and records a commitment to it.
- A relayer delivers the packet to the other chain, which executes it and writes an acknowledgement of success or error.
- The relayer carries the acknowledgement back to the sending chain. If the packet is not delivered before its timeout, the relayer delivers a timeout instead and the sending side reverts, which for a token transfer means that the sender is refunded.
Applications
Solray runs as one native Solana program that holds the IBC core state for the client, the connection and the channels. Two applications run on top of that state, one for tokens and one for positions.
Token transfer
The token transfer application implements ICS-20, and it is what keeps tokens
on Solana while Nolus holds receipts for them. When a Solana token is locked in
the program's escrow, a matching receipt, an ibc/ denom, is credited on
Nolus, and redeeming that receipt on Nolus releases the token on Solana again.
NLS works the other way round because it is native to Nolus. It stays locked on Nolus while the program issues a matching SPL token on Solana, which is minted when NLS arrives and burned when it is redeemed.
Remote Lease
The Remote Lease application runs the Solana side of Nolus positions. For each position it opens a dedicated account, runs the swaps, returns tokens to the escrow so that Nolus credits receipts for them, and finally closes the account.
User flow
A user works entirely from a Solana wallet in the app at app.nolus.io and never needs a Cosmos wallet, although every transaction on Solana still needs SOL in the user's wallet for fees. A round trip through the protocol looks like this:
- The user connects Phantom or Solflare, and because the user's Nolus account is derived from the same Solana key, the Solana wallet also signs Nolus transactions.
- The user locks tokens in Solray's escrow on Solana, and their Nolus account
is credited with a receipt, an
ibc/denom. NLS held on Solana takes the reverse path, since the SPL token is burned and the NLS locked on Nolus is released to the account. - With receipts in their Nolus account, the user opens, repays and closes positions and lends, and any proceeds are credited to the account as receipts.
- To take tokens back to Solana, the user redeems receipts from the Nolus account, and Solray releases the tokens from escrow to their Solana wallet.
A redemption needs the recipient token account to exist on Solana already. Because the program never creates that account, the app creates it first in a separate signature, and if the account is missing, the redemption fails and the receipts are returned to the Nolus account.
Redemptions to Solana made in the app time out if they are not delivered within 172,800 Solana slots, which is about 10 hours at today's slot rate. A timed-out redemption returns the receipts to the user's Nolus account.
Positions on Solana
Every position has a contract on Nolus and an account on Solana, and the two work together through Solray. The position's assets live in a per-position account of the Remote Lease application, which the Lease contract on Nolus controls over IBC through its protocol's remote lease controller contract. The Lease holds receipts on Nolus and redeems them into that account whenever the position needs the tokens. Swaps run on Solana through Metis, which is Jupiter's routing engine, and they execute through the Jupiter program.
Opening a position starts on Nolus and finishes on Solana. After the user
sends open_lease with the down payment to the Leaser, the Leaser creates the
Lease contract, which borrows from the lending pool. The Lease then asks
Solray to open its account on Solana with the Solana-side open_lease
instruction. Once the acknowledgement returns the account, the Lease
redeems its receipts for the down payment and the loan so that Solray releases
those tokens from escrow into the account. One swap through Metis then buys
the position asset, and the position is open.
Repaying, closing and liquidating reuse the same building blocks. When a swap
needs tokens, the Lease redeems receipts into the Solana account and asks
Solray to swap them with swap or swap_two. The Lease then asks Solray to
return the proceeds to the escrow with transfer_out, after which Nolus
credits receipts for them to the Lease. Once a position is fully settled,
close_lease tears down its Solana account. The messages a user sends on
Nolus, and every step of the lifecycle, are covered in
Opening a position.
Reference
This reference describes the deployed program, its IDL and instructions, and how its receipts appear on Nolus.
Deployment
| Item | Value |
|---|---|
| Program ID (Solana mainnet) | 9ATDq9dpDcM517hncjHnbWsBXzsNxKvqdfJ4L5qHJYy2 |
| Paired Nolus chain | pirin-1 |
| Release | v0.4.1 |
| IDL | https://docs.nolus.io/idl/solray.json |
| Security metadata | https://docs.nolus.io/security/solray.json |
| Security contact | [email protected], policy at Security |
The program's upgrade authority is recorded in its ProgramData account, and the program also embeds a security.txt that names [email protected] and the security policy page.
IDL
The IDL is a hand-written, Anchor-style JSON file pinned to the deployed wire
format. For each instruction it gives the discriminator, the argument layout
and the ordered account list, and it also describes the decodable account
layouts, the error codes and the PDA seed constants. Its top-level docs
array carries the decoding rules, which come down to four points:
- Instruction data is
[domain: u8][variant: u8][payload], where the two leading bytes form the instruction's discriminator and the payload is the Borsh serialization of the arguments in the listed order. - Accounts owned by the program are PDAs under the program ID and carry no discriminator, so identify an account by deriving its address from the seed constants, never by a data prefix.
- Errors come back as one of six classes, with codes 72 to 77, and the precise
cause is in the program log, on a line that starts with
Solray instruction processing has ended with error:. - The swap instructions,
swapandswap_two, forward a variable tail of swap-route accounts after the fixed account list, and that tail is not part of the IDL.
Instruction groups
The first discriminator byte, the domain, groups the IDL's 38 instructions:
| Domain | Group | Count | Instructions | Purpose |
|---|---|---|---|---|
| 0 | Config | 4 | new_source_mint, new_sink_mint, create_mint_metadata, update_mint_metadata | Register a Solana token or a Nolus token on the transfer channel, and publish token metadata for the SPL mints the program issues. |
| 1 | Core | 18 | create_client, close_client, open_try_connection, open_confirm_connection, close_connection, open_try_channel, open_confirm_channel, close_confirm_channel, finalize_channel, purge_stale_entries, close_escrow, close_remote_token_mint, create_statics_table, extend_statics_table, close_deactivated_table, and program administration instructions | IBC client, connection and channel handshakes and teardown, retiring tokens, address lookup tables and program administration. |
| 2 | Program | 1 | new_instance | Create the program instance. |
| 3 | Transfer | 10 | send_source_tokens, send_sink_tokens, receive_source_tokens, receive_sink_tokens, acknowledge_source_tokens, acknowledge_sink_tokens, timeout_source_tokens, timeout_sink_tokens, timeout_on_close_source_tokens, timeout_on_close_sink_tokens | ICS-20 token transfer. |
| 4 | Remote Lease | 5 | open_lease, swap, swap_two, transfer_out, close_lease | Packets from a Nolus Lease contract. |
A few entries in the table need more explanation. In the Core group, the address
lookup tables keep packet transactions within Solana's size limit, and two of
its instructions, purge_stale_entries and close_deactivated_table, are
permissionless cranks. The first prunes expired packet receipts and
acknowledgements, and the second reclaims the rent of retired lookup tables.
In the Transfer group, a source token is a Solana token held in escrow and a
sink token is a Nolus token minted on Solana. The user signs the two send_*
instructions, while relayers deliver the others.
Relayers also deliver all five Remote Lease instructions, which open the position account, swap one or two inputs through the Jupiter program, return tokens to the escrow so that Nolus credits receipts for them, and close the account.
Token denoms on Nolus
The receipt for a Solana token held in Solray's escrow appears on Nolus as an
ibc/ denom, which is ibc/ followed by the uppercase hex SHA-256 of the
token's IBC transfer path, transfer/<Nolus channel>/<mint address>. Because
the denom is a hash, resolving it back to its Solana mint takes a query on the
Nolus LCD:
curl -s https://lcd.nolus.network/ibc/apps/transfer/v1/denoms/<hash>Replace <hash> with the part of the denom after ibc/. In the response,
denom.base is the Solana mint address and denom.trace is the transfer
path, including the port and channel. For the tokens that are live today, see
the app at app.nolus.io.
Architecture
The contracts behind each Nolus protocol and the platform, how to discover their addresses, the off-chain services, and how positions run on Solana.
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.