Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
fc99b47
feat(web-app): add d_token_rate
FabianSanchezD Mar 8, 2026
3203dbe
refactor(web-app): centralize assets config, remove dead code and com…
KevinLatino Mar 7, 2026
64ae7a7
feat(web-app): add mypositions to borrow
FabianSanchezD Mar 8, 2026
bdcc1e7
fix(web-app): connect wallet had typo
FabianSanchezD Mar 8, 2026
cc211e2
fix(web-app): remove tooltip, fix decimals on btoken
FabianSanchezD Mar 8, 2026
4e78e96
fix(web-app): positions in dashboard had unuseful ids
FabianSanchezD Mar 8, 2026
5c458f5
feat: add rwa-faucet contract and minting functionality to web app
Villarley Mar 8, 2026
930ad7d
feat(ui): adding new toast for notification
KevinLatino Mar 8, 2026
60be3f9
feat(ui): adding new toast for notification
KevinLatino Mar 8, 2026
22ba137
feat(contracts/lending): add pool1ContractId and pool2ContractId to n…
aguilar1x Mar 8, 2026
feafe78
feat(lending): add dual-pool support to lending pools and helpers
aguilar1x Mar 8, 2026
7dcc977
feat(borrowing): add dual-pool support across borrowing feature
aguilar1x Mar 8, 2026
3ecf266
feat(orchestrator): update NekoLendingAdapter for dual-pool support
aguilar1x Mar 8, 2026
e4ae248
feat(borrowing): add real-time health factor UI for both lending pools
aguilar1x Mar 8, 2026
9261271
fix conflicts
FabianSanchezD Mar 8, 2026
e568553
feat: enhance wallet functionality with Freighter integration
Villarley Mar 8, 2026
ffae66f
fix build
FabianSanchezD Mar 8, 2026
51e5616
Merge pull request #162 from Neko-Protocol/fix/mostly-pool-fixes
KevinLatino Mar 8, 2026
7ed7cf1
refactor: improve modal components and error handling
Villarley Mar 8, 2026
4315296
Merge branch 'dev' into feat/mint-authority
Villarley Mar 8, 2026
d6a5e4a
Merge branch 'dev' into feat/healt-factor-ui
aguilar1x Mar 8, 2026
9dbde30
Merge pull request #163 from Neko-Protocol/feat/healt-factor-ui
KevinLatino Mar 8, 2026
3e73abb
chore: update generated contract-errors file timestamp and fix type d…
Villarley Mar 8, 2026
50a5c73
Merge branch 'feat/mint-authority' of https://github.com/Neko-Protoco…
Villarley Mar 8, 2026
1bc4adf
refactor: replace useNotification with useToast in wallet components
Villarley Mar 8, 2026
b3c539b
fix(web-app): d and b tokens with wrong decimals
FabianSanchezD Mar 8, 2026
a5f1f77
feat: user position dashboard
FabianSanchezD Mar 8, 2026
029d1b8
fix: remove perps errors
FabianSanchezD Mar 8, 2026
6326fb6
fix: remove animate pulse from user position card
FabianSanchezD Mar 8, 2026
a5746d1
fix: remove /pools route
FabianSanchezD Mar 8, 2026
6c4eeda
feat(ui): adding modals for test tokens
KevinLatino Mar 8, 2026
3abc861
Merge branch 'dev' into feat/mint-authority
KevinLatino Mar 8, 2026
28ec5e5
feat: full pools revamp
FabianSanchezD Mar 8, 2026
706c03e
Merge pull request #164 from Neko-Protocol/feat/mint-authority
KevinLatino Mar 8, 2026
a900a43
fix: remove position overview from lend and borrow
FabianSanchezD Mar 8, 2026
96831eb
Merge branch 'dev' into fix/pools
FabianSanchezD Mar 8, 2026
9b62be2
Merge pull request #165 from Neko-Protocol/fix/pools
FabianSanchezD Mar 8, 2026
5a90185
feat: borrow position heat factor
aguilar1x Mar 8, 2026
8a9d7ed
Merge branch 'dev' into feat/borrow-position
aguilar1x Mar 8, 2026
fa8cb48
feat: borrow position heat factor
aguilar1x Mar 8, 2026
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
12 changes: 12 additions & 0 deletions .cursor/debug-3cec5e.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{"sessionId":"3cec5e","location":"route.ts:POST","message":"Admin keypair info","data":{"adminPublicKey":"GDEQD7CITHS4AINJTA4VSACHOXK6ZOY6WTFUNLRHXTCLZXZ5TI4Y7Y5X","requestedAddress":"GA6Q2HIOUFZHNQ4RNL4INHZ7QPJ7RSB5YOIBRZEYHUJNKNL5FBE4M3C6"},"timestamp":1772938297455,"hypothesisId":"H1"}
{"sessionId":"3cec5e","location":"route.ts:path-check","message":"Which mint path","data":{"faucetContractId":"NOT_SET","usingBulk":false},"timestamp":1772938297456,"hypothesisId":"H3"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-start","message":"Minting token (legacy)","data":{"symbol":"USTRY","contractId":"CC6SODKGOTFEDWVNPR6ESJC3GL7NC5Y4DVFKYGATZJ74F2YXHTW4RJ6D","mintAmount":"1000000000","adminPubKey":"GDEQD7CITHS4AINJTA4VSACHOXK6ZOY6WTFUNLRHXTCLZXZ5TI4Y7Y5X"},"timestamp":1772938297456,"hypothesisId":"H2"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-ok","message":"Token minted OK","data":{"symbol":"USTRY","hash":"f8124f7df212c8080e3a0d42a45cd1a3fd9345ce9f86b346634d0aba224a6885"},"timestamp":1772938300940,"hypothesisId":"H2"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-start","message":"Minting token (legacy)","data":{"symbol":"TESOURO","contractId":"CA55OO3U556GXABJKDYP3QCGZ6AFNZPB27TROYP42AQPFFYPKU5EDOUH","mintAmount":"1000000000","adminPubKey":"GDEQD7CITHS4AINJTA4VSACHOXK6ZOY6WTFUNLRHXTCLZXZ5TI4Y7Y5X"},"timestamp":1772938300943,"hypothesisId":"H2"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-ok","message":"Token minted OK","data":{"symbol":"TESOURO","hash":"c9ad99e25fe023d1a143add23780edc3e94a9a8337947156d2726bea6f1e23b3"},"timestamp":1772938306315,"hypothesisId":"H2"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-start","message":"Minting token (legacy)","data":{"symbol":"CETES","contractId":"CCGWKS4GLAGPYIAOLBH6JM5RKUPMUCN47VRAEXAJWJFKXSXQ33VIRUAA","mintAmount":"1000000000","adminPubKey":"GDEQD7CITHS4AINJTA4VSACHOXK6ZOY6WTFUNLRHXTCLZXZ5TI4Y7Y5X"},"timestamp":1772938306317,"hypothesisId":"H2"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-ok","message":"Token minted OK","data":{"symbol":"CETES","hash":"6c916e63c42a96acb3a2fef48b8f801d67197e9cc661dc6d50083fef871f78fe"},"timestamp":1772938312060,"hypothesisId":"H2"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-start","message":"Minting token (legacy)","data":{"symbol":"USDY","contractId":"CBVLFSVBZGHVAH6CV4JQYPBJSX75VFR2NJC7CQX7QKQ7KOLGQZOZAGQK","mintAmount":"1000000000","adminPubKey":"GDEQD7CITHS4AINJTA4VSACHOXK6ZOY6WTFUNLRHXTCLZXZ5TI4Y7Y5X"},"timestamp":1772938312063,"hypothesisId":"H2"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-ok","message":"Token minted OK","data":{"symbol":"USDY","hash":"954b97f783d3f187f9f8f6c823559a16ba317e2883b16471b29923ee852793d5"},"timestamp":1772938316365,"hypothesisId":"H2"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-start","message":"Minting token (legacy)","data":{"symbol":"PYUSD","contractId":"CBCB5UDYZENTIUVVA7SHQOCVVCDXDMHEKJHHFM3OQKU2E5CAF2B62TIO","mintAmount":"1000000000","adminPubKey":"GDEQD7CITHS4AINJTA4VSACHOXK6ZOY6WTFUNLRHXTCLZXZ5TI4Y7Y5X"},"timestamp":1772938316367,"hypothesisId":"H2"}
{"sessionId":"3cec5e","location":"route.ts:legacy-mint-ok","message":"Token minted OK","data":{"symbol":"PYUSD","hash":"2e05db887e2ff78b43d0b7c40ef3d79e51cedaeb3a3bd9df79664779b79fbd31"},"timestamp":1772938321012,"hypothesisId":"H2"}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,6 @@ yarn-error.log*
# Rust/Stellar contracts
**/test_snapshots/
target/

deploy-full.cjs
deploy-pools.cjs
7 changes: 7 additions & 0 deletions apps/contracts/stellar-contracts/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion apps/contracts/stellar-contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ members = [
"rwa-lending",
"rwa-oracle",
"rwa-token",
"rwa-perps"
"rwa-perps",
"rwa-faucet"
]

[workspace.package]
Expand Down
23 changes: 23 additions & 0 deletions apps/contracts/stellar-contracts/rwa-faucet/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "rwa-faucet"
description = "RWA Faucet contract - Bulk mints multiple RWA tokens in a single Soroban invocation"
version.workspace = true
authors.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
publish = false

[lib]
crate-type = ["cdylib", "rlib"]
doctest = false

[dependencies]
soroban-sdk = { workspace = true }

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }

