Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
# Stellar / Soroban Configuration
NEXT_PUBLIC_STELLAR_NETWORK=testnet
NEXT_PUBLIC_STELLAR_RPC_URL=https://soroban-testnet.stellar.org
NEXT_PUBLIC_CONTRACT_ID=CC... # Replace with your deployed contract ID

# Application Settings
NEXT_PUBLIC_SITE_URL=http://localhost:3000
6 changes: 3 additions & 3 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { StellarAuthProvider } from "@/contexts/StellarAuthContext";
import { StellarProvider } from "@/context/StellarContext";
import "./globals.css";

export const metadata: Metadata = {
Expand Down Expand Up @@ -32,9 +32,9 @@ export default function RootLayout({
<html lang="en">
<body>
<div className="mesh-gradient" aria-hidden="true" />
<StellarAuthProvider>
<StellarProvider>
<div className="relative z-10">{children}</div>
</StellarAuthProvider>
</StellarProvider>
</body>
</html>
);
Expand Down
29 changes: 0 additions & 29 deletions contexts/StellarAuthContext.test.ts

This file was deleted.

111 changes: 0 additions & 111 deletions contexts/StellarAuthContext.tsx

This file was deleted.

60 changes: 41 additions & 19 deletions contracts/rent-escrow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,22 @@ pub enum Error {
pub enum DataKey {
Escrow,
Deadline,
RentToken,
/// Maps a roommate Address to their expected rent share (i128).
Shares(Address),
/// Maps a roommate Address to their total contributed amount (i128).
Contributions(Address),
}

/// Tracks an individual roommate's rent obligation and payment progress.
///
/// Stored per-roommate in the escrow using `DataKey::Escrow` inside the
/// `RentEscrow.roommates` map, keyed by the roommate's `Address`.
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RoommateState {
/// The roommate's expected rent share in token units (i128).
pub expected: i128,
/// The cumulative amount the roommate has contributed so far (i128).
pub paid: i128,
}

Expand All @@ -54,6 +58,7 @@ pub struct RoommateState {
pub struct RentEscrow {
pub landlord: Address,
pub token: Address,
pub token_address: Address,
pub rent_amount: i128,
pub roommates: Map<Address, RoommateState>,
}
Expand All @@ -70,12 +75,24 @@ pub struct RentEscrowContract;
#[contractimpl]
impl RentEscrowContract {
/// Initialize the escrow with landlord, token, rent amount, deadline, and roommates.
pub fn initialize(
env: Env,
landlord: Address,
token: Address,
pub fn initialize(
env: Env,
landlord: Address,
token: Address,
/// Initialize the escrow with landlord, rent amount, and roommates.
///
/// Reverts if `landlord` is the contract itself.
///
/// Persists the escrow state to ledger storage so that the values
/// survive across invocations and ledger closes.
pub fn initialize(
env: Env,
landlord: Address,
token: Address,
token_address: Address,
rent_amount: i128,
deadline: u64,
roommates: Map<Address, i128>,
Expand All @@ -87,8 +104,7 @@ impl RentEscrowContract {

landlord.require_auth();

// AC: total_rent > MIN_RENT
if rent_amount <= MIN_RENT {
if rent_amount < MIN_RENT {
return Err(Error::InvalidAmount);
}

Expand All @@ -110,17 +126,16 @@ impl RentEscrowContract {

env.storage().persistent().set(&DataKey::Escrow, &RentEscrow {
landlord,
token: token.clone(),
token,
token_address,
rent_amount,
roommates: roommate_states,
});

env.storage().persistent().set(&DataKey::Deadline, &deadline);
env.storage().persistent().set(&DataKey::RentToken, &token);

env.storage().persistent().extend_ttl(&DataKey::Escrow, BUMP_THRESHOLD, BUMP_AMOUNT);
env.storage().persistent().extend_ttl(&DataKey::Deadline, BUMP_THRESHOLD, BUMP_AMOUNT);
env.storage().persistent().extend_ttl(&DataKey::RentToken, BUMP_THRESHOLD, BUMP_AMOUNT);

Ok(())
}
Expand Down Expand Up @@ -182,6 +197,9 @@ impl RentEscrowContract {
state.paid += amount;
escrow.roommates.set(from.clone(), state);

let token_client = token::Client::new(&env, &escrow.token_address);
token_client.transfer(&from, &env.current_contract_address(), &amount);

env.storage().persistent().set(&DataKey::Escrow, &escrow);
env.storage().persistent().extend_ttl(&DataKey::Escrow, BUMP_THRESHOLD, BUMP_AMOUNT);

Expand Down Expand Up @@ -245,23 +263,27 @@ impl RentEscrowContract {
.persistent()
.get(&DataKey::Escrow)
.expect("escrow not initialized");

let token_client = token::TokenClient::new(&env, &escrow.token);
let balance = token_client.balance(&env.current_contract_address());

if balance < escrow.rent_amount {
return Err(Error::InsufficientFunding);
}

token_client.transfer(&env.current_contract_address(), &escrow.landlord, &balance);

let escrow: RentEscrow = env.storage()
.persistent()
.get(&DataKey::Escrow)
.expect("escrow not initialized");

let total_funded = Self::get_total_funded(env.clone());
let token_client = token::Client::new(&env, &escrow.token_address);
token_client.transfer(&env.current_contract_address(), &escrow.landlord, &total_funded);

env.storage().persistent().extend_ttl(&DataKey::Escrow, BUMP_THRESHOLD, BUMP_AMOUNT);
env.storage().persistent().extend_ttl(&DataKey::Deadline, BUMP_THRESHOLD, BUMP_AMOUNT);

env.events().publish(
(symbol_short!("release"), escrow.landlord),
balance,
);
env.events().publish_event(&AgreementReleased {
amount: total_funded,
});

Ok(())
}
Expand All @@ -287,7 +309,7 @@ impl RentEscrowContract {
escrow.roommates.set(roommate.clone(), state);
env.storage().persistent().set(&DataKey::Escrow, &escrow);

let token_client = token::TokenClient::new(&env, &escrow.token);
let token_client = token::Client::new(&env, &escrow.token_address);
token_client.transfer(&env.current_contract_address(), &roommate, &refund_amount);

Ok(refund_amount)
Expand All @@ -303,12 +325,12 @@ impl RentEscrowContract {
}

/// Retrieve the token address.
pub fn get_token(env: Env) -> Address {
pub fn get_token_address(env: Env) -> Address {
let escrow: RentEscrow = env.storage()
.persistent()
.get(&DataKey::Escrow)
.expect("escrow not initialized");
escrow.token
escrow.token_address
}

/// Retrieve the rent amount.
Expand Down Expand Up @@ -382,7 +404,7 @@ impl RentEscrowContract {

env.storage().persistent().set(&DataKey::Escrow, &escrow);

let token_client = token::TokenClient::new(&env, &escrow.token);
let token_client = token::Client::new(&env, &escrow.token_address);
token_client.transfer(&env.current_contract_address(), &from, &refund_amount);

env.storage().persistent().extend_ttl(&DataKey::Escrow, BUMP_THRESHOLD, BUMP_AMOUNT);
Expand Down
27 changes: 2 additions & 25 deletions contracts/rent-escrow/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn test_initialize() {
roommate_shares.set(Address::generate(&env), 500);

env.mock_all_auths();

client.initialize(&landlord, &1000_i128, &TEST_DEADLINE, &roommate_shares);
client.initialize(
&landlord,
&token_address,
Expand All @@ -83,7 +83,7 @@ fn test_initialize() {
.expect("escrow should be stored after initialize");

assert_eq!(escrow.landlord, landlord);
assert_eq!(escrow.token, token_address);
assert_eq!(escrow.token_address, token_address);
});
}

Expand Down Expand Up @@ -353,26 +353,3 @@ fn test_contribute_emits_event() {
)
);
}

#[test]
fn test_initialize_invalid_rent_amount() {
let env = Env::default();
let contract_id = env.register(RentEscrowContract, ());
let client = RentEscrowContractClient::new(&env, &contract_id);
let landlord = Address::generate(&env);
let token = Address::generate(&env);
let mut shares = Map::new(&env);

env.mock_all_auths();

// Test with 0 rent
let result = client.try_initialize(&landlord, &token, &0, &TEST_DEADLINE, &shares);
assert_eq!(result.err(), Some(Ok(Error::InvalidAmount)));

// Test with MIN_RENT
let result = client.try_initialize(&landlord, &token, &MIN_RENT, &TEST_DEADLINE, &shares);
assert_eq!(result.err(), Some(Ok(Error::InvalidAmount)));

// Test with MIN_RENT + 1 (should succeed)
client.initialize(&landlord, &token, &(MIN_RENT + 1), &TEST_DEADLINE, &shares);
}
Loading
Loading