Liquid Staking on NEAR Protocol
LiNEAR - Liquid Staking on NEAR Protocol
LiNEAR Protocol is a liquid staking solution built on the NEAR Protocol. LiNEAR unlocks liquidity of the staked NEAR by creating a staking derivative to be engaged with various DeFi protocols on NEAR and Aurora, while also enjoying over 10% APY staking rewards of the underlying base tokens. LiNEAR is the cornerstone piece of the NEAR-Aurora DeFi ecosystem.
Contracts
The LiNEAR smart contracts are implemented with NEAR Rust SDK. The core contract is located in contracts/linear, and several mock contracts were made for testing various scenarios via simulation test.
The LiNEAR smart contracts have been audited by BlockSec and Hacken. According to BlockSec's audit report and Hacken's audit report, no critical issues were found, and few low-risk minor issues were reported and have been fixed.
Deployment
Mainnet
- LiNEAR contract and $LiNEAR token:
linear-protocol.near
Testnet
- LiNEAR contract and $LiNEAR token:
linear-protocol.testnet
Development
Prerequisite
- Rust: v1.69.0
- Node.js: v20
Build
- Build all artifacts:
make - Build linear only:
make linear
Test
We adopt unit tests and heavily used the workspace-js test framework to test the major scenarios and workflow of the LiNEAR smart contract in the Sandbox environment. Lint with rustfmt and clippy is also required when making changes to contract.
- Install Node.js v20
- Run
npm ito set up the environment - Run lint with
rustfmtandclippy:make lint - Run all tests:
make test - Run LiNEAR simulation tests:
make test-linear
- Run specific test file: TEST_FILE={filename} make test-linear
- Print contract logs: NO_LOGS=false make test-linear
Deploy
A CLI tool is made to help deploy and manage the contract. In order to use it, run npm i first.
All CLI arguments could be passed as environment variables as well, with LI prefix.
- deploy contract:
./bin/cli.js deploy linear.testnet
- init contract:
./bin/cli.js init linear.testnet --signer owner.testnet --owner_id owner.testnet
Release
- Checkout a release branch from main
git checkout -b release/v1.0.x
- Make sure all tests pass:
make test
- Update version in
contracts/linear/Cargo.toml - Git commit version update and push the release branch:
git commit -m 'v1.0.x'
- git push origin release/v1.0.x
- Create a PR from the release branch
- Once the PR is merged, publish a new release on GitHub
Manage
Drain
Drain is to totally remove a validator from candidate list, all funds on it will be re-distributed among others.Drain is a manager operation. Before starting it, make sure the local validator accounting has been synced with the validator staking pool so the drain amount is selected from the latest balances.
- Stop assigning new stake to the validator:
0, which can be done by either removing this
validator from nodes list or setting its weight to 0 directly. Run
set-node command to update the weight.
- Set validator base stake amount to 0. Run set-node-base-amounts command
to update the base stake amount.
- Sync the validator balance before drain:
epochupdaterewards for the validator and wait for the callback to
succeed. This records any staking pool rewards before the drain amount is
selected.
- Call syncbalancefrom_validator for the validator and wait for the
callback to succeed. This aligns local stakedamount and unstakedamount
with the staking pool account.
- If either callback fails, retry and do not continue to drain-unstake.
- Settle unstaked balance before drain:
getvalidator shows unstakedamount greater than or equal to 1 NEAR
(1000000000000000000000000 yoctoNEAR), call epoch_withdraw
and wait for the callback to succeed. The drain guard only tolerates less
than 1 NEAR as staking-pool precision dust.
- After epochwithdraw, call syncbalancefromvalidator again and wait
for the callback to succeed.
- Verify
get_validatorbeforedrain-unstake:
weight is 0.
- basestakeamount is 0.
- pending_release is false.
- unstaked_amount is less than 1 NEAR
(1000000000000000000000000 yoctoNEAR).
- draining is false.
- Run
drain-unstaketo unstake all funds from the validator. - After 4 full epochs have passed since the
drain-unstaketransaction
get_validator again:
- weight is 0.
- basestakeamount is 0.
- staked_amount is 0.
- pending_release is false.
- draining is true.
- Run
drain-withdrawto withdraw the drained funds. The callback adds the
Design
Terminologies
totalshareamount: Total amount of LiNEAR that was minted (minus burned).totalstakednear_amount: Total amount of NEAR that was staked by users to this contract.
stakeshareprice: how much NEAR does one stake share (LiNEAR) worth. equals to :totalnearamount/totalshareamount.
Information for Developers
More useful information for developers who're building with LiNEAR
LiNEAR App URLs
- mainnet: https://app.linearprotocol.org
- testnet: https://testnet.linearprotocol.org
LiNEAR contract addresses on NEAR:
- mainnet:
linear-protocol.near - testnet:
linear-protocol.testnet
$LiNEAR ERC20 contracts on Aurora (bridged via Rainbow Bridge)
- mainnet:
0x918dbe087040a41b786f0da83190c293dae24749 - testnet:
0xe4979cac5d70f01697f795f0ad56bbca05912c44
$LiNEAR Oracles
Oracles on Aurora mainnet:
- LINEAR / NEAR (Flux):
0x8f975aC6deFD2c9d50c58BABF4B1f880E6dE7996 - LINEAR / USD (Flux):
0x2eBf49106814Fcd8685ed6c8a7315Ca528CdA232
- LINEAR / NEAR (Flux):
NEARENV=mainnet near view fpo.opfilabs.near getentry '{"pair": "LINEAR/NEAR", "provider": "opfilabs.near"}' - LINEAR / USD (price oracle):
NEARENV=mainnet near view priceoracle.near getpricedata '{"assetids": ["linear-protocol.near"]}'