[package.metadata.stellar]
contract = true
cargo_inherit = true
42 changes: 42 additions & 0 deletions apps/contracts/stellar-contracts/rwa-faucet/src/contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use soroban_sdk::{contract, contractimpl, vec, Address, Env, IntoVal, Symbol, Vec};

use crate::storage::Storage;
use crate::types::MintRequest;

#[contract]
pub struct Faucet;

#[contractimpl]
impl Faucet {
/// Initialize the faucet with an admin address.
/// The admin must be the same account that controls the rwa-token contracts.
pub fn initialize(env: Env, admin: Address) {
assert!(!Storage::is_initialized(&env), "Faucet: already initialized");
admin.require_auth();
Storage::set_admin(&env, &admin);
Storage::set_initialized(&env);
}

/// Mint multiple tokens in a single invocation (permissionless on testnet).
/// The faucet contract must be the admin of each token contract so that
/// cross-contract calls to set_authorized and mint are auto-authorized.
pub fn bulk_mint(env: Env, requests: Vec<MintRequest>) {
for req in requests.iter() {
env.invoke_contract::<()>(
&req.token,
&Symbol::new(&env, "set_authorized"),
vec![&env, req.to.into_val(&env), true.into_val(&env)],
);
env.invoke_contract::<()>(
&req.token,
&Symbol::new(&env, "mint"),
vec![&env, req.to.into_val(&env), req.amount.into_val(&env)],
);
}
}

/// Return the admin address.
pub fn admin(env: Env) -> Address {
Storage::get_admin(&env)
}
}
8 changes: 8 additions & 0 deletions apps/contracts/stellar-contracts/rwa-faucet/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![no_std]

