Skip to content

Commit 5f35c76

Browse files
author
Edward (OpenClaw)
committed
docs+code: rename "rent" to "lease fee" throughout asset-leasing
"Rent" collides with Solana's account-rent concept. Switching to "lease fee" makes the per-second payment from lessee to lessor unambiguous. Also tightens the README: - Drops the "this README is a teaching document" / "It is not a deployed, audited production program" / "treat it as a learning example" framing — true of every program in program-examples by definition. - Drops the disambiguation disclaimer about Solana account rent vs lease rent (no longer needed once the per-second payment is named the lease fee). - Drops fungibility caveats around what the lessee returns — just says they return leased_amount of leased_mint. - Switches "neutral escrow" to "non-custodial escrow". - Adds a concrete xNVDA / USDC worked example with both rallying- and falling-NVIDIA scenarios so the asymmetric-payoff (short-like) shape of the lessee position is explicit. - Adds §4.3 (falling-price path) covering the case where the leased asset depreciates and the lessee benefits. Code rename details: - Field: Lease.rent_per_second -> Lease.lease_fee_per_second - Field: Lease.last_rent_paid_ts -> Lease.last_paid_ts - Function: compute_rent_due -> compute_lease_fee_due - Instruction: pay_rent -> pay_lease_fee (module / handler / Accounts struct / source filename) - Error: InvalidRentPerSecond -> InvalidLeaseFeePerSecond - Local variables and code comments updated to match. - Solana terminology kept verbatim where it really means account rent (rent-exempt lamports, Sysvar<Rent>, sysvar::rent::ID, rent_epoch, etc.). The Quasar port is renamed in lockstep so the two implementations stay byte-for-byte identical at the IDL level (same discriminators, same Lease layout). Quasar's tests are renamed to match. All 11 LiteSVM tests pass after the rename. Quasar `quasar build` also succeeds.
1 parent 709542e commit 5f35c76

23 files changed

Lines changed: 421 additions & 314 deletions

defi/asset-leasing/anchor/README.md

Lines changed: 218 additions & 111 deletions
Large diffs are not rendered by default.

