Skip to content
Open
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
5 changes: 5 additions & 0 deletions stellar-lend/contracts/lending/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ mod deposit;
mod events;
mod flash_loan;
mod pause;
mod token_adapter;
mod token_adapter_erc20;
mod token_adapter_native;
mod token_adapter_wrapped;
mod token_adapter_verify;
mod token_receiver;
mod withdraw;
mod reentrancy;
Expand Down
16 changes: 11 additions & 5 deletions stellar-lend/contracts/lending/src/token_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ pub struct AdapterVerificationResult {
pub verification_data: Vec<u8>,
}

#[contracttype]
#[derive(Clone)]
pub enum AdapterDataKey {
Adapter(Address),
AdapterList,
}

/// TokenAdapterTrait - Core trait for all token adapters
///
/// This trait defines the standard interface that all token adapters
Expand All @@ -93,8 +100,8 @@ pub trait TokenAdapterTrait {
/// Get the total supply of the token
fn total_supply(&self, env: &Env) -> Result<i128, AdapterError>;

/// Approve spender to transfer tokens
fn approve(&self, env: &Env, spender: &Address, amount: i128) -> Result<(), AdapterError>;
/// Approve spender to transfer tokens from the owner
fn approve(&self, env: &Env, owner: &Address, spender: &Address, amount: i128) -> Result<(), AdapterError>;

/// Get approved allowance
fn allowance(&self, env: &Env, owner: &Address, spender: &Address) -> Result<i128, AdapterError>;
Expand Down Expand Up @@ -124,7 +131,7 @@ pub mod factory {
}

/// Detect the type of token at the given address
fn detect_token_type(env: &Env, token_address: &Address) -> Result<TokenAdapterType, AdapterError> {
fn detect_token_type(_env: &Env, _token_address: &Address) -> Result<TokenAdapterType, AdapterError> {
// Token type detection logic
// In practice, this would query the token contract to determine its type
// For now, we default to ERC20 as the most common type
Expand All @@ -139,7 +146,6 @@ pub mod factory {
if !config.enabled {
return Err(AdapterError::InvalidConfig);
}
// Store adapter configuration
Ok(())
crate::token_adapter_verify::register_adapter(env, config)
}
}
79 changes: 27 additions & 52 deletions stellar-lend/contracts/lending/src/token_adapter_erc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! enabling the lending protocol to interact with any ERC-20 compliant token.

use crate::token_adapter::{AdapterConfig, AdapterError, TokenAdapterType};
use soroban_sdk::{Address, Env, Vec};
use soroban_sdk::{Address, Env, Vec, token::Client as TokenClient};

/// ERC-20 adapter for standard token interactions
pub struct ERC20Adapter {
Expand Down Expand Up @@ -43,29 +43,20 @@ impl ERC20Adapter {
/// These methods interact with standard ERC-20 token contracts
pub mod erc20 {
use super::*;
use soroban_sdk::{Symbol, TryFromVal};

const TRANSFER_FN: Symbol = Symbol::new(&Env::new("transfer"));
const BALANCE_OF_FN: Symbol = Symbol::new(&Env::new("balance_of"));
const TOTAL_SUPPLY_FN: Symbol = Symbol::new(&Env::new("total_supply"));
const APPROVE_FN: Symbol = Symbol::new(&Env::new("approve"));
const ALLOWANCE_FN: Symbol = Symbol::new(&Env::new("allowance"));
const TRANSFER_FROM_FN: Symbol = Symbol::new(&Env::new("transfer_from"));

/// Transfer tokens from the current contract to another address
pub fn transfer(
env: &Env,
token: &Address,
from: &Address,
to: &Address,
amount: i128,
) -> Result<(), AdapterError> {
// Call the token's transfer function
// In Soroban, this would use the token interface
env.invoke_contract(
token,
&TRANSFER_FN,
(to, amount).into_val(env),
);
if amount <= 0 {
return Err(AdapterError::InvalidConfig);
}
let token_client = TokenClient::new(env, token);
token_client.transfer(from, to, &amount);
Ok(())
}

Expand All @@ -75,42 +66,32 @@ pub mod erc20 {
token: &Address,
address: &Address,
) -> Result<i128, AdapterError> {
// Call the token's balance_of function
let result: Result<i128, _> = env.invoke_contract(
token,
&BALANCE_OF_FN,
address.into_val(env),
);
result.map_err(|_| AdapterError::AdapterFailed)
let token_client = TokenClient::new(env, token);
Ok(token_client.balance(address))
}

/// Get the total supply of the token
pub fn total_supply(
env: &Env,
token: &Address,
) -> Result<i128, AdapterError> {
// Call the token's total_supply function
let result: Result<i128, _> = env.invoke_contract(
token,
&TOTAL_SUPPLY_FN,
().into_val(env),
);
result.map_err(|_| AdapterError::AdapterFailed)
let token_client = TokenClient::new(env, token);
Ok(token_client.total_supply())
}

/// Approve a spender to use a certain amount of tokens
pub fn approve(
env: &Env,
token: &Address,
owner: &Address,
spender: &Address,
amount: i128,
) -> Result<(), AdapterError> {
// Call the token's approve function
env.invoke_contract(
token,
&APPROVE_FN,
(spender, amount).into_val(env),
);
if amount < 0 {
return Err(AdapterError::InvalidConfig);
}
let token_client = TokenClient::new(env, token);
token_client.approve(owner, spender, &amount, &(env.ledger().sequence() + 100));
Ok(())
}

Expand All @@ -121,13 +102,8 @@ pub mod erc20 {
owner: &Address,
spender: &Address,
) -> Result<i128, AdapterError> {
// Call the token's allowance function
let result: Result<i128, _> = env.invoke_contract(
token,
&ALLOWANCE_FN,
(owner, spender).into_val(env),
);
result.map_err(|_| AdapterError::AdapterFailed)
let token_client = TokenClient::new(env, token);
Ok(token_client.allowance(owner, spender))
}

/// Transfer tokens using allowance
Expand All @@ -138,12 +114,11 @@ pub mod erc20 {
to: &Address,
amount: i128,
) -> Result<(), AdapterError> {
// Call the token's transfer_from function
env.invoke_contract(
token,
&TRANSFER_FROM_FN,
(from, to, amount).into_val(env),
);
if amount <= 0 {
return Err(AdapterError::InvalidConfig);
}
let token_client = TokenClient::new(env, token);
token_client.transfer_from(from, to, &amount);
Ok(())
}
}
Expand All @@ -162,7 +137,7 @@ impl super::TokenAdapterTrait for ERC20Adapter {
}

fn transfer(&self, env: &Env, from: &Address, to: &Address, amount: i128) -> Result<(), AdapterError> {
erc20::transfer(env, &self.config.token_address, to, amount)
erc20::transfer(env, &self.config.token_address, from, to, amount)
}

fn balance_of(&self, env: &Env, address: &Address) -> Result<i128, AdapterError> {
Expand All @@ -173,8 +148,8 @@ impl super::TokenAdapterTrait for ERC20Adapter {
erc20::total_supply(env, &self.config.token_address)
}

fn approve(&self, env: &Env, spender: &Address, amount: i128) -> Result<(), AdapterError> {
erc20::approve(env, &self.config.token_address, spender, amount)
fn approve(&self, env: &Env, owner: &Address, spender: &Address, amount: i128) -> Result<(), AdapterError> {
erc20::approve(env, &self.config.token_address, owner, spender, amount)
}

fn allowance(&self, env: &Env, owner: &Address, spender: &Address) -> Result<i128, AdapterError> {
Expand Down
57 changes: 22 additions & 35 deletions stellar-lend/contracts/lending/src/token_adapter_native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! Native tokens require different handling than contract-based tokens.

use crate::token_adapter::{AdapterConfig, AdapterError, TokenAdapterType};
use soroban_sdk::{Address, Env, Vec};
use soroban_sdk::{Address, Env, Vec, token::StellarAssetClient};

/// Native token adapter for handling native blockchain assets
pub struct NativeAdapter {
Expand Down Expand Up @@ -48,66 +48,53 @@ pub mod native {
use super::*;

/// Transfer native tokens (XLM)
///
/// For native tokens, transfer is handled by the blockchain itself,
/// not by a token contract.
pub fn transfer(
env: &Env,
token: &Address,
from: &Address,
to: &Address,
amount: i128,
) -> Result<(), AdapterError> {
if amount <= 0 {
return Err(AdapterError::TokenNotSupported);
return Err(AdapterError::InvalidConfig);
}

// Native token transfer - uses blockchain's native mechanism
// In Soroban, this would involve the token::transfer call
// For XLM, we use the native token interface
let client = StellarAssetClient::new(env, token);
client.transfer(from, to, &amount);
Ok(())
}

/// Get the native balance of an address
///
/// Native token balances are obtained from the blockchain state,
/// not from a contract.
pub fn balance_of(
env: &Env,
token: &Address,
address: &Address,
) -> Result<i128, AdapterError> {
// Get native balance from blockchain
// This would use Soroban's native token interface
Ok(0) // Placeholder - actual implementation would query chain state
let client = StellarAssetClient::new(env, token);
Ok(client.balance(address))
}

/// Get the total native token supply
///
/// The total supply of native tokens is determined by the blockchain.
pub fn total_supply(
env: &Env,
_env: &Env,
) -> Result<i128, AdapterError> {
// Total supply is determined by the blockchain
// For Stellar, this would be the XLM in circulation
Ok(0) // Placeholder
Err(AdapterError::NotImplemented)
}

/// Mint native tokens (requires special permissions)
pub fn mint(
env: &Env,
to: &Address,
amount: i128,
_env: &Env,
_to: &Address,
_amount: i128,
) -> Result<(), AdapterError> {
// Minting native tokens requires special permissions
// This would typically be restricted to the contract admin
Err(AdapterError::NotImplemented)
}

/// Burn native tokens
pub fn burn(
env: &Env,
from: &Address,
amount: i128,
_env: &Env,
_from: &Address,
_amount: i128,
) -> Result<(), AdapterError> {
// Burning native tokens requires special permissions
Err(AdapterError::NotImplemented)
}
}
Expand All @@ -125,19 +112,19 @@ impl super::TokenAdapterTrait for NativeAdapter {
self.config.enabled
}

fn transfer(&self, env: &Env, _from: &Address, to: &Address, amount: i128) -> Result<(), AdapterError> {
native::transfer(env, to, amount)
fn transfer(&self, env: &Env, from: &Address, to: &Address, amount: i128) -> Result<(), AdapterError> {
native::transfer(env, &self.config.token_address, from, to, amount)
}

fn balance_of(&self, env: &Env, address: &Address) -> Result<i128, AdapterError> {
native::balance_of(env, address)
native::balance_of(env, &self.config.token_address, address)
}

fn total_supply(&self, env: &Env) -> Result<i128, AdapterError> {
native::total_supply(env)
}

fn approve(&self, _env: &Env, _spender: &Address, _amount: i128) -> Result<(), AdapterError> {
fn approve(&self, _env: &Env, _owner: &Address, _spender: &Address, _amount: i128) -> Result<(), AdapterError> {
// Native tokens don't support approval in the same way
Err(AdapterError::NotImplemented)
}
Expand All @@ -160,5 +147,5 @@ pub fn verify_native_token(
) -> Result<bool, AdapterError> {
// Native tokens are identified by special addresses or flags
// In Stellar, native XLM is handled differently from token contracts
Ok(false) // Placeholder - actual implementation would check
Ok(true) // Native adapter is considered valid when enabled
}
33 changes: 26 additions & 7 deletions stellar-lend/contracts/lending/src/token_adapter_verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,23 @@ pub fn register_adapter(
return Err(AdapterError::VerificationFailed);
}

// Store the adapter configuration
// In practice, this would use persistent storage
env.storage()
.persistent()
.set(&crate::token_adapter::AdapterDataKey::Adapter(config.token_address.clone()), &config);

let mut adapters: Vec<AdapterConfig> = env
.storage()
.persistent()
.get(&crate::token_adapter::AdapterDataKey::AdapterList)
.unwrap_or_else(|| Vec::new(env));

if !adapters.iter().any(|existing| existing.token_address == config.token_address) {
adapters.push_back(config.clone());
env.storage()
.persistent()
.set(&crate::token_adapter::AdapterDataKey::AdapterList, &adapters);
}

Ok(())
}

Expand All @@ -130,17 +145,21 @@ pub fn get_adapter(
env: &Env,
token_address: &Address,
) -> Result<Option<AdapterConfig>, AdapterError> {
// Retrieve adapter configuration from storage
// In practice, this would read from persistent storage
Ok(None)
Ok(env
.storage()
.persistent()
.get(&crate::token_adapter::AdapterDataKey::Adapter(token_address.clone())))
}

/// List all registered adapters
pub fn list_adapters(
env: &Env,
) -> Result<Vec<AdapterConfig>, AdapterError> {
// Return all registered adapters
Ok(Vec::new(env))
Ok(env
.storage()
.persistent()
.get(&crate::token_adapter::AdapterDataKey::AdapterList)
.unwrap_or_else(|| Vec::new(env)))
}

/// Upgrade path for new tokens
Expand Down
Loading