pub mod contract;
pub mod storage;
pub mod types;

#[cfg(test)]
mod test;
33 changes: 33 additions & 0 deletions apps/contracts/stellar-contracts/rwa-faucet/src/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use soroban_sdk::{contracttype, Address, Env};

#[contracttype]
enum DataKey {
Admin,
Initialized,
}

pub struct Storage;

impl Storage {
pub fn set_admin(env: &Env, admin: &Address) {
env.storage().instance().set(&DataKey::Admin, admin);
}

pub fn get_admin(env: &Env) -> Address {
env.storage()
.instance()
.get(&DataKey::Admin)
.expect("Faucet: admin not set")
}

pub fn set_initialized(env: &Env) {
env.storage().instance().set(&DataKey::Initialized, &true);
}

pub fn is_initialized(env: &Env) -> bool {
env.storage()
.instance()
.get(&DataKey::Initialized)
.unwrap_or(false)
}
}
151 changes: 151 additions & 0 deletions apps/contracts/stellar-contracts/rwa-faucet/src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#![cfg(test)]
extern crate std;

use crate::contract::{Faucet, FaucetClient};
use crate::types::MintRequest;
use soroban_sdk::{
testutils::Address as _,
token::TokenClient,
Address, Env, Vec,
};

/// Use Stellar Asset Contract as the mintable token for testing.
/// The issuer acts as the admin who can mint.
fn create_test_token(env: &Env, admin: &Address) -> Address {
let contract = env.register_stellar_asset_contract_v2(admin.clone());
contract.address()
}

fn create_faucet<'a>(env: &Env) -> (FaucetClient<'a>, Address) {
let address = env.register(Faucet, ());
let client = FaucetClient::new(env, &address);
(client, address)
}

#[test]
fn test_initialize() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let (faucet, _) = create_faucet(&env);

faucet.initialize(&admin);
assert_eq!(faucet.admin(), admin);
}

#[test]
#[should_panic(expected = "Faucet: already initialized")]
fn test_initialize_twice_panics() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let (faucet, _) = create_faucet(&env);

faucet.initialize(&admin);
faucet.initialize(&admin);
}

#[test]
fn test_bulk_mint_single_token() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let user = Address::generate(&env);

let token_addr = create_test_token(&env, &admin);
let token_client = TokenClient::new(&env, &token_addr);

let (faucet, _) = create_faucet(&env);
faucet.initialize(&admin);

let requests = Vec::from_array(
&env,
[MintRequest {
token: token_addr.clone(),
to: user.clone(),
amount: 1_000_0000000, // 1000 with 7 decimals
}],
);