defi/asset-leasing/anchor/programs/asset-leasing/src/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ pub enum AssetLeasingError {
1010
InvalidLeasedAmount,
1111
#[msg("Required collateral amount must be greater than zero")]
1212
InvalidCollateralAmount,
13-
#[msg("Rent per second must be greater than zero")]
14-
InvalidRentPerSecond,
13+
#[msg("Lease fee per second must be greater than zero")]
14+
InvalidLeaseFeePerSecond,
1515
#[msg("Maintenance margin is outside the allowed range")]
1616
InvalidMaintenanceMargin,
1717
#[msg("Liquidation bounty is outside the allowed range")]

defi/asset-leasing/anchor/programs/asset-leasing/src/instructions/close_expired.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
constants::{COLLATERAL_VAULT_SEED, LEASED_VAULT_SEED, LEASE_SEED},
99
errors::AssetLeasingError,
1010
instructions::{
11-
pay_rent::update_last_paid_ts,
11+
pay_lease_fee::update_last_paid_ts,
1212
shared::{close_vault, transfer_tokens_from_vault},
1313
},
1414
state::{Lease, LeaseStatus},
@@ -155,20 +155,20 @@ pub fn handle_close_expired(context: Context<CloseExpired>) -> Result<()> {
155155
&[collateral_vault_seeds],
156156
)?;
157157

158-
// Settle rent accounting on the default path.
158+
// Settle lease-fee accounting on the default path.
159159
//
160-
// We are not forwarding any accrued rent to the lessor here — on default
160+
// We are not forwarding any accrued lease fees to the lessor here — on default
161161
// the lessor takes the whole collateral vault as compensation — but we
162-
// still bump \`last_rent_paid_ts\` so the invariant
163-
// \`last_rent_paid_ts <= now.min(end_ts)\` stays intact. That matters for
162+
// still bump \`last_paid_ts\` so the invariant
163+
// \`last_paid_ts <= now.min(end_ts)\` stays intact. That matters for
164164
// any future version of the program that wants to split the collateral
165-
// differently (pro-rata rent, partial refund on default, haircut to the
165+
// differently (pro-rata lease fees, partial refund on default, haircut to the
166166
// lessee for unused time): such a version can read
167-
// \`last_rent_paid_ts\` and trust that everything up to \`now\` is already
167+
// \`last_paid_ts\` and trust that everything up to \`now\` is already
168168
// settled, rather than having to reason about whether this branch ever
169169
// bumped the timestamp.
170170
//
171-
// No-op on the \`Listed\` branch because rent never started accruing.
171+
// No-op on the \`Listed\` branch because Lease fees never started accruing.
172172
if status == LeaseStatus::Active {
173173
update_last_paid_ts(&mut context.accounts.lease, now);
174174
}

defi/asset-leasing/anchor/programs/asset-leasing/src/instructions/create_lease.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,15 @@ pub fn handle_create_lease(
7575
lease_id: u64,
7676
leased_amount: u64,
7777
required_collateral_amount: u64,
78-
rent_per_second: u64,
78+
lease_fee_per_second: u64,
7979
duration_seconds: i64,
8080
maintenance_margin_bps: u16,
8181
liquidation_bounty_bps: u16,
8282
feed_id: [u8; 32],
8383
) -> Result<()> {
8484
// Reject leased_mint == collateral_mint. Allowing both to be the same
8585
// mint would collapse the two vaults' seed derivations into one shared
86-
// token-balance pool, making rent-vs-collateral accounting ambiguous and
86+
// token-balance pool, making lease-fee-vs-collateral accounting ambiguous and
8787
// enabling griefing paths where the lessee's "collateral" is the same
8888
// asset they already hold as the lease principal.
8989
require!(
@@ -96,7 +96,7 @@ pub fn handle_create_lease(
9696
required_collateral_amount > 0,
9797
AssetLeasingError::InvalidCollateralAmount
9898
);
99-
require!(rent_per_second > 0, AssetLeasingError::InvalidRentPerSecond);
99+
require!(lease_fee_per_second > 0, AssetLeasingError::InvalidLeaseFeePerSecond);
100100
require!(duration_seconds > 0, AssetLeasingError::InvalidDuration);
101101
require!(
102102
maintenance_margin_bps > 0 && maintenance_margin_bps <= MAX_MAINTENANCE_MARGIN_BPS,
@@ -131,13 +131,13 @@ pub fn handle_create_lease(
131131
// No collateral yet — posted on take_lease.
132132
collateral_amount: 0,
133133
required_collateral_amount,
134-
rent_per_second,
134+
lease_fee_per_second,
135135
duration_seconds,
136-
// start_ts / end_ts / last_rent_paid_ts are set when the lease
136+
// start_ts / end_ts / last_paid_ts are set when the lease
137137
// activates in `take_lease`.
138138
start_ts: 0,
139139
end_ts: 0,
140-
last_rent_paid_ts: 0,
140+
last_paid_ts: 0,
141141
maintenance_margin_bps,
142142
liquidation_bounty_bps,
143143
feed_id,

defi/asset-leasing/anchor/programs/asset-leasing/src/instructions/liquidate.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
},
1212
errors::AssetLeasingError,
1313
instructions::{
14-
pay_rent::compute_rent_due,
14+
pay_lease_fee::compute_lease_fee_due,
1515
shared::{close_vault, transfer_tokens_from_vault},
1616
},
1717
state::{Lease, LeaseStatus},
@@ -34,7 +34,7 @@ pub struct Liquidate<'info> {
3434
#[account(mut)]
3535
pub keeper: Signer<'info>,
3636

37-
/// CHECK: PDA seed + rent/collateral destination.
37+
/// CHECK: PDA seed + lease-fee / collateral destination.
3838
#[account(mut)]
3939
pub lessor: UncheckedAccount<'info>,
4040

@@ -174,10 +174,10 @@ pub fn handle_liquidate(context: Context<Liquidate>) -> Result<()> {
174174
AssetLeasingError::PositionHealthy
175175
);
176176

177-
// Settle accrued rent first (up to end_ts) so the lessor is paid for the
177+
// Settle accrued lease fees first (up to end_ts) so the lessor is paid for the
178178
// time the lessee actually used. Only then slice off bounty + remainder.
179-
let rent_due = compute_rent_due(&context.accounts.lease, now)?;
180-
let rent_payable = rent_due.min(context.accounts.lease.collateral_amount);
179+
let lease_fee_due = compute_lease_fee_due(&context.accounts.lease, now)?;
180+
let lease_fee_payable = lease_fee_due.min(context.accounts.lease.collateral_amount);
181181

182182
let lease_key = context.accounts.lease.key();
183183
let collateral_vault_bump = context.accounts.lease.collateral_vault_bump;
@@ -193,11 +193,11 @@ pub fn handle_liquidate(context: Context<Liquidate>) -> Result<()> {
193193
core::slice::from_ref(&leased_vault_bump),
194194
];
195195

196-
if rent_payable > 0 {
196+
if lease_fee_payable > 0 {
197197
transfer_tokens_from_vault(
198198
&context.accounts.collateral_vault,
199199
&context.accounts.lessor_collateral_account,
200-
rent_payable,
200+
lease_fee_payable,
201201
&context.accounts.collateral_mint,
202202
&context.accounts.collateral_vault.to_account_info(),
203203
&context.accounts.token_program,
@@ -209,10 +209,10 @@ pub fn handle_liquidate(context: Context<Liquidate>) -> Result<()> {
209209
.accounts
210210
.lease
211211
.collateral_amount
212-
.checked_sub(rent_payable)
212+
.checked_sub(lease_fee_payable)
213213
.ok_or(AssetLeasingError::MathOverflow)?;
214214

215-
// Bounty is a percentage of the collateral *after* rent — guarantees we
215+
// Bounty is a percentage of the collateral *after* lease fees — guarantees we
216216
// never try to pay out more than what actually sits in the vault.
217217
let bounty = (remaining as u128)
218218
.checked_mul(context.accounts.lease.liquidation_bounty_bps as u128)
@@ -264,7 +264,7 @@ pub fn handle_liquidate(context: Context<Liquidate>) -> Result<()> {
264264
)?;
265265

266266
context.accounts.lease.collateral_amount = 0;
267-
context.accounts.lease.last_rent_paid_ts = now.min(context.accounts.lease.end_ts);
267+
context.accounts.lease.last_paid_ts = now.min(context.accounts.lease.end_ts);
268268
context.accounts.lease.status = LeaseStatus::Liquidated;
269269

270270
Ok(())

defi/asset-leasing/anchor/programs/asset-leasing/src/instructions/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub mod close_expired;
22
pub mod create_lease;
33
pub mod liquidate;
4-
pub mod pay_rent;
4+
pub mod pay_lease_fee;
55
pub mod return_lease;
66
pub mod shared;
77
pub mod take_lease;
@@ -10,7 +10,7 @@ pub mod top_up_collateral;
1010
pub use close_expired::*;
1111
pub use create_lease::*;
1212
pub use liquidate::*;
13-
pub use pay_rent::*;
13+
pub use pay_lease_fee::*;
1414
pub use return_lease::*;
1515
pub use shared::*;
1616
pub use take_lease::*;

defi/asset-leasing/anchor/programs/asset-leasing/src/instructions/pay_rent.rs renamed to defi/asset-leasing/anchor/programs/asset-leasing/src/instructions/pay_lease_fee.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ use crate::{
1212
};
1313

1414
#[derive(Accounts)]
15-
pub struct PayRent<'info> {
16-
/// Anyone may settle rent — the lessee has every incentive to keep the
17-
/// lease current, but a keeper bot could also push a payment before a
15+
pub struct PayLeaseFee<'info> {
16+
/// Anyone may settle the lease fee — the lessee has every incentive to keep the
17+
/// lease current, but a keeper bot could also push a lease fee payment before a
1818
/// liquidation check so healthy leases stay healthy.
1919
#[account(mut)]
2020
pub payer: Signer<'info>,
@@ -45,7 +45,7 @@ pub struct PayRent<'info> {
4545
pub collateral_vault: Box<InterfaceAccount<'info, TokenAccount>>,
4646

4747
/// Lessor's collateral-mint ATA, created on demand so the lessor does not
48-
/// need to pre-fund it with rent.
48+
/// need to pre-fund it with the lease fee.
4949
#[account(
5050
init_if_needed,
5151
payer = payer,
@@ -60,21 +60,21 @@ pub struct PayRent<'info> {
6060
pub system_program: Program<'info, System>,
6161
}
6262

63-
pub fn handle_pay_rent(context: Context<PayRent>) -> Result<()> {
63+
pub fn handle_pay_lease_fee(context: Context<PayLeaseFee>) -> Result<()> {
6464
let now = Clock::get()?.unix_timestamp;
6565

66-
let rent_amount = compute_rent_due(&context.accounts.lease, now)?;
66+
let lease_fee_amount = compute_lease_fee_due(&context.accounts.lease, now)?;
6767

6868
// No time has passed (or already capped at end_ts). Nothing to do.
69-
if rent_amount == 0 {
69+
if lease_fee_amount == 0 {
7070
update_last_paid_ts(&mut context.accounts.lease, now);
7171
return Ok(());
7272
}
7373

74-
// Cap rent at whatever collateral actually sits in the vault. If the
74+
// Cap lease fees at whatever collateral actually sits in the vault. If the
7575
// lessee under-collateralised we cannot magically create funds; the
7676
// remainder is their debt and can trigger liquidation.
77-
let payable = rent_amount.min(context.accounts.collateral_amount_available());
77+
let payable = lease_fee_amount.min(context.accounts.collateral_amount_available());
7878

7979
if payable > 0 {
8080
let lease_key = context.accounts.lease.key();
@@ -108,27 +108,27 @@ pub fn handle_pay_rent(context: Context<PayRent>) -> Result<()> {
108108
Ok(())
109109
}
110110

111-
/// Rent accrues linearly: `(min(now, end_ts) - last_rent_paid_ts) * rate`.
111+
/// Lease fee accrues linearly: `(min(now, end_ts) - last_paid_ts) * rate`.
112112
/// Extracted so it can be re-used by `return_lease` and `liquidate` for a
113113
/// final settlement before closing the lease.
114-
pub fn compute_rent_due(lease: &Lease, now: i64) -> Result<u64> {
114+
pub fn compute_lease_fee_due(lease: &Lease, now: i64) -> Result<u64> {
115115
let cutoff = now.min(lease.end_ts);
116-
if cutoff <= lease.last_rent_paid_ts {
116+
if cutoff <= lease.last_paid_ts {
117117
return Ok(0);
118118
}
119-
let elapsed = (cutoff - lease.last_rent_paid_ts) as u64;
119+
let elapsed = (cutoff - lease.last_paid_ts) as u64;
120120
elapsed
121-
.checked_mul(lease.rent_per_second)
121+
.checked_mul(lease.lease_fee_per_second)
122122
.ok_or(AssetLeasingError::MathOverflow.into())
123123
}
124124

125-
/// Advance `last_rent_paid_ts` but never past the lease end — after end_ts
126-
/// the lease is settled and extra rent does not accrue.
125+
/// Advance `last_paid_ts` but never past the lease end — after end_ts
126+
/// the lease is settled and extra Lease fees do not accrue.
127127
pub fn update_last_paid_ts(lease: &mut Lease, now: i64) {
128-
lease.last_rent_paid_ts = now.min(lease.end_ts);
128+
lease.last_paid_ts = now.min(lease.end_ts);
129129
}
130130

131-
impl<'info> PayRent<'info> {
131+
impl<'info> PayLeaseFee<'info> {
132132
fn collateral_amount_available(&self) -> u64 {
133133
self.lease.collateral_amount
134134
}

defi/asset-leasing/anchor/programs/asset-leasing/src/instructions/return_lease.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
constants::{COLLATERAL_VAULT_SEED, LEASED_VAULT_SEED, LEASE_SEED},
99
errors::AssetLeasingError,
1010
instructions::{
11-
pay_rent::{compute_rent_due, update_last_paid_ts},
11+
pay_lease_fee::{compute_lease_fee_due, update_last_paid_ts},
1212
shared::{close_vault, transfer_tokens_from_user, transfer_tokens_from_vault},
1313
},
1414
state::{Lease, LeaseStatus},
@@ -19,7 +19,7 @@ pub struct ReturnLease<'info> {
1919
#[account(mut)]
2020
pub lessee: Signer<'info>,
2121

22-
/// CHECK: Reference only — receives rent + closed-vault rent refund.
22+
/// CHECK: Reference only — receives the lease fee + closed-vault rent-exempt-lamport refund.
2323
#[account(mut)]
2424
pub lessor: UncheckedAccount<'info>,
2525

@@ -40,7 +40,7 @@ pub struct ReturnLease<'info> {
4040
pub collateral_mint: Box<InterfaceAccount<'info, Mint>>,
4141

4242
/// Leased tokens flow back into this vault from the lessee, then out to
43-
/// the lessor in the same instruction. Closed at the end to reclaim rent.
43+
/// the lessor in the same instruction. Closed at the end to reclaim rent-exempt lamports.
4444
#[account(
4545
mut,
4646
seeds = [LEASED_VAULT_SEED, lease.key().as_ref()],
@@ -134,9 +134,9 @@ pub fn handle_return_lease(context: Context<ReturnLease>) -> Result<()> {
134134
&[leased_vault_seeds],
135135
)?;
136136

137-
// 3. Settle accrued rent: collateral vault -> lessor.
138-
let rent_due = compute_rent_due(&context.accounts.lease, now)?;
139-
let rent_payable = rent_due.min(context.accounts.lease.collateral_amount);
137+
// 3. Settle accrued lease fees: collateral vault -> lessor.
138+
let lease_fee_due = compute_lease_fee_due(&context.accounts.lease, now)?;
139+
let lease_fee_payable = lease_fee_due.min(context.accounts.lease.collateral_amount);
140140

141141
let collateral_vault_bump = context.accounts.lease.collateral_vault_bump;
142142
let collateral_vault_seeds: &[&[u8]] = &[
@@ -145,11 +145,11 @@ pub fn handle_return_lease(context: Context<ReturnLease>) -> Result<()> {
145145
core::slice::from_ref(&collateral_vault_bump),
146146
];
147147

148-
if rent_payable > 0 {
148+
if lease_fee_payable > 0 {
149149
transfer_tokens_from_vault(
150150
&context.accounts.collateral_vault,
151151
&context.accounts.lessor_collateral_account,
152-
rent_payable,
152+
lease_fee_payable,
153153
&context.accounts.collateral_mint,
154154
&context.accounts.collateral_vault.to_account_info(),
155155
&context.accounts.token_program,
@@ -158,20 +158,20 @@ pub fn handle_return_lease(context: Context<ReturnLease>) -> Result<()> {
158158
}
159159

160160
// 4. Refund remaining collateral to the lessee. Returning early does not
161-
// entitle the lessee to a future-rent refund — rent only accrues for time
162-
// actually used, so `compute_rent_due` already excludes the unused tail.
163-
let collateral_after_rent = context
161+
// entitle the lessee to a future-lease-fee refund — Lease fees only accrue for time
162+
// actually used, so `compute_lease_fee_due` already excludes the unused tail.
163+
let collateral_after_lease_fees = context
164164
.accounts
165165
.lease
166166
.collateral_amount
167-
.checked_sub(rent_payable)
167+
.checked_sub(lease_fee_payable)
168168
.ok_or(AssetLeasingError::MathOverflow)?;
169169

170-
if collateral_after_rent > 0 {
170+
if collateral_after_lease_fees > 0 {
171171
transfer_tokens_from_vault(
172172
&context.accounts.collateral_vault,
173173
&context.accounts.lessee_collateral_account,
174-
collateral_after_rent,
174+
collateral_after_lease_fees,
175175
&context.accounts.collateral_mint,
176176
&context.accounts.collateral_vault.to_account_info(),
177177
&context.accounts.token_program,

defi/asset-leasing/anchor/programs/asset-leasing/src/instructions/take_lease.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub fn handle_take_lease(context: Context<TakeLease>) -> Result<()> {
127127
lease.collateral_amount = required_collateral_amount;
128128
lease.start_ts = now;
129129
lease.end_ts = end_ts;
130-
lease.last_rent_paid_ts = now;
130+
lease.last_paid_ts = now;
131131
lease.status = LeaseStatus::Active;
132132

133133
Ok(())

defi/asset-leasing/anchor/programs/asset-leasing/src/lib.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub mod asset_leasing {
2323
lease_id: u64,
2424
leased_amount: u64,
2525
required_collateral_amount: u64,
26-
rent_per_second: u64,
26+
lease_fee_per_second: u64,
2727
duration_seconds: i64,
2828
maintenance_margin_bps: u16,
2929
liquidation_bounty_bps: u16,
@@ -34,7 +34,7 @@ pub mod asset_leasing {
3434
lease_id,
3535
leased_amount,
3636
required_collateral_amount,
37-
rent_per_second,
37+
lease_fee_per_second,
3838
duration_seconds,
3939
maintenance_margin_bps,
4040
liquidation_bounty_bps,
@@ -48,18 +48,18 @@ pub mod asset_leasing {
4848
instructions::take_lease::handle_take_lease(context)
4949
}
5050

51-
/// Stream rent from the collateral vault to the lessor, up to `end_ts`.
51+
/// Stream the lease fee from the collateral vault to the lessor, up to `end_ts`.
5252
/// Anyone may call this to keep the lease current.
53-
pub fn pay_rent(context: Context<PayRent>) -> Result<()> {
54-
instructions::pay_rent::handle_pay_rent(context)
53+
pub fn pay_lease_fee(context: Context<PayLeaseFee>) -> Result<()> {
54+
instructions::pay_lease_fee::handle_pay_lease_fee(context)
5555
}
5656

5757
/// Lessee adds more collateral to stay above the maintenance margin.
5858
pub fn top_up_collateral(context: Context<TopUpCollateral>, amount: u64) -> Result<()> {
5959
instructions::top_up_collateral::handle_top_up_collateral(context, amount)
6060
}
6161

62-
/// Lessee returns the leased tokens (at or before `end_ts`). Accrued rent
62+
/// Lessee returns the leased tokens (at or before `end_ts`). Accrued lease fees
6363
/// is settled and the remaining collateral is refunded.
6464
pub fn return_lease(context: Context<ReturnLease>) -> Result<()> {
6565
instructions::return_lease::handle_return_lease(context)

0 commit comments

Comments
 (0)