faucet.bulk_mint(&requests);

assert_eq!(token_client.balance(&user), 1_000_0000000);
}

#[test]
fn test_bulk_mint_multiple_tokens() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let user = Address::generate(&env);

let token_a = create_test_token(&env, &admin);
let token_b = create_test_token(&env, &admin);
let client_a = TokenClient::new(&env, &token_a);
let client_b = TokenClient::new(&env, &token_b);

let (faucet, _) = create_faucet(&env);
faucet.initialize(&admin);

let requests = Vec::from_array(
&env,
[
MintRequest {
token: token_a.clone(),
to: user.clone(),
amount: 500_0000000,
},
MintRequest {
token: token_b.clone(),
to: user.clone(),
amount: 100_0000000,
},
],
);

faucet.bulk_mint(&requests);

assert_eq!(client_a.balance(&user), 500_0000000);
assert_eq!(client_b.balance(&user), 100_0000000);
}

#[test]
fn test_bulk_mint_multiple_recipients() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let user_a = Address::generate(&env);
let user_b = Address::generate(&env);

let token_addr = create_test_token(&env, &admin);
let token_client = TokenClient::new(&env, &token_addr);

let (faucet, _) = create_faucet(&env);
faucet.initialize(&admin);

let requests = Vec::from_array(
&env,
[
MintRequest {
token: token_addr.clone(),
to: user_a.clone(),
amount: 200_0000000,
},
MintRequest {
token: token_addr.clone(),
to: user_b.clone(),
amount: 300_0000000,
},
],
);

faucet.bulk_mint(&requests);

assert_eq!(token_client.balance(&user_a), 200_0000000);
assert_eq!(token_client.balance(&user_b), 300_0000000);
}
9 changes: 9 additions & 0 deletions apps/contracts/stellar-contracts/rwa-faucet/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use soroban_sdk::{contracttype, Address};

#[contracttype]
#[derive(Clone, Debug)]
pub struct MintRequest {
pub token: Address,
pub to: Address,
pub amount: i128,
}
2 changes: 1 addition & 1 deletion apps/contracts/stellar-contracts/rwa-token/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ doctest = false

[dependencies]
soroban-sdk = { workspace = true }
rwa-oracle = { path = "../rwa-oracle" }

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }
rwa-oracle = { path = "../rwa-oracle" }

[package.metadata.stellar]
contract = true
Expand Down
6 changes: 6 additions & 0 deletions apps/contracts/stellar-contracts/rwa-token/src/admin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,10 @@ impl Admin {
pub fn authorized(env: &Env, id: &Address) -> bool {
AuthorizationStorage::get(env, id)
}

/// Transfer admin role to a new address. Current admin must authorize.
pub fn set_admin(env: &Env, new_admin: &Address) {
Self::require_admin(env);
MetadataStorage::update_admin(env, new_admin);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ impl MetadataStorage {
env.storage().instance().set(&ADMIN_KEY, admin);
}

pub fn update_admin(env: &Env, new_admin: &Address) {
env.storage().instance().set(&ADMIN_KEY, new_admin);
}

pub fn get_token(env: &Env) -> TokenStorage {
env.storage()
.instance()
Expand Down
5 changes: 5 additions & 0 deletions apps/contracts/stellar-contracts/rwa-token/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ impl RWATokenContract {
Admin::get_admin(&env)
}

/// Transfer admin role to a new address. Current admin must authorize.
pub fn set_admin(env: Env, new_admin: Address) {
Admin::set_admin(&env, &new_admin);
}

/// Mint tokens to an address. Admin-only.
pub fn mint(env: Env, to: Address, amount: i128) {
Admin::mint(&env, &to, amount);
Expand Down
2 changes: 1 addition & 1 deletion apps/contracts/stellar-contracts/rwa-token/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub use common::error::Error;

// Import RWA Oracle WASM for reading RWA asset prices
pub mod rwa_oracle {
soroban_sdk::contractimport!(file = "../target/wasm32-unknown-unknown/release/rwa_oracle.wasm");
soroban_sdk::contractimport!(file = "../target/wasm32v1-none/release/rwa_oracle.wasm");
}

pub mod contract;
Expand Down
5 changes: 5 additions & 0 deletions apps/web-app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ NEXT_PUBLIC_STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org

# SoroSwap ApiKey
NEXT_PUBLIC_SOROSWAP_API_KEY=

# Faucet contract (client-side, permissionless on testnet)
# The faucet contract is the admin of all RWA tokens and handles set_authorized + mint.
# Users call bulk_mint directly via Freighter (no server-side key needed).
NEXT_PUBLIC_FAUCET_CONTRACT_ID=
Loading
Loading